Overview
This guide aims to provide an overview of using Terraform to create a complete Citrix DaaS Resource Location on Google Cloud Platform (GCP).
At the end of the process, you created:
- A new Citrix Cloud Resource Location (RL) running on Google Cloud Platform (GCP)
- 2 Cloud Connector Virtual Machines registered with the Domain and the Resource Location
- A Hypervisor Connection and a Hypervisor Pool pointing to the new Resource Location in Google Cloud Platform (GCP)
- A Machine Catalog based on the uploaded Master Image VHD or on a Google Cloud Platform (GCP)-based Master Image
- A Delivery Group based on the Machine Catalog with full Autoscale Support
- Example policies and policy scopes bound to the Delivery Group
Note:
This guide aims at using Google Cloud Platform (GCP) as a Resource Location for Citrix DaaS.
Of course Google Cloud Platform (GCP) and other supported HyperScalers can also be used for on-premises Citrix Virtual Apps And Desktops (CVAD) customers.
Guides for Virtual Apps And Desktops (CVAD) customers using HyperScalers together with Terraform will be provided soon!
What is Terraform
Terraform is an Infrastructure-as-Code (IaC) tool that defines cloud and on-prem resources in easy-readable configuration files rather than through a GUI.
IaC allows you to build, change, and manage your infrastructure safely and consistently by defining resource configurations.
These configurations can be versioned, reused, and shared and are created in its native declarative configuration language known as HashiCorp Configuration Language (HCL), or optionally using JSON.
Terraform creates and manages resources on Cloud platforms and other services through their application programming interfaces (APIs). Terraform providers are compatible with virtually any platform or service with an accessible API.
More information about Terraform can be found at https://developer.hashicorp.com/terraform/intro.
Installation
HashiCorp distributes Terraform as a binary package. You can also install Terraform using popular package managers. In this example, we use Chocolatey for Windows to deploy Terraform. Chocolatey is a free and open-source package management system for Windows. Install the Terraform package from the CLI.
Installation of Chocolatey
Open a PowerShell shell with Administrative rights and paste the following command:
Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
Chocolatey downloads and installs all necessary components automatically:
|
Run choco --v
to check if Chocolatey was installed successfully:
|
Installation of Terraform
After the successful installation of Chocolatey, you can install Terraform by running this command on the PowerShell session:
choco install terraform
|
Run
terraform -version
to check if Terraform was installed successfully:
|
The installation of Terraform is now completed.
Terraform - Basics and Commands
Terraform Block
The terraform {}
block contains Terraform settings, including the required providers to provision your infrastructure. Terraform installs providers from the Terraform Registry.
Providers
The provider block configures the specified provider. A provider is a plug-in that Terraform uses to create and manage your resources. Providing multiple provider blocks in the Terraform configuration enables managing resources from different providers.
Resources
Resource blocks define the components of the infrastructure - physical, virtual, or logical. These blocks contain arguments to configure the resource. The provider's reference lists the required and optional arguments for each resource.
The core Terraform workflow consists of three stages
Write
:
You define resources that are deployed, altered, or deleted.
Plan
:
Terraform creates an execution plan describing the infrastructure it creates, updates, or destroys based on the existing infrastructure and your configuration.
Apply
:
On approval, Terraform does the proposed operations in the correct order, respecting any resource dependencies.
Terraform does not only add complete configurations, it also allows you to change previously added configurations.
For example, changing the DNS servers of a NIC of a Google Cloud Platform (GCP) VM does not require redeploying the whole configuration - Terraform only alters the needed resources.
Terraform Provider for Citrix
Citrix has developed a custom Terraform provider for automating Citrix product deployments and configurations.
Note:
The Citrix Terraform provider is currently in Tech Preview!
This guide is updated when the provider has reached RTM.
You can use Terraform with Citrix's provider to manage your Citrix products via Infrastructure as Code. Terraform provides higher efficiency and consistency in infrastructure management and better reusability in infrastructure configuration.
-
The provider defines individual units of infrastructure and currently supports both
Citrix Virtual Apps and Desktops
andCitrix DaaS
solutions. - You can automate the creation of a site setup including host connections, machine catalogs, and delivery groups.
- You can deploy resources in Google Cloud Platform (GCP), AWS, and Azure, as well as supported on-premises Hypervisors.
Note:
All information about the Citrix Terraform provider can be found on GitHub: https://github.com/citrix/terraform-provider-citrix
Terraform expects to be invoked from a working directory that contains configuration files written in the Terraform language.
Terraform uses configuration content from this directory and also uses the directory to store settings, cached plug-ins and modules, and state data.
A working directory must be initialized before Terraform can do any operations.
Initialize the working directory by using the command:
terraform init
|
The provider defines how Terraform can interact with the underlying API. Configurations must declare which providers they require so Terraform can install and use them.
Terraform CLI finds and installs providers when initializing a working directory. It can automatically download providers from a Terraform registry or load them from a local mirror or cache.
Example: Terraform configuration files - provider.tf
The file provider.tf
contains the information on the target site where to apply the configuration.
Depending on whether it is a Citrix Cloud site or a Citrix On-Premises site, the provider needs to be configured differently:
terraform {
required_version = ">= 1.7.4"
required_providers {
citrix = {
source = "citrix/citrix"
version = ">=0.5.4"
}
}
}
# Configure the Citrix Cloud Provider
provider "citrix" {
customer_id = "${var.CC_CustomerID}"
client_id = "${var.CC_APIKey-ClientID}"
client_secret = "${var.CC_APIKey-ClientSecret}"
}
|
A guide for creating a secure API client can be found in Citrix Developer Docs and will be shown later.
terraform {
required_version = ">= 1.7.4"
required_providers {
citrix = {
source = "citrix/citrix"
version = ">=0.5.4"
}
}
}
# Configure the Citrix On-Premises Provider
provider "citrix" {
hostname = "${var.CVAD_DDC_HostName}"
client_id = "${var.CVAD_Admin_Account_UN}" #Domain\\Username
client_secret = "${var.CVAD_Admin_Account_Password}}"
}
|
Example - Schema used for the Provider configuration
-
client_id
(String): Client-ID for Citrix DaaS service authentication
For Citrix On-Premises customers: Use this variable to specify theDomain-Admin Username
.
For Citrix Cloud customers: Use this variable to specifyCloud API Key Client ID
.
Can be set via the Environment Variable CITRIX_CLIENT_ID. -
client_secret
(String, Sensitive): Client Secret for Citrix DaaS service authentication
For Citrix On-Premises customers: Use this variable to specify theDomain-Admin Password
.
For Citrix Cloud customers: Use this variable to specifyCloud API Key Client Secret
.
Can be set via the Environment Variable CITRIX_CLIENT_SECRET. -
customer_id
(String): Citrix Cloud customer ID
Only applicable for Citrix Cloud customers.
Can be set via the Environment Variable CITRIX_CUSTOMER_ID. -
disable_ssl_verification
(Boolean): Disable SSL verification against the target DDC
Only applicable to on-premises customers. Citrix Cloud customers do not need this option. Set totrue
to skip SSL verification only when the target DDC does not have a valid SSL certificate issued by a trusted CA.
When set totrue
, please make sure that your provider config is set for a known DDChostname
.
It is recommended to configure a valid certificate for the target DDC.
Can be set via the Environment Variable CITRIX_DISABLE_SSL_VERIFICATION. -
environment
(String): Citrix Cloud environment of the customer
Only applicable for Citrix Cloud customers. Available options:Production, Staging, Japan, JapanStaging
.
Can be set via the Environment Variable CITRIX_ENVIRONMENT. -
hostname
(String) : Hostname/base URL of Citrix DaaS service
For Citrix On-Premises customers (Required): Use this variable to specify the Delivery Controllerhostname
.
For Citrix Cloud customers (Optional): Use this variable to override the Citrix DaaS servicehostname
.
Can be set via the Environment Variable CITRIX_HOSTNAME.
Note:
All information about the Citrix Terraform provider can be found on GitHub.
Deploying a Citrix Cloud Resource location on Google Cloud Platform (GCP) using Terraform
Overview
This guide showcases the possibility of creating a complete Citrix Cloud Resource Location on Google Cloud Platform (GCP) using Terraform. We want to reduce manual interventions to the absolute minimum.
All Terraform configuration files can be found later on GitHub - we will update this guide as soon as the GitHub repository is ready.
In this guide, we will use an existing domain and will not deploy a new domain. For further instructions on deploying a new domain, please refer to the guide Citrix DaaS and Terraform—Automatic Deployment of a Resource Location on Microsoft Azure. Please note that this guide will be reworked soon!
The AD deployment used for this guide consists of a Hub-and-Spoke model - each Resource Location running on a Hypervisor/Hyperscaler is connected to the main Domain Controller using an IPSec-based Site-to-Site VPN. Each Resource Location has its own sub-domain.
NOTE: For easier reading and understanding, we did not use any
loop
-constructs orcount
keywords - each resource is created explicitly.
The Terraform flow is split into different parts:
-
Part One - this part can be run on any computer where Terraform is installed :
-
Creating the initially needed Resources on Google Cloud Platform (GCP) :
- Creating all needed Firewall rules and Tags on Google Cloud Platform (GCP)
- Creating all needed PowerShell scripts on Google Cloud Platform (GCP)
- Creating all needed IP- and DHCP configurations on Google Cloud Platform (GCP)
- Creating a needed Storage Bucket and its configuration on Google Cloud Platform (GCP)
- Creating a Windows Server 2022-based Master Image VM used for deploying the Machine Catalog in step 3
- Creating two Windows Server 2022-based VMs, which will be used as Cloud Connector VMs in Step 2
- Creating a Windows Server 2022-based VM acting as an Administrative workstation for running Terraform steps 2 and 3—this is necessary because you will use WinRM for further configuration and deployment in steps 2 and 3!
- Creating all necessary scripts for joining the VMs to the existing sub-domain
- Putting the VMs into the existing sub-domain
-
Creating the initially needed Resources on Google Cloud Platform (GCP) :
NOTE: The Master Image needs to be configured before it can be deployed - Terraform will configure a blank, Domain-joined VM! The Administrative VM must have Terraform installed - use the Chocolately-Guidance provided previously for installation
-
Part Two - this part can only be run on the previously created Administrative VM as the deployment of steps 2 and 3 relies heavily on WinRM:
-
Configuring the three previously created Virtual Machines on Google Cloud Platform (GCP):
- Installing the needed software on the CCs
- Installing the needed software on the Admin-VM
-
Creating the necessary Resources in Citrix Cloud:
- Creating a Resource Location in Citrix Cloud
- Uploading all relevant configurations to the Cloud Connector VMs
- Configuring the 2 Cloud Connectors
- Registering the 2 Cloud Connectors in the newly created Resource Location
-
-
Part Three:
-
Creating the Machine Catalog and Delivery Group in Citrix Cloud:
- Retrieving the Site- and Zone-ID of the Resource Location
- Creating a dedicated Hypervisor Connection to Google Cloud Platform (GCP)
- Creating a dedicated Hypervisor Resource Pool
- Creating a Machine Catalog (MC) in the newly created Resource Location
- Creating a Delivery Group (DG) based on the MC in the newly created Resource Location
- Setting the AutoScale configuration for the created Delivery Group
- Deploying some sample policies and binding them to the created Delivery Group
-
Creating the Machine Catalog and Delivery Group in Citrix Cloud:
NOTE: Please make sure that all Terraform-related VMs can communicate using WinRM.
If the Admin-VM is not able to connect to the Cloud Connectors using WinRM, the deployment will fail.
Various configuration guides for WinRM can be found on the Internet.
Determine if WinRM connections/communications are functioning
We strongly recommend a quick check to determine the communication before starting the Terraform scripts:
Open a PowerShell console and type:
|
A short Terraform script also checks if the communication via WinRM between the Admin-VM and, in this example, the CC1-VM is working as intended:
locals {
#### Test the WinRM communication
#### Need to invoke PowerShell as Domain User as the provisioner does not allow to be run in a Domain Users-context
TerraformTestWinRMScript = <<-EOT
$PSUsername = '${var.Provisioner_DomainAdmin-Username}'
$PSPassword = '${var.Provisioner_DomainAdmin-Password}'
$PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
$PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
Invoke-Command -ComputerName localhost -Credential $PSCredential -ScriptBlock {
$FileNameForData = 'C:\temp\xdinst\Processes.txt'
If (Test-Path $FileNameForData) {Remove-Item -Path $FileNameForData -Force}
Get-Process | Out-File -FilePath 'C:\temp\xdinst\Processes.txt'
}
EOT
}
#### Write script into local data-directory
resource "local_file" "WriteWinRMTestScriptIntoDataDirectory" {
filename = "${path.module}/data/Terraform-Test-WinRM.ps1"
content = local.TerraformTestWinRMScript
}
resource "null_resource" "CreateTestScriptOnCC1" {
connection {
type = var.Provisioner_Type
user = var.Provisioner_Admin-Username
password = var.Provisioner_Admin-Password
host = var.Provisioner_CC1-IP
timeout = var.Provisioner_Timeout
}
provisioner "file" {
source = "${path.module}/data/Terraform-Test-WinRM.ps1"
destination = "C:/temp/xdinst/Terraform-Test-WinRM.ps1"
}
provisioner "remote-exec" {
inline = [
"powershell -File 'C:/temp/xdinst/Terraform-Test-WinRM.ps1'"
]
}
}
|
If you can see something like the output below...
|
...then you can be sure that the provisioning using WinRM is working as intended!
Configuration using variables
All needed configuration settings are stored in the corresponding Variables that need to be set. Some Configuration settings are propagated throughout the whole Terraform configuration...
You need to start each of the 3 modules manually using the Terraform workflow terraform init
, terraform plan
, and terraform apply
in the corresponding module directory. Terraform then completes the necessary configuration steps of the corresponding module.
NOTE: Each module must be completed successfully before the next module can be started!
File System structure
Root-Directory
Module 1: _CConGCP-Creation
:
Filename | Purpose |
---|---|
_CConGCP-Creation-Create.tf
|
Resource configuration and primary flow definition |
_CConGCP-Creation-Create-variables.tf
|
Definition of Variables |
_CConGCP-Creation-Create.auto.tfvars.json
|
Setting the values of the Variables |
_CConGCP-Creation-Provider.tf
|
Provider definition and configuration |
_CConGCP-Creation-Provider-variables.tf
|
Definition of Variables |
_CConGCP-Creation-Provider.auto.tfvars.json
|
Setting the values of the Variables |
TF-Domain-Join-Script.ps1
|
Powershell-Script for joining the Admin-VM to the Domain |
DATA-Directory
|
All other needed files are placed in here (see later) |
Module 2: _CConGCP-Install
:
Filename | Purpose |
---|---|
_CConGCP-Install-CreatePreReqs.tf
|
Resource configuration and primary flow definition |
_CConGCP-Install-CreatePreReqs-variables.tf
|
Definition of Variables |
_CConGCP-Install-CreatePreReqs.auto.tfvars.json
|
Setting the values of the Variables |
_CConGCP-Install-Provider.tf
|
Provider definition and configuration |
_CConGCP-Install-Provider-variables.tf
|
Definition of Variables |
_CConGCP-Install-Provider.auto.tfvars.json
|
Setting the values of the Variables |
Module 3: _CConGCP-CCStuff
:
Filename | Purpose |
---|---|
_CConGCP-CCStuff-CreateCCEntities.tf
|
Resource configuration and primary flow definition |
_CConGCP-CCStuff-CreateCCEntities-variables.tf
|
Definition of Variables |
_CConGCP-CCStuff-CreateCCEntities.auto.tfvars.json
|
Setting the values of the Variables |
_CConGCP-CCStuff-CreateCCEntities-GCP.auto.tfvars.json
|
Extracted Service Account details for further use |
_CConGCP-CCStuff-Provider.tf
|
Provider definition and configuration |
_CConGCP-CCStuff-Provider-variables.tf
|
Definition of Variables |
_CConGCP-CCStuff-Provider.auto.tfvars.json
|
Setting the values of the Variables |
var.CC_Install_LogPath
-based directory:
Filename | Purpose |
---|---|
CitrixPosHSDK.exe
|
DaaS Remote PoSH SDK installer |
cwc.json
|
Configuration file for unattended Cloud Connector setup |
<gcp-project-id>.json
|
Service Account used for Authentication |
GetBT.ps1 , CreateRL.ps1 , GetSiteID.ps1 , GetZoneID.ps1 , ...
|
Various PowerShell scripts needed for deployment |
GetBT.txt , GetRLID.txt , GetSiteID.txt , GetZoneid.txt , ...
|
txt-files containing the results of Powershell |
NOTE: All Terraform-related directories and files (
.terraform
,-terrafrom.lock.hcl
,terraform.tfstate
,terraform.tfstate
) must not be changed or deleted - doing so might break the deployment!
Change the settings in the .json
files according to your needs.
Before setting the corresponding settings or running the Terraform workflow, the following prerequisites are needed to ensure a smooth and error-free build.
Prerequisites
Installing Google Cloud Tools for PowerShell and gcloud CLI
In this guide, we use gcloud CLI and PowerShell cmdlets to determine further needed information.
Further information about gcloud CLI and the installation/configuration can be found at gcloud Command Line Interface, and further information about the PowerShell cmdlets for Google Cloud can be found at Cloud Tools for PowerShell.
After installing gcloud CLI you need to configure it:
|
Enabling all needed APIs
Citrix Cloud interacts with your Google Cloud project by using several different APIs. These APIs aren't necessarily enabled by default, but they're necessary for Citrix Virtual Delivery Agent (VDA) fleet creation and lifecycle management. For Citrix Cloud to function, the following Google APIs must be enabled on your project:
- Compute Engine API
- Cloud Resource Manager API
- Identity and Access Management (IAM) API
- Cloud Build API
- Cloud Domain Name System (DNS) API
To enable the APIs, paste the following commands into gcloud CLI:
|
Existing Google Cloud Platform (GCP) entities
We anticipate that the following resources already exist and are already configured on Google Cloud Platform (GCP) :
- A working tenant
- All needed rights for the IAM user on the tenant
- A working Network structure with at least one subnet in the VPC
- A security group configured for allowing inbound connections from the subnet and partially from the Internet: WinRM-HTTP, WinRM-HTTPS, UDP, DNS (UDP and TCP), ICMP (for testing purposes), HTTP, HTTPS, TCP (for testing purposes), RDP. No blocking rules for outbound connections should be in place
- An access key with its secret (see a description of how to create the key later on)
- No bottlenecks/quotas that might block the deployment
You can get the needed information about the Network configuration by using gcloud
:
|
Note the Network-Name
(in this example default
), the Subnet-Name
, the Network-URI
and the Subnet-URI
of the Network configuration you want to use and put it into the corresponding .auto.tvars.json
file.
NOTE: Do NOT forget to enable Google Private Access on the corresponding Subnet: You can do it on the Subnet pag or by using
gcloud
.
Using gcloud
to enable GPA and check if GPA is enabled:
|
Using gcloud
to determine all used internal IPs:
|
As you must be aware of any default quotas that might break the deployment of the Worker VMs, you can get the quota information by using gcloud
:
|
You can filter the Quotas or change them using the corresponding Quota pages on the Google Cloud console:
Getting the available Compute Images from Google Cloud Platform (GCP)
We want to automatically deploy the virtual machines necessary for the Domain Controller and the Cloud Connectors - so we need detailed configuration settings:
Get the available Compute images:
|
Note the value of the NAME you want to use - for example windows-server-2022-dc-v20240313
.
Getting the available VM sizes from Google Cloud Platform (GCP)
We need to determine the available VM sizes. A gcloud
call helps us to list the available instance types on Google Cloud Platform (GCP):
|
We need to filter the results to narrow down usable instances for the Cloud Connectors and the Admin-VM - we want to use instances with max. 2 vCPUs:
|
|
Note the Name
parameter of the Google Cloud Platform (GCP) instances you want to use.
Caution: Be sure that your subscription has no quota limitation on the chosen VM type and you have enough resources on Google Cloud Platform (GCP) to create all the Virtual Machines planned to put into the Machine Catalog by checking quotas - otherwise the creation of the Machine Catalog fails if there are not enough compute resources available!
Creating a Service Account for Google Cloud Platform (GCP) Authentication or checking the rights of an existing Service Account
Google Identity and Access Management (IAM) grants you granular access to specific Google Cloud resources.
It is important to define WHO has access to WHICH resources and WHAT they can do with them. Service accounts live inside projects, similar to other resources you deploy on Google Cloud.
A Google Cloud Build Service Account is provisioned automatically once the Google APIs are enabled. The Cloud Build service account is identifiable with an email address that begins with the Project Number. The Cloud Build Account requires the following three roles:
- Cloud Build Service Account (assigned by default)
- Compute Instance Admin
- Service Account User
To enable Citrix Cloud to access all needed entities, you need to add the following roles:
- Compute Admin
- Storage Admin
- Cloud Build Editor
- Service Account User
- Cloud Datastore User
You can find detailed information about assigning these roles in the Tech Zone article Getting Started with Citrix DaaS on Google Cloud.
Use another gcloud
-call to check if the Service Account you plan to use is assigned to the project and has the required IAM roles:
|
Further Software Components for Configuration and Deployment
The Terraform deployment needs actual versions of the following software components:
-
Citrix Cloud Connector Installer:
cwcconnector.exe
. Download the Citrix Cloud Connector Installer -
Citrix Remote PowerShell SDK Installer:
CitrixPoshSdk.exe
. Download the Citrix Remote PowerShell SDK Installer
These components are required during the workflow. The Terraform engine looks for these files. In this guide we anticipate that the necessary software can be downloaded from a Storage Repository - Terraform creates a Storage Bucket where all necessary software is uploaded to.
The URIs of the Storage Repository can be set in the corresponding variables:
-
For the Cloud Connector:
"CC_Install_CWCURI":"https://storage.googleapis.com/XXXXXXXXXX/cwcconnector.exe"
-
For the Remote Powershell SDK:
"CC_Install_RPoSHURI":"https://storage.googleapis.com/XXXXXXXXXX/CitrixPoshSdk.exe"
Creating a Secure Client in Citrix Cloud
The Secure Client in Citrix Cloud is the same as the Access Key in Google Cloud Platform (GCP). It is used for Authentication.
API clients in Citrix Cloud are always tied to one administrator and one customer and are not visible to other administrators. If you want to access more than one customer, you must create API clients within each customer.
API clients are automatically restricted to the rights of the 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.
Select the Identity and Access Management option from the menu to create an API client. If this option does not appear, you do not have adequate permissions to create an API client. Contact your administrator to get the required permissions.
-
Click
API Access
,Secure Clients
and put a name in the textbox adjacent to the buttonCreate Client
. After entering a name. clickCreate Client
:
-
After the Secure Client is created, copy and write down the shown
ID
andSecret
:
The Secret is only visible during creation - after closing the window, you cannot get it anymore.
Theclient-id
andclient-secret
fields are needed by the Citrix Terraform provider.
The also neededcustomer-id
field can be found in your Citrix Cloud details.Put the values in the corresponding
.auto.tvars.json
file:
{
...
"CC_APIKey-ClientID":"f4xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxc15",
"CC_APIKey-ClientSecret":"VxxxxxxxxxxxxxxxxxxA==",
"CC_CustomerID": "uxxxxxxxxxxxxx",
"CC_APIKey_Type": "client_credentials",
...
}
|
Creating a Bearer Token in Citrix Cloud
The Bearer Token is needed to authorize some REST-API calls in Citrix Cloud.
As the Citrix provider has not implemented all functionalities yet, some REST-API calls are still needed. The Bearer Token authorizes these calls.
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 is [https://api-us.cloud.com/cctrustoauth2/1234567890/tokens/clients]
In this example, we use the Postman application to create a Bearer Token:
-
Paste the correct URI into Postman´s address bar and select POST as the method. Verify the correct settings of the API call.
![Creating a Bearer Token using Postman](/en-us/tech-zone/build/media/deployment-guides_citrix-daas-terraform-Google Cloud Platform (GCP) -create-bearertoken.png)
If everything is set correctly, Postman shows a Response containing a JSON-formatted file containing the Bearer token in the fieldaccess-token
: The token is normally valid for3600
seconds. -
Put the access-token value in the corresponding
.auto.tvars.json
file:
{..."CC_APIKey-ClientID":"f4xxxxxx-xxxx-xxxx-xxxx-Xxxxxxxxxc15","CC_APIKey-ClientSecret":"VxxxxxxxxxxxxxxxxxxA==","CC_CustomerID": "uxxxxxxxxxxxxx",
"CC_APIKey_Type": "client_credentials",
"CC_APIKey_Bearer": "CWSAuth bearer=eyJhbGciOiJSUzI1NiIsI...0q0IW7SZFVzeBittWnEwTYOZ7Q",...}
NOTE: In this deployment, Terraform automatically requests a valid Bearer Token using a REST-API call from Citrix Cloud and stores the Bearer Token on the hard disk.
If subsequent needs for having a Bearer Token occur during script progress, Terraform loads the Bearer Token from the hard drive.
You also can use PowerShell to request a Bearer Token - therefore, you need a valid Secure Client stored in Citrix Cloud:
|
Module 1: Create the initially needed Resources on Google Cloud Platform (GCP)
This module is split into the following configuration parts:
-
Creating the initially needed Resources on Google Cloud Platform (GCP):
- Creating all needed Firewall rules on Google Cloud Platform (GCP)
- Creating all needed internal IP addresses on Google Cloud Platform (GCP)
- Creating all needed IAM policies on Google Cloud Platform (GCP)
- Creating the Storage Bucket and uploading all required software items on Google Cloud Platform (GCP)
- Creating a Windows Server 2022-based Master Image VM used for deploying the Machine Catalog in Step 3
- Creating two Windows Server 2022-based VMs, which will be used as Cloud Connector VMs in Step 2
- Creating a Windows Server 2022-based VM acting as an Administrative workstation for running Terraform steps 2 and 3—this is necessary because you will use WinRM for further configuration and deployment in steps 2 and 3!
- Creating all necessary scripts for joining the VMs to the existing sub-domain
- Putting the VMs into the existing sub-domain
- Fetching and saving a valid Bearer Token
Terraform automatically does all these steps.
NOTE: This first part is separated from the next steps as we assume that there is no Admin-VM installed in the corresponding VPC/Subnet. The Admin-VM will be created now in Module 1 and all further modules must be run from it. Working with WinRM calls over the Internet would pose a security threat and would significantly reduce the performance of the Terraform-based deployment. These are the reasons for the split approach!
Please make sure you have configured the variables according to your needs.
The configuration can be started by following the normal Terraform workflow:
terraform init
,
terraform plan
and if no errors occur
terraform apply
|
As no errors occurred, Terraform has completed the creation and partial configuration of the relevant prerequisites on Google Cloud Platform (GCP).
Example of successful creation:
All VMs were successfully created:
|
Now, the next step can be started.
Module 2: Install and Configure all Resources in Google Cloud Platform (GCP)
IMPORTANT: This module can only be run on the previously created Administrative VM as the deployment of steps 2 and 3 relies heavily on WinRM! Please install Terraform on the Administrative VM before trying to run the scripts!
This module is split into the following configuration parts:
-
Configuring the three previously created Virtual Machines on Google Cloud Platform (GCP):
- Installing the needed software on the Cloud Connectors
- Installing the needed software on the Admin-VM
-
Creating the necessary Resources in Citrix Cloud:
- Creating a Resource Location in Citrix Cloud
- Configuring the 2 Cloud Connectors
- Registering the 2 Cloud Connectors in the newly created Resource Location
NOTE: This module has some complexity as many automated actions and many different entities are created on Google Cloud Platform (GCP) and run on the VMs. It contains a mixture of PowerShell scripts, REST-API calls and Terraform scripts to achieve a working deployment.
Our provider does not currently support creating a Resource Location on Citrix Cloud. Therefore, we use a PowerShell script to create one using a REST-API call.
Please make sure you have configured the variables according to your needs by using the corresponding .auto.tfvars.json
file.
Terraform runs various scripts before creating the Cloud Connectors' configuration to determine needed information, such as the Site ID, the Zone ID, and the Resource Location ID.
These IDs are used in other scripts or files - for example, the parameter file for deploying the Cloud Connector needs the Resource Location ID of the Resource Location, which Terraform creates automatically. Unfortunately, the REST-API provider does not return the ID of the newly created Resource Location, so we need to run PowerShell after the creation of the Resource Location:
Example scripts - creating the configuration file for the unattended installation of the Cloud Controller software:
At first, Terraform writes the configuration file without the Resource Location ID as the Resource Location is created later:
### Create CWC-Installer configuration file based on variables and save it into Transfer directory
resource "local_file" "CWC-Configuration" {
depends_on = [restapi_object.CreateRL]
content = jsonencode(
{
"customerName" = "${var.CC_CustomerID}",
"clientId" = "${var.CC_APIKey-ClientID}",
"clientSecret" = "${var.CC_APIKey-ClientSecret}",
"resourceLocationId" = "XXXXXXXXXX",
"acceptTermsOfService" = true
}
)
filename = "${var.CC_Install_LogPath}/DATA/cwc.json"
}
|
After installing further pre-requisites and creating the Resource Location, Terraform runs a PowerShell script to get the needed ID and updates the configuration file for the CWC installer:
#### Change RL-ID in CWC-JSON file to valid Zone-ID
resource "local_file" "CreateValidCWCOnAVM" {
depends_on = [ terraform_data.ZoneID2 ]
content = <<-EOT
Add-Content ${var.CC_Install_LogPath}/log.txt "`nCWC-Script started."
#### Need to invoke PowerShell as Domain User as the provisioner does not allow to be run in a Domain Users-context
$PSUsername = '${var.Provisioner_DomainAdmin-Username}'
$PSPassword = '${var.Provisioner_DomainAdmin-Password}'
$PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
$PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
Invoke-Command -ComputerName localhost -Credential $PSCredential -ScriptBlock {
$path = "${var.CC_Install_LogPath}"
# Correct the Resource Location ID in cwc.json file
$requestUri = "https://api-eu.cloud.com/resourcelocations"
$CCBearerToken = Get-Content -Path ${var.CC_Install_LogPath}/DATA/GetBT.txt -Force
$CCSiteID = Get-Content -Path ${var.CC_Install_LogPath}/DATA/GetSiteID.txt -Force
$CCZoneID = Get-Content -Path ${var.CC_Install_LogPath}/DATA/GetZoneID.txt -Force
$headers = @{ "Accept"="application/json"; "Authorization" = $CCBearerToken; "Citrix-CustomerId" = "${var.CC_CustomerID}"}
$response = Invoke-RestMethod -Uri $requestUri -Method GET -Headers $headers | Convertto-Json
Add-Content ${var.CC_Install_LogPath}/log.txt "`nCWC-Response: $response"
$RLs = ConvertFrom-Json $response
$RLFiltered = $RLs.items | Where-Object name -in "${var.CC_RestRLName}"
Add-Content ${var.CC_Install_LogPath}/log.txt $RLFiltered
$RLID = $RLFiltered.id
$OrigContent = Get-Content ${var.CC_Install_LogPath}/DATA/cwc.json
Add-Content ${var.CC_Install_LogPath}/log.txt $RLID
Add-Content ${var.CC_Install_LogPath}/log.txt $OrigContent
$CorrContent = $OrigCOntent.Replace('XXXXXXXXXX', $RLID) | Out-File -FilePath ${var.CC_Install_LogPath}/DATA/cwc.json -NoNewline -Encoding Ascii
$PathCompl = "${var.CC_Install_LogPath}/DATA/GetRLID.txt"
Set-Content -Path $PathCompl -Value $RLID -NoNewline -Encoding Ascii
Add-Content ${var.CC_Install_LogPath}/log.txt "`ncwc.json corrected."
Add-Content ${var.CC_Install_LogPath}/log.txt "`nCWC-Script completed."
}
EOT
filename = "${var.CC_Install_LogPath}/DATA/CreateValidCWCOnAVM.ps1"
}
|
The Terraform configuration contains some idle time slots to make sure that background operations on Google Cloud Platform (GCP) or the VMs can completed before the next configuration steps occur.
We have seen different elapsed configuration times related to different loads on the Google Cloud Platform (GCP) systems!
Before running Terraform, we cannot see the Resource Location:
The configuration can be started by following the normal Terraform workflow:
terraform init
,
terraform plan
and if no errors occur
terraform apply
|
This configuration completes the creation and configuration of all initial resources:
- Installing the needed software on the Cloud Connectors
- Creating a Resource Location in Citrix Cloud
- Configuring the 2 Cloud Connectors
- Registering the 2 Cloud Connectors in the newly created Resource Location
After successful runs of all needed scripts, we can see the new Resource Location in Citrix Cloud and the two Cloud Connectors bound to the Resource Location:
The environment is now ready to deploy a Machine Catalog and a Delivery Group using Module 3.
Module 3: Create all Resources in Google Cloud Platform (GCP) and Citrix Cloud
This module is split into the following configuration parts:
- Creating a Hypervisor Connection to Google Cloud Platform (GCP) and a Hypervisor Pool
- Creating a Machine Catalog (MC) in the newly created Resource Location
- Creating a Delivery Group (DG) based on the MC in the newly created Resource Location
- Deploying some example policies using Terraform
The Terraform configuration contains some idle time slots to ensure that background operations on Google Cloud Platform (GCP) or on the VMs can be completed before the next configuration steps occur.
We have seen different elapsed configuration times related to different loads on the Google Cloud Platform (GCP) systems! Before Terraform can create the Hypervisor Connection and the Hypervisor Pool, Terraform must retrieve the Site-ID and Zone-ID of the newly created Resource Location. As the Citrix Terraform Provider currently has no Cloud-level functionalities implemented, Terraform needs PowerShell scripts to retrieve the IDs. It created the necessary scripts with all the needed variables, saved the scripts, and executed them in Module 2.
NOTE: The Citrix Remote PowerShell SDK must be installed on the machine running Terraform as Terraform relies on the SDK!
After retrieving the IDs, Terraform configures a Hypervisor Connection to Google Cloud Platform (GCP) and a Hypervisor Resource Pool associated with the Hypervisor Connection. As soon as these prerequisites are completed, the Machine Catalog is created. After successfully creating the Hypervisor Connection, the Hypervisor Resource Pool, and the Machine Catalog, the last step of the deployment process starts - creating the Delivery Group.
The Terraform configuration assumes that all machines in the created Machine Catalog are used in the Delivery Group and that Autoscale will be configured for this Delivery Group.
More information about Autoscale can be found here: https://docs.citrix.com/en-us/tech-zone/learn/tech-briefs/autoscale.html
NOTE: This module has some complexity as many automated actions and many different entities are created. The entities can be configured using the corresponding
.auto.tfvars.json
file.
The deployment of Citrix Policies is a new feature built in version 0.5.2. We need to know the internal policy name, as localized policy names and descriptions are unusable.
Therefore, we need to use a PowerShell script to determine all internal names - some prerequisites are necessary for the script to work.
You can use any machine but the Cloud Connectors!
- Install the Citrix Supportability Pack
- Install the Citrix Group Policy Management - scroll down to Group Policy
After installation of the pre-requisites open a PowerShell console:
|
Using these names allows the deployment of policies by using the Terraform provider.
NOTE: Currently it is only supported to deploy policies using Terraform on-premise with CVAD 2402 or higher. Trying to deploy policies on a DaaS-based environment does not work yet, as the needed backend configurations are not generally available yet. We will update this guide as soon as the Cloud backend has all modifications generally rolled out.
Please make sure you have configured the variables according to your needs.
Caution:
Be sure that your subscription has no quota limitation on the chosen VM type and you have enough resources on Google Cloud Platform (GCP) to create all the Virtual Machines planned to put into the Machine Catalog by checking quotas - otherwise the creation of the Machine Catalog will fail if there are not enough compute resources available!
Before running Terraform, no Terraform-related entities were available:
No Terraform-related Hypervisor Connection to Google Cloud Platform exists on Citrix Cloud:
No Terraform-related Machine Catalog for Google Cloud Platform exists on Citrix Cloud:
No Terraform-related Delivery Group for Google Cloud Platform exists on Citrix Cloud:
The configuration can be started by following the normal Terraform workflow:
terraform init
,
terraform plan
and if no errors occur
terraform apply
|
This configuration completes the full deployment of a Citrix Cloud Resource Location in Google Cloud Platform (GCP).
The environment created by Terraform is now ready for usage, and all entities are in place:
The Hypervisor Connection and the Hypervisor Pool:
The Delivery Group and the Worker-VM:
The AutoScale settings of the Delivery Group:
Connection to the Worker-VMs Desktop:
Appendix
Examples of the Terraform scripts
Module 1: CConGCP-Creation
These are the Terraform configuration files for Module 1 (excerpts):
provider.tf
# Terraform deployment of Citrix DaaS on Google Cloud Platform (GCP)
## Definition of all required Terraform providers
terraform {
required_version = ">= 1.7.5"
required_providers {
restapi = {
source = "Mastercard/restapi"
version = ">=1.18.2"
}
citrix = {
source = "citrix/citrix"
version = ">=0.5.4"
}
google = {
source = "hashicorp/google"
version = ">=5.21.0"
}
}
}
# Configure the Google GCP Provider
provider "google" {
credentials = file(var.CCOnGCP-Creation-Provider-GCPAuthFileJSON)
project = var.CCOnGCP-Creation-Provider-GCPProject
region = var.CCOnGCP-Creation-Provider-GCPRegion
zone = var.CCOnGCP-Creation-Provider-GCPZone
}
# Configure the Citrix Provider
provider "citrix" {
customer_id = "${var.CC_CustomerID}"
client_id = "${var.CC_APIKey-ClientID}"
client_secret = "${var.CC_APIKey-ClientSecret}"
}
|
GetBearerToken.tf
### Create PowerShell file for determining the BearerToken
resource "local_file" "GetBearerToken" {
content = <<-EOT
asnp Citrix*
$key= "${var.CC_APIKey-ClientID}"
$secret= "${var.CC_APIKey-ClientSecret}"
$customer= "${var.CC_CustomerID}"
$XDStoredCredentials = Set-XDCredentials -StoreAs default -ProfileType CloudApi -CustomerId $customer -APIKey $key -SecretKey $secret
$auth = Get-XDAuthentication
$BT = $GLOBAL:XDAuthToken | Out-File "${path.module}/GetBT.txt" - Encoding Ascii - NoNewLine
EOT
filename = "${path.module}/GetBT.ps1"
}
### Running GetBearertoken-Script to retrieve the Bearer Token
resource "terraform_data" "GetBT" {
depends_on = [ local_file.GetBearerToken ]
provisioner "local-exec" {
command = "${path.module}/GetBT.ps1"
interpreter = ["PowerShell", "-File"]
}
}
### Retrieving the Bearer Token
data "local_file" "Retrieve_BT" {
depends_on = [ terraform_data.GetBT ]
filename = "${path.module}/GetBT.txt"
}
output "terraform_data_BR_Read" {
value = data.local_file.Retrieve_BT.content
}
|
create.tf
# Terraform deployment of Citrix DaaS on Google Cloud Platform
## Creation of all required entities - Networking
### Get VPC
data "google_compute_network" "VPC" {
name = "${lower(var.GCP_VPC_Name)}"
}
### Get Subnet
data "google_compute_subnetwork" "Subnet" {
name = "${lower(var.GCP_VPC_Subnet_Name)}"
}
### Create Firewall Rule - allow global HTTP
resource "google_compute_firewall" "Allow-HTTP" {
name = "${var.GCP_App_Global_Name}-fw-allow-http"
network = data.google_compute_network.VPC.name
allow {
protocol = "tcp"
ports = ["80"]
}
source_ranges = ["0.0.0.0/0"]
target_tags = ["http"]
}
### Create Firewall Rule - allow global HTTPS
resource "google_compute_firewall" "Allow-HTTPS" {
name = "${var.GCP_App_Global_Name}-fw-allow-https"
network = data.google_compute_network.VPC.name
allow {
protocol = "tcp"
ports = ["443"]
}
source_ranges = ["0.0.0.0/0"]
target_tags = ["https"]
}
### Create Firewall Rule - allow Subnet SSH
resource "google_compute_firewall" "Allow-SSH" {
name = "${var.GCP_App_Global_Name}-fw-allow-ssh"
network = data.google_compute_network.VPC.name
allow {
protocol = "tcp"
ports = ["22"]
}
source_ranges = ["${data.google_compute_subnetwork.Subnet.ip_cidr_range}"]
target_tags = ["ssh"]
}
### Create Firewall Rule - allow global RDP
resource "google_compute_firewall" "Allow-RDP" {
name = "${var.GCP_App_Global_Name}-fw-allow-rdp"
network = data.google_compute_network.VPC.name
allow {
protocol = "tcp"
ports = ["3389"]
}
source_ranges = ["0.0.0.0/0"]
target_tags = ["rdp"]
}
### Create Firewall Rule - allow Subnet WinRM
resource "google_compute_firewall" "Allow-WinRM" {
name = "${var.GCP_App_Global_Name}-fw-allow-winrm"
network = data.google_compute_network.VPC.name
allow {
protocol = "tcp"
ports = ["5985","5986"]
}
source_ranges = ["${data.google_compute_subnetwork.Subnet.ip_cidr_range}"]
target_tags = ["winrm"]
}
### Create Firewall Rule - allow Subnet All TCP
resource "google_compute_firewall" "Allow-All-TCP" {
name = "${var.GCP_App_Global_Name}-fw-allow-all-tcp"
network = data.google_compute_network.VPC.name
allow {
protocol = "tcp"
ports = ["0-65534"]
}
source_ranges = ["${data.google_compute_subnetwork.Subnet.ip_cidr_range}"]
target_tags = ["all-tcp"]
}
### Create Firewall Rule - allow Subnet All UDP
resource "google_compute_firewall" "Allow-All-UDP" {
name = "${var.GCP_App_Global_Name}-fw-allow-all-udp"
network = data.google_compute_network.VPC.name
allow {
protocol = "udp"
ports = ["0-65534"]
}
source_ranges = ["${data.google_compute_subnetwork.Subnet.ip_cidr_range}"]
target_tags = ["all-udp"]
}
### Create Firewall Rule - allow Subnet All ICMP
resource "google_compute_firewall" "Allow-All-ICMP" {
name = "${var.GCP_App_Global_Name}-fw-allow-all-icmp"
network = data.google_compute_network.VPC.name
allow {
protocol = "icmp"
}
source_ranges = ["${data.google_compute_subnetwork.Subnet.ip_cidr_range}"]
target_tags = ["all-icmp"]
}
### Create static internal IP address for CC1
resource "google_compute_address" "internal-ip-cc1" {
name ="internal-ip-cc1"
subnetwork = data.google_compute_subnetwork.Subnet.id
address_type = "INTERNAL"
address = "${var.GCP_VPC_InternalIP_CC1}"
region = "${var.CCOnGCP-Creation-Provider-GCPRegion}"
}
### Create static internal IP address for CC2
resource "google_compute_address" "internal-ip-cc2" {
name ="internal-ip-cc2"
subnetwork = data.google_compute_subnetwork.Subnet.id
address_type = "INTERNAL"
address = "${var.GCP_VPC_InternalIP_CC2}"
region = "${var.CCOnGCP-Creation-Provider-GCPRegion}"
}
### Create static internal IP Address for Admin-VM
resource "google_compute_address" "internal-ip-adminvm" {
name ="internal-ip-avm"
subnetwork = data.google_compute_subnetwork.Subnet.id
address_type = "INTERNAL"
address = "${var.GCP_VPC_InternalIP_AdminVM}"
region = "${var.CCOnGCP-Creation-Provider-GCPRegion}"
}
### Create static internal IP Address for WMI
resource "google_compute_address" "internal-ip-wmi" {
name ="internal-ip-wmi"
subnetwork = data.google_compute_subnetwork.Subnet.id
address_type = "INTERNAL"
address = "${var.GCP_VPC_InternalIP_WMI}"
region = "${var.CCOnGCP-Creation-Provider-GCPRegion}"
}
### Create PowerShell file for joining the domain
resource "local_file" "CreateDomainJoinScript" {
content = <<-EOT
Start-Sleep -Seconds 10
net user administrator /active:yes
net user administrator ${var.GCP_App_Global_LocalAdminPW}
netdom.exe join $env:COMPUTERNAME /domain:'${var.GCP_App_Global_FullDomainName}' /UserD:'${var.GCP_App_Global_DomainAdminUPN}' /PasswordD:'${var.GCP_App_Global_DomainAdminPW}' /reboot:5
EOT
filename = "${path.module}/TF-Domain-Join-Script.ps1"
}
### Create a Storage Bucket for storage of all needed stuff
resource "google_storage_bucket" "Prereqs" {
depends_on = [ local_file.CreateDomainJoinScript ]
name = "${var.GCP_App_Global_Name}-storagebucket"
location = "EU"
storage_class = "STANDARD"
force_destroy = true
}
resource "google_storage_bucket_access_control" "Prereqs" {
bucket = google_storage_bucket.Prereqs.name
role = "READER"
entity = "allUsers"
}
resource "google_storage_bucket_iam_binding" "iam" {
depends_on = [ google_storage_bucket.Prereqs ]
bucket = google_storage_bucket.Prereqs.name
members = [ "allUsers" ]
role = "roles/storage.objectViewer"
}
#### Upload all required Software
resource "google_storage_bucket_object" "Prereqs" {
depends_on = [ google_storage_bucket.Prereqs ]
name = "domain-join-script.ps1"
source = "${path.module}/TF-Domain-Join-Script.ps1"
bucket = google_storage_bucket.Prereqs.name
}
resource "google_storage_bucket_object" "CC" {
depends_on = [ google_storage_bucket.Prereqs ]
name = "cwcconnector.exe"
source = "${path.module}/DATA/cwcconnector.exe"
bucket = google_storage_bucket.Prereqs.name
}
resource "google_storage_bucket_object" "PoSH" {
depends_on = [ google_storage_bucket.Prereqs ]
name = "CitrixPoSHSDK.exe"
source = "${path.module}/DATA/CitrixPoSHSDK.exe"
bucket = google_storage_bucket.Prereqs.name
}
resource "google_storage_bucket_object" "CHC" {
depends_on = [ google_storage_bucket.Prereqs ]
name = "CloudHealthCheckInstaller_x64.msi"
source = "${path.module}/DATA/CloudHealthCheckInstaller_x64.msi"
bucket = google_storage_bucket.Prereqs.name
}
## Create the VMs
### Create the CC1-VM
resource "google_compute_instance" "CC1" {
depends_on = [ google_compute_address.internal-ip-cc1,google_storage_bucket_object.Prereqs ]
name = "${var.GCP_VM_CC1_Name}"
hostname = "${var.GCP_VM_CC1_Name}.${var.GCP_App_Global_FullDomainName}"
machine_type = "${var.GCP_VM_CC_InstanceType}"
zone = "${var.CCOnGCP-Creation-Provider-GCPZone}"
tags = ["rdp","http","https","winrm", "all-tcp", "all-udp", "all-icmp","http-server","https-server"]
boot_disk {
initialize_params {
image = "${var.GCP_VM_CC_ImageName}"
}
}
metadata = {
sysprep-specialize-script-url = "https://storage.googleapis.com/tacg-gcp-tf-storagebucket/domain-join-script.ps1"
}
network_interface {
network = data.google_compute_network.VPC.name
subnetwork = data.google_compute_subnetwork.Subnet.name
network_ip = "${var.GCP_VPC_InternalIP_CC1}"
access_config { }
}
service_account {
email = "${var.GCP_VM_ServiceAccount}"
scopes = ["cloud-platform"]
}
}
### Create the CC2-VM
resource "google_compute_instance" "CC2" {
depends_on = [ google_compute_address.internal-ip-cc2,google_storage_bucket_object.Prereqs ]
name = "${var.GCP_VM_CC2_Name}"
hostname = "${var.GCP_VM_CC2_Name}.${var.GCP_App_Global_FullDomainName}"
machine_type = "${var.GCP_VM_CC_InstanceType}"
zone = "${var.CCOnGCP-Creation-Provider-GCPZone}"
tags = ["rdp","http","https","winrm", "all-tcp", "all-udp", "all-icmp","http-server","https-server"]
boot_disk {
initialize_params {
image = "${var.GCP_VM_CC_ImageName}"
}
}
metadata = {
sysprep-specialize-script-url = "https://storage.googleapis.com/tacg-gcp-tf-storagebucket/domain-join-script.ps1"
}
network_interface {
network = data.google_compute_network.VPC.name
subnetwork = data.google_compute_subnetwork.Subnet.name
network_ip = "${var.GCP_VPC_InternalIP_CC2}"
access_config { }
}
service_account {
email = "${var.GCP_VM_ServiceAccount}"
scopes = ["cloud-platform"]
}
}
### Create the Admin-VM
resource "google_compute_instance" "AdminVM" {
depends_on = [ google_compute_address.internal-ip-adminvm, google_storage_bucket_object.Prereqs ]
name = "${var.GCP_VM_AdminVM_Name}"
hostname = "${var.GCP_VM_AdminVM_Name}.${var.GCP_App_Global_FullDomainName}"
machine_type = "${var.GCP_VM_CC_InstanceType}"
zone = "${var.CCOnGCP-Creation-Provider-GCPZone}"
tags = ["rdp","http","https","winrm", "all-tcp", "all-udp", "all-icmp","http-server","https-server"]
boot_disk {
initialize_params {
image = "${var.GCP_VM_CC_ImageName}"
}
}
metadata = {
sysprep-specialize-script-url = "https://storage.googleapis.com/tacg-gcp-tf-storagebucket/domain-join-script.ps1"
#sysprep-specialize-script-url = google_storage_bucket_object.Prereqs.name
}
network_interface {
network = data.google_compute_network.VPC.name
subnetwork = data.google_compute_subnetwork.Subnet.name
network_ip = "${var.GCP_VPC_InternalIP_AdminVM}"
access_config { }
}
service_account {
email = "${var.GCP_VM_ServiceAccount}"
scopes = ["cloud-platform"]
}
}
### Create the WMI-VM
resource "google_compute_instance" "WMI" {
depends_on = [ google_compute_address.internal-ip-wmi, google_storage_bucket_object.Prereqs ]
name = "${var.GCP_VM_WMI_Name}"
hostname = "${var.GCP_VM_WMI_Name}.${var.GCP_App_Global_FullDomainName}"
machine_type = "${var.GCP_VM_CC_InstanceType_WMI}"
zone = "${var.CCOnGCP-Creation-Provider-GCPZone}"
tags = ["rdp","http","https","winrm", "all-tcp", "all-udp", "all-icmp","http-server","https-server"]
boot_disk {
initialize_params {
image = "${var.GCP_VM_CC_ImageName}"
}
}
metadata = {
sysprep-specialize-script-url = "https://storage.googleapis.com/tacg-gcp-tf-storagebucket/domain-join-script.ps1"
}
network_interface {
network = data.google_compute_network.VPC.name
subnetwork = data.google_compute_subnetwork.Subnet.name
network_ip = "${var.GCP_VPC_InternalIP_WMI}"
access_config { }
}
service_account {
email = "${var.GCP_VM_ServiceAccount}"
scopes = ["cloud-platform"]
}
}
|
Module 2: CConGCP-Install
These are the Terraform configuration files for Module 2 (excerpts):
provider.tf
# Terraform deployment of Citrix DaaS on Google Cloud Platform (GCP)
## Definition of all required Terraform providers
terraform {
required_version = ">= 1.7.5"
required_providers {
restapi = {
source = "Mastercard/restapi"
version = ">=1.18.2"
}
citrix = {
source = "citrix/citrix"
version = ">=0.5.4"
}
google = {
source = "hashicorp/google"
version = ">=5.21.0"
}
}
}
# Configure the Google GCP Provider
provider "google" {
credentials = file(var.CCOnGCP-Creation-Provider-GCPAuthFileJSON)
project = var.CCOnGCP-Creation-Provider-GCPProject
region = var.CCOnGCP-Creation-Provider-GCPRegion
zone = var.CCOnGCP-Creation-Provider-GCPZone
}
# Configure the Citrix Provider
provider "citrix" {
customer_id = "${var.CC_CustomerID}"
client_id = "${var.CC_APIKey-ClientID}"
client_secret = "${var.CC_APIKey-ClientSecret}"
}
|
CreatePreReqs.tf
# Terraform deployment of Citrix DaaS on Google Cloud Platform
locals {
}
## Create all Pre-requisites
### Create local directory
resource "local_file" "Log" {
content = "Directory created."
filename = "${var.CC_Install_LogPath}/log.txt"
}
resource "local_file" "LogData" {
depends_on = [ local_file.Log ]
content = "Directory created."
filename = "${var.CC_Install_LogPath}/DATA/log.txt"
}
data "google_compute_address" "IPOfCC1" {
name = "internal-ip-cc1"
}
data "google_compute_address" "IPOfCC2" {
name = "internal-ip-cc2"
}
### Create PowerShell file for installing the Citrix Remote PoSH SDK on AVM
resource "local_file" "InstallPoSHSDKOnAVM" {
content = <<-EOT
#### Need to invoke PowerShell as Domain User as the provisioner does not allow to be run in a Domain Users-context
$PSUsername = '${var.Provisioner_DomainAdmin-Username}'
$PSPassword = '${var.Provisioner_DomainAdmin-Password}'
$PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
$PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
Invoke-Command -ComputerName localhost -Credential $PSCredential -ScriptBlock {
$path = "${var.CC_Install_LogPath}"
If(!(test-path -PathType container $path))
{
New-Item -ItemType Directory -Path $path
}
Add-Content ${var.CC_Install_LogPath}/log.txt "`nScript started."
# Download Citrix Remote PowerShell SDK
Invoke-WebRequest '${var.CC_Install_RPoSHURI}' -OutFile '${var.CC_Install_LogPath}/DATA/CitrixPoshSdk.exe'
Add-Content ${var.CC_Install_LogPath}/log.txt "`nPowerShell SDK downloaded."
# Install Citrix Remote PowerShell SDK
Start-Process -Filepath "${var.CC_Install_LogPath}/DATA/CitrixPoshSdk.exe" -ArgumentList "-quiet"
Add-Content ${var.CC_Install_LogPath}/log.txt "`nPowerShell SDK installed."
# Timeout to settle all processes
Start-Sleep -Seconds 60
Add-Content ${var.CC_Install_LogPath}/log.txt "`nTimeout elapsed."
}
EOT
filename = "${var.CC_Install_LogPath}/DATA/InstallPoSHSDKOnAVM.ps1"
}
#### Execute Pre-Reqs-Script on AVM
resource "null_resource" "ExecuteInstallPoSHSDKOnAVM" {
provisioner "local-exec" {
command = " ${var.CC_Install_LogPath}/DATA/InstallPoSHSDKOnAVM.ps1"
interpreter = ["PowerShell", "-Command"]
}
}
### Create PowerShell file for extracting the GCP credentials in a Tarraform variable file on AVM
resource "local_file" "ExtractGCPCredentialsToVarFile" {
content = <<-EOT
#### Need to invoke PowerShell as Domain User as the provisioner does not allow to be run in a Domain Users-context
$PSUsername = '${var.Provisioner_DomainAdmin-Username}'
$PSPassword = '${var.Provisioner_DomainAdmin-Password}'
$PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
$PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
Invoke-Command -ComputerName localhost -Credential $PSCredential -ScriptBlock {
Add-Content ${var.CC_Install_LogPath}/log.txt "`nGCP-Extraction Script started."
$json = Get-Content '${var.CC_Install_LogPath}/${var.CCOnGCP-Creation-Provider-GCPAuthFileJSON}' | Out-String | ConvertFrom-Json
$key = "`"tv_private_key`"" + ":" + "`"" + $json.private_key + "`""
$keycorr = [string]::join("\n",($key.Split("`n")))
$email = "`"tv_client_email`"" + ":" + "`"" + $json.client_email + "`""
$Path = 'c:/TACG/_CCOnGCP/_CCOnGCP-CCStuff/_CCOnGCP-CCStuff-CreateCCEntities-GCP.auto.tfvars.json'
Set-Content -Path $Path -Encoding Ascii -Value "{"
Add-Content -Path $Path -Encoding Ascii -Value ($keycorr + ",")
Add-Content -Path $Path -Encoding Ascii -Value $email
Add-Content -Path $Path -NoNewline -Encoding Ascii -Value '}'
Add-Content ${var.CC_Install_LogPath}/log.txt "`nGCP-Extraction Script stopped."
}
EOT
filename = "${var.CC_Install_LogPath}/DATA/ExtractGCPCredentialsToVarFile.ps1"
}
#### Execute Pre-Reqs-Script on AVM
resource "null_resource" "ExecuteExtractGCPCredentialsToVarFile" {
provisioner "local-exec" {
command = " ${var.CC_Install_LogPath}/DATA/ExtractGCPCredentialsToVarFile.ps1"
interpreter = ["PowerShell", "-Command"]
}
}
### Create CWC-Installer configuration file based on variables and save it into Transfer directory
resource "local_file" "CWC-Configuration" {
content = jsonencode(
{
"customerName" = "${var.CC_CustomerID}",
"clientId" = "${var.CC_APIKey-ClientID}",
"clientSecret" = "${var.CC_APIKey-ClientSecret}",
"resourceLocationId" = "XXXXXXXXXX",
"acceptTermsOfService" = true
}
)
filename = "${var.CC_Install_LogPath}/DATA/cwc.json"
}
### Retrieving the BearerToken
#### Create PowerShell script to download BearerToken
resource "local_file" "GetBearerToken" {
content = <<-EOT
asnp Citrix*
$key= "${var.CC_APIKey-ClientID}"
$secret= "${var.CC_APIKey-ClientSecret}"
$customer= "${var.CC_CustomerID}"
$XDStoredCredentials = Set-XDCredentials -StoreAs default -ProfileType CloudApi -CustomerId $customer -APIKey $key -SecretKey $secret
$auth = Get-XDAuthentication
$BT = $GLOBAL:XDAuthToken | Out-File "${var.CC_Install_LogPath}/DATA/GetBT.txt" -NoNewline -Encoding Ascii
EOT
filename = "${var.CC_Install_LogPath}/DATA/GetBT.ps1"
}
#### Running GetBearertoken-Script to retrieve the Bearer Token
resource "terraform_data" "GetBT" {
depends_on = [ local_file.GetBearerToken, null_resource.ExecuteInstallPoSHSDKOnAVM]
provisioner "local-exec" {
command = "${var.CC_Install_LogPath}/DATA/GetBT.ps1"
interpreter = ["PowerShell", "-File"]
}
}
### Create a dedicated Resource Location in Citrix Cloud
#### Create the script to create a dedicated Resource Location in Citrix Cloud
resource "local_file" "CreateRLScript" {
depends_on = [ terraform_data.GetBT ]
content = <<-EOT
Add-Content ${var.CC_Install_LogPath}/log.txt "`nCreateRL-Script started."
$CCCustomerID = "${var.CC_CustomerID}"
$CCBearerToken = Get-Content -Path ${var.CC_Install_LogPath}/DATA/GetBT.txt -Force
$CCName ="${var.CC_RestRLName}"
$CCGuid = New-Guid
#
$requestUri = "https://api-eu.cloud.com/resourcelocations"
$headers = @{ "Accept"="application/json"; "Authorization" = $CCBearerToken; "Citrix-CustomerId" = $CCCustomerID; "Content-Type" = "application/json" }
$Body = @{ "id"=$CCGuid; "name" = $CCName; "internalOnly" = $false; "timeZone" = "GMT Standard Time"; "readOnly" = $false}
$Bodyjson = $Body | Convertto-Json -Depth 3
$response = Invoke-RestMethod -Uri $requestUri -Method POST -Headers $headers -Body $Bodyjson -ContentType "application/json"
Add-Content ${var.CC_Install_LogPath}/log.txt "`n$response"
Add-Content ${var.CC_Install_LogPath}/log.txt "`nCreateRL-Script finished."
EOT
filename = "${var.CC_Install_LogPath}/DATA/CreateRL.ps1"
}
#### Running the Resource Location-Script to generate the Resource Location
resource "terraform_data" "ResourceLocation" {
depends_on = [ local_file.CreateRLScript ]
provisioner "local-exec" {
command = "${var.CC_Install_LogPath}/DATA/CreateRL.ps1"
interpreter = ["PowerShell", "-File"]
}
}
#### Wait 10 mins after RL creation to settle Zone creation
resource "time_sleep" "wait_900_seconds" {
depends_on = [ terraform_data.ResourceLocation ]
create_duration = "900s"
}
#### Wait 1 mins after RL creation to settle Zone creation
resource "time_sleep" "wait_60_seconds" {
depends_on = [ terraform_data.ResourceLocation ]
create_duration = "60s"
}
### Create PowerShell file for determining the SiteID
resource "local_file" "GetSiteIDScript" {
depends_on = [time_sleep.wait_60_seconds]
content = <<-EOT
Add-Content ${var.CC_Install_LogPath}/log.txt "`nSiteID-Script started."
$requestUri = "https://api-eu.cloud.com/cvad/manage/me"
$CCBearerToken = Get-Content -Path ${var.CC_Install_LogPath}/DATA/GetBT.txt -Force
$headers = @{ "Accept"="application/json"; "Authorization" = $CCBearerToken; "Citrix-CustomerId" = "${var.CC_CustomerID}" }
$response = Invoke-RestMethod -Uri $requestUri -Method GET -Headers $headers | Select-Object Customers
$responsetojson = $response | Convertto-Json -Depth 3
$responsekorr = $responsetojson -replace("null","""empty""")
$responsefromjson = $responsekorr | Convertfrom-json
$SitesObj=$responsefromjson.Customers[0].Sites[0]
$Export1 = $SitesObj -replace("@{Id=","")
$SplittedString = $Export1.Split(";")
$SiteID= $SplittedString[0]
$PathCompl = "${var.CC_Install_LogPath}/DATA/GetSiteID.txt"
Set-Content -Path $PathCompl -Value $SiteID -NoNewline -Encoding Ascii
Add-Content ${var.CC_Install_LogPath}/log.txt "`nSiteID-Script successfully completed."
EOT
filename = "${var.CC_Install_LogPath}/DATA/GetSiteID.ps1"
}
#### Running the SiteID-Script to generate the SiteID
resource "terraform_data" "SiteID" {
depends_on = [ local_file.GetSiteIDScript ]
provisioner "local-exec" {
command = "${var.CC_Install_LogPath}/DATA/GetSiteID.ps1"
interpreter = ["PowerShell", "-File"]
}
}
### Create PowerShell file for determining the ZoneID
resource "local_file" "GetZoneIDScript" {
depends_on = [time_sleep.wait_900_seconds,terraform_data.SiteID]
content = <<-EOT
Add-Content ${var.CC_Install_LogPath}/log.txt "`nZoneID-Script started."
$requestUri = "https://api-eu.cloud.com/cvad/manage/Zones"
$CCBearerToken = Get-Content -Path ${var.CC_Install_LogPath}/DATA/GetBT.txt -Force
$CCSiteID = Get-Content -Path ${var.CC_Install_LogPath}/DATA/GetSiteID.txt -Force
Add-Content ${var.CC_Install_LogPath}/log.txt "`nBearer-Token: $CCBearerToken"
Add-Content ${var.CC_Install_LogPath}/log.txt "`nSiteID: $CCSiteID"
$headers = @{ "Accept"="application/json"; "Authorization" = $CCBearerToken; "Citrix-CustomerId" = "${var.CC_CustomerID}"; "Citrix-InstanceId" = $CCSiteID }
Add-Content ${var.CC_Install_LogPath}/log.txt "`nHeader: $headers"
$response = Invoke-RestMethod -Uri $requestUri -Method GET -Headers $headers | Convertto-Json
Add-Content ${var.CC_Install_LogPath}/log.txt "`nResponse: $response"
$responsedejson = $response | ConvertFrom-Json
Add-Content ${var.CC_Install_LogPath}/log.txt "`nResponseDeJSON: $responsedejeson"
$ZoneId = $responsedejson.Items | Where-Object { $_.Name -eq "${var.CC_RestRLName}" } | Select-Object id
$Export1 = $ZoneId -replace("@{Id=","")
$ZoneID = $Export1 -replace("}","")
$PathCompl = "${var.CC_Install_LogPath}/DATA/GetZoneID.txt"
Set-Content -Path $PathCompl -Value $ZoneID -NoNewline -Encoding Ascii
Add-Content ${var.CC_Install_LogPath}/log.txt "`nZoneID-Script completed."
EOT
filename = "${var.CC_Install_LogPath}/DATA/GetZoneID.ps1"
}
#### Running the ZoneID-Script to generate the ZoneID
resource "terraform_data" "ZoneID" {
depends_on = [ local_file.GetZoneIDScript ]
provisioner "local-exec" {
command = "${var.CC_Install_LogPath}/DATA/GetZoneID.ps1"
interpreter = ["PowerShell", "-File"]
}
}
resource "terraform_data" "ZoneID2" {
depends_on = [ terraform_data.ZoneID ]
provisioner "local-exec" {
command = "${var.CC_Install_LogPath}/DATA/GetZoneID.ps1"
interpreter = ["PowerShell", "-File"]
}
}
#### Change RL-ID in CWC-JSON file to valid Zone-ID
resource "local_file" "CreateValidCWCOnAVM" {
depends_on = [ terraform_data.ZoneID2 ]
content = <<-EOT
Add-Content ${var.CC_Install_LogPath}/log.txt "`nCWC-Script started."
#### Need to invoke PowerShell as Domain User as the provisioner does not allow to be run in a Domain Users-context
$PSUsername = '${var.Provisioner_DomainAdmin-Username}'
$PSPassword = '${var.Provisioner_DomainAdmin-Password}'
$PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
$PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
Invoke-Command -ComputerName localhost -Credential $PSCredential -ScriptBlock {
$path = "${var.CC_Install_LogPath}"
# Correct the Resource Location ID in cwc.json file
$requestUri = "https://api-eu.cloud.com/resourcelocations"
$CCBearerToken = Get-Content -Path ${var.CC_Install_LogPath}/DATA/GetBT.txt -Force
$CCSiteID = Get-Content -Path ${var.CC_Install_LogPath}/DATA/GetSiteID.txt -Force
$CCZoneID = Get-Content -Path ${var.CC_Install_LogPath}/DATA/GetZoneID.txt -Force
$headers = @{ "Accept"="application/json"; "Authorization" = $CCBearerToken; "Citrix-CustomerId" = "${var.CC_CustomerID}"}
$response = Invoke-RestMethod -Uri $requestUri -Method GET -Headers $headers | Convertto-Json
Add-Content ${var.CC_Install_LogPath}/log.txt "`nCWC-Response: $response"
$RLs = ConvertFrom-Json $response
$RLFiltered = $RLs.items | Where-Object name -in "${var.CC_RestRLName}"
Add-Content ${var.CC_Install_LogPath}/log.txt $RLFiltered
$RLID = $RLFiltered.id
$OrigContent = Get-Content ${var.CC_Install_LogPath}/DATA/cwc.json
Add-Content ${var.CC_Install_LogPath}/log.txt $RLID
Add-Content ${var.CC_Install_LogPath}/log.txt $OrigContent
$CorrContent = $OrigCOntent.Replace('XXXXXXXXXX', $RLID) | Out-File -FilePath ${var.CC_Install_LogPath}/DATA/cwc.json -NoNewline -Encoding Ascii
$PathCompl = "${var.CC_Install_LogPath}/DATA/GetRLID.txt"
Set-Content -Path $PathCompl -Value $RLID -NoNewline -Encoding Ascii
Add-Content ${var.CC_Install_LogPath}/log.txt "`ncwc.json corrected."
Add-Content ${var.CC_Install_LogPath}/log.txt "`nCWC-Script completed."
}
EOT
filename = "${var.CC_Install_LogPath}/DATA/CreateValidCWCOnAVM.ps1"
}
#### Running the CWC-Script to generate the ZoneID
resource "terraform_data" "ExecuteCreateValidCWCOnAVM" {
depends_on = [ local_file.CreateValidCWCOnAVM ]
provisioner "local-exec" {
command = "${var.CC_Install_LogPath}/DATA/CreateValidCWCOnAVM.ps1"
interpreter = ["PowerShell", "-File"]
}
}
#### Create PowerShell file for CWC-Installer-Script for CCs
##### Check %LOCALAPPDATA%\Temp\CitrixLogs\CloudServicesSetup and %ProgramData%\Citrix\WorkspaceCloud\InstallLogs for logs!!!!!
resource "local_file" "InstallCWCOnCC" {
#depends_on = [ terraform_data.ExecuteCreateValidCWCOnAVM ]
content = <<-EOT
#### Need to invoke PowerShell as Domain User as the provisioner does not allow to be run in a Domain Users-context
$PSUsername = '${var.Provisioner_DomainAdmin-Username}'
$PSPassword = '${var.Provisioner_DomainAdmin-Password}'
$PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
$PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
Invoke-Command -ComputerName localhost -Credential $PSCredential -ScriptBlock {
$path = "${var.CC_Install_LogPath}"
If(!(test-path -PathType container $path))
{
New-Item -ItemType Directory -Path $path
}
Add-Content ${var.CC_Install_LogPath}/log.txt "`nScript started."
# Download the Citrix Cloud Connector-Software to CC
Invoke-WebRequest ${var.CC_Install_CWCURI} -OutFile '${var.CC_Install_LogPath}/DATA/CWCConnector.exe'
# Install Citrix Cloud Controller based on the cwc.json configuration file
# Check %LOCALAPPDATA%\Temp\CitrixLogs\CloudServicesSetup and %ProgramData%\Citrix\WorkspaceCloud\InstallLogs for logs!!!!!
Add-Content ${var.CC_Install_LogPath}/log.txt "`nInstalling Cloud Connector."
Start-Process -Wait -Filepath "${var.CC_Install_LogPath}/DATA/CWCConnector.exe" -ArgumentList "/q /ParametersFilePath:${var.CC_Install_LogPath}/DATA/cwc.json"
Add-Content ${var.CC_Install_LogPath}/log.txt "`nInstalled Cloud Connector."
#Restart-Computer -Force
}
EOT
filename = "${var.CC_Install_LogPath}/DATA/InstallCWCOnCC.ps1"
}
#### Create restart script
resource "local_file" "RestartCC" {
#depends_on = [ terraform_data.ExecuteCreateValidCWCOnAVM ]
content = <<-EOT
#### Need to invoke PowerShell as Domain User as the provisioner does not allow to be run in a Domain Users-context
$PSUsername = '${var.Provisioner_DomainAdmin-Username}'
$PSPassword = '${var.Provisioner_DomainAdmin-Password}'
$PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
$PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
Invoke-Command -ComputerName localhost -Credential $PSCredential -ScriptBlock {
Restart-Computer -Force
}
EOT
filename = "${var.CC_Install_LogPath}/DATA/RestartCC.ps1"
}
#######################################################################################################################################################################
### Upload required components to CC1
#### Set the Provisioner-Connection
resource "null_resource" "UploadRequiredComponentsToCC1" {
depends_on = [ local_file.InstallCWCOnCC ]
connection {
type = var.Provisioner_Type
user = var.Provisioner_Admin-Username
password = var.Provisioner_Admin-Password
host = data.google_compute_address.IPOfCC1.address
timeout = var.Provisioner_Timeout
}
###### Upload Cloud Connector configuration file to CC1
provisioner "file" {
source = "${var.CC_Install_LogPath}/DATA/cwc.json"
destination = "${var.CC_Install_LogPath}/DATA/cwc.json"
}
###### Upload SiteID file to CC1
provisioner "file" {
source = "${var.CC_Install_LogPath}/DATA/GetSiteID.txt"
destination = "${var.CC_Install_LogPath}/DATA/GetSiteID.txt"
}
###### Upload ZoneID file to CC1
provisioner "file" {
source = "${var.CC_Install_LogPath}/DATA/GetZoneID.txt"
destination = "${var.CC_Install_LogPath}/DATA/GetZoneID.txt"
}
###### Upload RLID file to CC1
provisioner "file" {
source = "${var.CC_Install_LogPath}/DATA/GetRLID.txt"
destination = "${var.CC_Install_LogPath}/DATA/GetRLID.txt"
}
###### Upload PreReqs script to CC1
provisioner "file" {
source = "${var.CC_Install_LogPath}/DATA/InstallCWCOnCC.ps1"
destination = "${var.CC_Install_LogPath}/DATA/InstallCWCOnCC.ps1"
}
###### Upload Restart script to CC1
provisioner "file" {
source = "${var.CC_Install_LogPath}/DATA/RestartCC.ps1"
destination = "${var.CC_Install_LogPath}/DATA/RestartCC.ps1"
}
}
###### Execute the PreReqs script on CC1
resource "null_resource" "CallRequiredScriptsOnCC1" {
depends_on = [ null_resource.UploadRequiredComponentsToCC1 ]
connection {
type = var.Provisioner_Type
user = var.Provisioner_Admin-Username
password = var.Provisioner_Admin-Password
host = data.google_compute_address.IPOfCC1.address
timeout = var.Provisioner_Timeout
}
provisioner "remote-exec" {
inline = [
"powershell -File ${var.CC_Install_LogPath}/DATA/InstallCWCOnCC.ps1"
]
}
}
### Upload required components to CC2
#### Set the Provisioner-Connection
resource "null_resource" "UploadRequiredComponentsToCC2" {
depends_on = [ local_file.InstallCWCOnCC ]
connection {
type = var.Provisioner_Type
user = var.Provisioner_Admin-Username
password = var.Provisioner_Admin-Password
host = data.google_compute_address.IPOfCC2.address
timeout = var.Provisioner_Timeout
}
###### Upload Cloud Connector configuration file to CC2
provisioner "file" {
source = "${var.CC_Install_LogPath}/DATA/cwc.json"
destination = "${var.CC_Install_LogPath}/DATA/cwc.json"
}
###### Upload SiteID file to CC2
provisioner "file" {
source = "${var.CC_Install_LogPath}/DATA/GetSiteID.txt"
destination = "${var.CC_Install_LogPath}/DATA/GetSiteID.txt"
}
###### Upload ZoneID file to CC2
provisioner "file" {
source = "${var.CC_Install_LogPath}/DATA/GetZoneID.txt"
destination = "${var.CC_Install_LogPath}/DATA/GetZoneID.txt"
}
###### Upload RLID file to CC2
provisioner "file" {
source = "${var.CC_Install_LogPath}/DATA/GetRLID.txt"
destination = "${var.CC_Install_LogPath}/DATA/GetRLID.txt"
}
###### Upload PreReqs script to CC2
provisioner "file" {
source = "${var.CC_Install_LogPath}/DATA/InstallCWCOnCC.ps1"
destination = "${var.CC_Install_LogPath}/DATA/InstallCWCOnCC.ps1"
}
###### Upload Restart script to CC2
provisioner "file" {
source = "${var.CC_Install_LogPath}/DATA/RestartCC.ps1"
destination = "${var.CC_Install_LogPath}/DATA/RestartCC.ps1"
}
}
###### Execute the PreReqs script on CC2
resource "null_resource" "CallRequiredScriptsOnCC2" {
depends_on = [ null_resource.UploadRequiredComponentsToCC2 ]
connection {
type = var.Provisioner_Type
user = var.Provisioner_Admin-Username
password = var.Provisioner_Admin-Password
host = data.google_compute_address.IPOfCC2.address
timeout = var.Provisioner_Timeout
}
provisioner "remote-exec" {
inline = [
"powershell -File ${var.CC_Install_LogPath}/DATA/InstallCWCOnCC.ps1"
]
}
}
#### Wait 30 mins after CWC creation before restart
resource "time_sleep" "wait_1800_seconds_CC1" {
depends_on = [ null_resource.CallRequiredScriptsOnCC1 ]
create_duration = var.Provisioner_Reboot
}
#### Wait 30 mins after CWC creation before restart
resource "time_sleep" "wait_1800_seconds_CC2" {
depends_on = [ null_resource.CallRequiredScriptsOnCC2 ]
create_duration = var.Provisioner_Reboot
}
###### Execute the Reboot script on CC1
resource "null_resource" "CallRebootScriptOnCC1" {
depends_on = [ time_sleep.wait_1800_seconds_CC1 ]
connection {
type = var.Provisioner_Type
user = var.Provisioner_Admin-Username
password = var.Provisioner_Admin-Password
host = data.google_compute_address.IPOfCC1.address
timeout = var.Provisioner_Timeout
}
provisioner "remote-exec" {
inline = [
"powershell -File ${var.CC_Install_LogPath}/DATA/RebootCC.ps1"
]
}
}
###### Execute the Reboot script on CC2
resource "null_resource" "CallRebootScriptOnCC2" {
depends_on = [ time_sleep.wait_1800_seconds_CC2 ]
connection {
type = var.Provisioner_Type
user = var.Provisioner_Admin-Username
password = var.Provisioner_Admin-Password
host = data.google_compute_address.IPOfCC2.address
timeout = var.Provisioner_Timeout
}
provisioner "remote-exec" {
inline = [
"powershell -File ${var.CC_Install_LogPath}/DATA/RebootCC.ps1"
]
}
}
|
Module 3: CConGCP-CitrixCloudStuff
These are the Terraform configuration files for Module 3 (excerpts):
provider.tf
# Terraform deployment of Citrix DaaS on Google Cloud Platform (GCP)
## Definition of all required Terraform providers
terraform {
required_version = ">= 1.7.5"
required_providers {
restapi = {
source = "Mastercard/restapi"
version = ">=1.18.2"
}
citrix = {
source = "citrix/citrix"
version = ">=0.5.4"
}
google = {
source = "hashicorp/google"
version = ">=5.21.0"
}
}
}
# Configure the Google GCP Provider
provider "google" {
credentials = file(var.CCOnGCP-Creation-Provider-GCPAuthFileJSON)
project = var.CCOnGCP-Creation-Provider-GCPProject
region = var.CCOnGCP-Creation-Provider-GCPRegion
zone = var.CCOnGCP-Creation-Provider-GCPZone
}
# Configure the Citrix Provider
provider "citrix" {
customer_id = "${var.CC_CustomerID}"
client_id = "${var.CC_APIKey-ClientID}"
client_secret = "${var.CC_APIKey-ClientSecret}"
}
|
CreateCCEntities.tf
# Terraform deployment of Citrix DaaS on Google Cloud Platform
## Creating all Citrix Cloud-related entities
### Creating a Hypervisor Connection
#### Retrieving the ZoneID
data "local_file" "LoadZoneID" {
filename = "${var.CC_Install_LogPath}/DATA/GetZoneID.txt"
}
### Retrieving the GCP credentials
data "local_file" "GCPCredentials" {
filename = "${var.CC_Install_LogPath}/DATA/tacg-gcp-406812-a8e71b537c99.json"
}
#### Creating the Hypervisor Connection
resource "citrix_gcp_hypervisor" "CreateHypervisorConnection" {
depends_on = [ data.local_file.LoadZoneID ]
name = "${var.CC_GCP-HypConn-Name}"
zone = data.local_file.LoadZoneID.content
#service_account_id = "${var.CC_GCP-ServiceAccountID}"
#service_account_credentials = data.local_file.GCPCredentials.content
service_account_id = "${var.tv_client_email}"
service_account_credentials = "${var.tv_private_key}"
}
#### Creating the Hypervisor Resource Pool
resource "citrix_gcp_hypervisor_resource_pool" "CreateHypervisorPool" {
depends_on = [ citrix_gcp_hypervisor.CreateHypervisorConnection ]
name = "${var.CC_GCP-HypConnPool-Name}"
hypervisor = citrix_gcp_hypervisor.CreateHypervisorConnection.id
project_name = "${var.CCOnGCP-CCStuff-Provider-GCPProjectName}"
region = "${var.CCOnGCP-CCStuff-Provider-GCPRegion}"
vpc = "${var.CC_GCP-HypConnPool-VPC}"
subnets = [ "${var.CC_GCP-HypConnPool-Subnet}", ]
}
### Creating a Machine Catalog
#### Retrieving the VPC ID
data "google_compute_network" "GCPVPC" {
name = "${var.CC_GCP-HypConnPool-VPC}"
}
#### Retrieving the Subnet Mask based on the Subnet ID
data "google_compute_network" "GCPSubnet" {
name = "${var.CC_GCP-HypConnPool-Subnet}"
}
#### Sleep 60s to let AWS Background processes settle
resource "time_sleep" "wait_60_seconds" {
depends_on = [ citrix_gcp_hypervisor_resource_pool.CreateHypervisorPool ]
create_duration = "60s"
}
#### Create the Machine Catalog
resource "citrix_machine_catalog" "CreateMCSCatalog" {
depends_on = [ time_sleep.wait_60_seconds ]
name = "${var.CC_GCP-MC-Name}"
description = "${var.CC_GCP-MC-Description}"
allocation_type = "${var.CC_GCP-MC-AllocationType}"
session_support = "${var.CC_GCP-MC-SessionType}"
is_power_managed = true
is_remote_pc = false
provisioning_type = "MCS"
zone = data.local_file.LoadZoneID.content
provisioning_scheme = {
hypervisor = citrix_gcp_hypervisor.CreateHypervisorConnection.id
hypervisor_resource_pool = citrix_gcp_hypervisor_resource_pool.CreateHypervisorPool.id
identity_type = "${var.CC_GCP-MC-IDPType}"
machine_domain_identity = {
domain = "${var.CC_GCP-MC-Domain}"
domain_ou = "${var.CC_GCP-MC-DomainOU}"
service_account = "${var.CC_GCP-MC-DomainAdmin-Username-UPN}"
service_account_password = "${var.CC_GCP-MC-DomainAdmin-Password}"
}
gcp_machine_config = {
master_image = "${var.CC_GCP-MC-MasterImage}"
storage_type = "${var.CC_GCP-MC-StorageType}"
#machine_profile = "${var.CC_GCP-MC-MasterImage}"
}
number_of_total_machines = "${var.CC_GCP-MC-Machine_Count}"
machine_account_creation_rules = {
naming_scheme = "${var.CC_GCP-MC-Naming_Scheme_Name}"
naming_scheme_type = "${var.CC_GCP-MC-Naming_Scheme_Type}"
}
}
}
#### Sleep 60s to let Citrix Cloud Background processes settle
resource "time_sleep" "wait_60_seconds_1" {
depends_on = [ citrix_machine_catalog.CreateMCSCatalog ]
create_duration = "60s"
}
#### Create an Example-Policy Set
resource "citrix_policy_set" "SetPolicies" {
count = var.CC_GCP-Policy-IsNotDaaS ? 1 : 0
#depends_on = [ time_sleep.wait_60_seconds_1 ]
name = "${var.CC_GCP-Policy-Name}"
description = "${var.CC_GCP-Policy-Description}"
type = "DeliveryGroupPolicies"
scopes = [ "All" ]
policies = [
{
name = "TACG-GCP-TF-Pol1"
description = "Policy to enable use of Universal Printer"
is_enabled = true
policy_settings = [
{
name = "UniversalPrintDriverUsage"
value = "Use universal printing only"
use_default = false
},
]
policy_filters = [
{
type = "DesktopGroup"
is_enabled = true
is_allowed = true
},
]
},
{
name = "TACG-GCP-TF-Pol2"
description = "Policy to enable Client Drive Redirection"
is_enabled = true
policy_settings = [
{
name = "UniversalPrintDriverUsage"
value = "Prohibited"
use_default = false
},
]
policy_filters = [
{
type = "DesktopGroup"
is_enabled = true
is_allowed = true
},
]
}
]
}
#Sleep 60s to let Citrix Cloud Background processes settle
resource "time_sleep" "Wait_60_Seconds_2" {
depends_on = [ citrix_policy_set.SetPolicies ]
create_duration = "60s"
}
#### Create the Delivery Group based on the Machine Catalog
resource "citrix_delivery_group" "CreateDG" {
depends_on = [ time_sleep.wait_60_seconds_1]
name = "${var.CC_GCP-DG-Name}"
associated_machine_catalogs = [
{
machine_catalog = citrix_machine_catalog.CreateMCSCatalog.id
machine_count = "${var.CC_GCP-MC-Machine_Count}"
}
]
desktops = [
{
published_name = "${var.CC_GCP-DG-PublishedDesktopName}"
description = "${var.CC_GCP-DG-Description}"
restricted_access_users = {
allow_list = [ "TACG-GCP\\vdaallowed" ]
}
enabled = true
enable_session_roaming = var.CC_GCP-DG-SessionRoaming
}
]
autoscale_settings = {
autoscale_enabled = true
disconnect_peak_idle_session_after_seconds = 300
log_off_peak_disconnected_session_after_seconds = 300
peak_log_off_action = "Nothing"
power_time_schemes = [
{
days_of_week = [
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday"
]
name = "${var.CC_GCP-DG-AS-Name}"
display_name = "${var.CC_GCP-DG-AS-Name}"
peak_time_ranges = [
"09:00-17:00"
]
pool_size_schedules = [
{
time_range = "09:00-17:00",
pool_size = 1
}
]
pool_using_percentage = false
},
]
}
restricted_access_users = {
allow_list = [ "TACG-GCP\\vdaallowed" ]
}
reboot_schedules = [
{
name = "TACG-GCP-Reboot Schedule"
reboot_schedule_enabled = true
frequency = "Weekly"
frequency_factor = 1
days_in_week = [
"Sunday",
]
start_time = "02:00"
start_date = "2024-01-01"
reboot_duration_minutes = 0
ignore_maintenance_mode = true
natural_reboot_schedule = false
}
]
policy_set_id = citrix_policy_set.SetPolicies.id
}
|
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