Jump to content
Updated Privacy Statement

Gerhard Krenn

Tech Zone Admins
  • Posts

    2
  • Joined

  • Last visited

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

Gerhard Krenn's Achievements

Newbie

Newbie (1/14)

  • Week One Done
  • One Month Later
  • One Year In

Recent Badges

13

Reputation

  1. 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 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: .tg {border-collapse:collapse;border-spacing:0;width:100%} .tg .tg-5jvq{background-color:#0000b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px; text-align:left;vertical-align:top} PS C:\TACG> 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)) Forcing web requests to allow TLS v1.2 (Required for requests to Chocolatey.org) Getting latest version of the Chocolatey package for download. Not using proxy. Getting Chocolatey from https://community.chocolatey.org/api/v2/package/chocolatey/2.2.2. Downloading https://community.chocolatey.org/api/v2/package/chocolatey/2.2.2 to C:\TACG\AppData\Local\Temp\chocolatey\chocoInstall\chocolatey.zip Not using proxy. Extracting C:\TACG\AppData\Local\Temp\chocolatey\chocoInstall\chocolatey.zip to C:\TACG\AppData\Local\Temp\chocolatey\chocoInstall Installing Chocolatey on the local machine Creating ChocolateyInstall as an environment variable (targeting 'Machine') Setting ChocolateyInstall to 'C:\ProgramData\chocolatey' WARNING: It's very likely you will need to close and reopen your shell before you can use choco. Restricting write permissions to Administrators We are setting up the Chocolatey package repository. The packages themselves go to 'C:\ProgramData\chocolatey\lib' (i.e. C:\ProgramData\chocolatey\lib\yourPackageName). A shim file for the command line goes to 'C:\ProgramData\chocolatey\bin' and points to an executable in 'C:\ProgramData\chocolatey\lib\yourPackageName'. Creating Chocolatey folders if they do not already exist. chocolatey.nupkg file not installed in lib. Attempting to locate it from bootstrapper. PATH environment variable does not have C:\ProgramData\chocolatey\bin in it. Adding... WARNING: Not setting tab completion: Profile file does not exist at 'C:\TACG\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1'. Chocolatey (choco.exe) is now ready. You can call choco from anywhere, command line or powershell by typing choco. Run choco /? for a list of functions. You may need to shut down and restart powershell and/or consoles first prior to using choco. Ensuring Chocolatey commands are on the path Ensuring chocolatey.nupkg is in the lib folder PS C:\TACG> Run choco --v to check if Chocolatey was installed successfully: .tg {border-collapse:collapse;border-spacing:0;width:100%} .tg .tg-5jvq{background-color:#0000b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px; text-align:left;vertical-align:top} PS C:\TACG> choco --v Chocolatey v2.2.2 PS C:\TACG> Installation of Terraform After the successful installation of Chocolatey, you can install Terraform by running this command on the PowerShell session: choco install terraform .tg {border-collapse:collapse;border-spacing:0;width:100%} .tg .tg-5jvq{background-color:#0000b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px; text-align:left;vertical-align:top} PS C:\TACG> choco install terraform Chocolatey v2.2.2 Installing the following packages: terraform By installing, you accept licenses for the packages. Progress: Downloading terraform 1.6.4... 100% terraform v1.7.4 [Approved] terraform package files install completed. Performing other installation steps. The package terraform wants to run 'chocolateyInstall.ps1'. Note: If you don't run this script, the installation will fail. Note: To confirm automatically next time, use '-y' or consider: choco feature enable -n allowGlobalConfirmation Do you want to run the script?([Y]es/[A]ll - yes to all/[N]o/[P]rint): A Removing old terraform plug-ins Downloading terraform 64 bit from 'https://releases.hashicorp.com/terraform/1.7.4/terraform_1.7.4_windows_amd64.zip' Progress: 100% - Completed download of C:\TACG\AppData\Local\Temp\chocolatey\terraform\1.7.4\terraform_1.7.4_windows_amd64.zip (25.05 MB). Download of terraform_1.7.4_windows_amd64.zip (25.05 MB) completed. Hashes match. Extracting C:\TACG\AppData\Local\Temp\chocolatey\terraform\1.7.4\terraform_1.7.4_windows_amd64.zip to C:\ProgramData\chocolatey\lib\terraform\tools... C:\ProgramData\chocolatey\lib\terraform\tools ShimGen has successfully created a shim for terraform.exe The install of terraform was successful. Software installed to 'C:\ProgramData\chocolatey\lib\terraform\tools' Chocolatey installed 1/1 packages. See the log for details (C:\ProgramData\chocolatey\logs\chocolatey.log). PS C:\TACG> Run terraform -version to check if Terraform was installed successfully: .tg {border-collapse:collapse;border-spacing:0;width:100%} .tg .tg-5jvq{background-color:#0000b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px; text-align:left;vertical-align:top} PS C:\TACG> terraform -version Terraform v1.7.4 on windows_amd64 PS C:\TACG> 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. 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 and Citrix 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. 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 .tg {border-collapse:collapse;border-spacing:0;width:100%} .tg .tg-5jvq{background-color:#0000b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px; text-align:left;vertical-align:top} PS C:\TACG> terraform init Initializing the backend... Successfully configured the backend "local"! Terraform will automatically use this backend unless the backend configuration changes. Initializing provider plug-ins... - Finding citrix/citrix versions matching ">= 0.5.4"... - Installing citrix/citrix v0.5.4... - Installed citrix/citrix v0.5.4 (signed by a HashiCorp partner, key ID 25D62DD8407EA386) Partner and community providers are signed by their developers. If you'd like to know more about provider signing, you can read about it here: https://www.terraform.io/docs/cli/plug-ins/signing.html Terraform has created a lock file .terraform.lock.hcl to record the provider selections it made above. Include this file in your version control repository so that Terraform can guarantee to make the same selections by default when you run "terraform init" in the future. Terraform has been successfully initialized! You may now begin working with Terraform. Try running "terraform plan" to see any changes that are required for your infrastructure. All Terraform commands should now work. If you ever set or change modules or backend configuration for Terraform, rerun this command to reinitialize your working directory. If you forget, other commands will detect it and remind you to do so if necessary. PS C:\TACG> 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: .table_component { overflow: auto; width: 100%; } .table_component table { border: 1px solid #323232; border-radius: 3px; height: 100%; width: 90%; table-layout: fixed; border-collapse: collapse; border-spacing: 1px; text-align: left; } .table_component caption { caption-side: top; text-align: left; } .table_component th { border: 1px solid #323232; background-color: #323232; color: #ffffff; padding: 10px; } .table_component td { border: 1px solid #323232; background-color: #323232; color: #ffffff; padding: 10px; font-family: Consolas,Courier New; font-size:12px; } 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. .table_component { overflow: auto; width: 100%; } .table_component table { border: 1px solid #323232; border-radius: 3px; height: 100%; width: 90%; table-layout: fixed; border-collapse: collapse; border-spacing: 1px; text-align: left; } .table_component caption { caption-side: top; text-align: left; } .table_component th { border: 1px solid #323232; background-color: #323232; color: #ffffff; padding: 10px; } .table_component td { border: 1px solid #323232; background-color: #323232; color: #ffffff; padding: 10px; font-family: Consolas,Courier New; font-size:12px; } 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 the Domain-Admin Username. For Citrix Cloud customers: Use this variable to specify Cloud 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 the Domain-Admin Password. For Citrix Cloud customers: Use this variable to specify Cloud 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 to true to skip SSL verification only when the target DDC does not have a valid SSL certificate issued by a trusted CA. When set to true, please make sure that your provider config is set for a known DDC hostname. 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 Controller hostname. For Citrix Cloud customers (Optional): Use this variable to override the Citrix DaaS service hostname. Can be set via the Environment Variable CITRIX_HOSTNAME. 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. 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 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 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: .tg {border-collapse:collapse;border-spacing:0;width:100%} .tg .tg-5jvq{background-color:#0000b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px; text-align:left;vertical-align:top} test-wsman -ComputerName <IP-Address of the computer you want to reach> -Credential <IP-Address of the computer you want to reach>\administrator -Authentication Basic The response should look like: wsmid : http://schemas.dmtf.org/wbem/wsman/identity/1/wsmanidentity.xsd ProtocolVersion : http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd ProductVendor : Microsoft Corporation ProductVersion : OS: 0.0.0 SP: 0.0 Stack: 3.0 Another possibility is to open a PowerShell console and type: Enter-PSSession -ComputerName <IP-Address of the computer you want to reach> -Credential <IP-Address of the computer you want to reach>\administrator The response should look like: [10.156.0.5]: PS C:\Users\Administrator\Documents> 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: .table_component { overflow: auto; width: 100%; } .table_component table { border: 1px solid #323232; border-radius: 3px; height: 100%; width: 90%; table-layout: fixed; border-collapse: collapse; border-spacing: 1px; text-align: left; } .table_component caption { caption-side: top; text-align: left; } .table_component th { border: 1px solid #323232; background-color: #323232; color: #ffffff; padding: 10px; } .table_component td { border: 1px solid #323232; background-color: #323232; color: #ffffff; padding: 10px; font-family: Consolas,Courier New; font-size:12px; } 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... .tg {border-collapse:collapse;border-spacing:0;width:100%} .tg .tg-5jvq{background-color:#0000b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px; text-align:left;vertical-align:top} null_resource.CreateTestScriptOnCC1: Creating... null_resource.CreateTestScriptOnCC1: Provisioning with 'remote-exec'... null_resource.CreateTestScriptOnCC1 (remote-exec): Connecting to remote host via WinRM... null_resource.CreateTestScriptOnCC1 (remote-exec): Host: 10.156.0.5 null_resource.CreateTestScriptOnCC1 (remote-exec): Port: 5985 null_resource.CreateTestScriptOnCC1 (remote-exec): User: administrator null_resource.CreateTestScriptOnCC1 (remote-exec): Password: true null_resource.CreateTestScriptOnCC1 (remote-exec): HTTPS: false null_resource.CreateTestScriptOnCC1 (remote-exec): Insecure: false null_resource.CreateTestScriptOnCC1 (remote-exec): NTLM: false null_resource.CreateTestScriptOnCC1 (remote-exec): CACert: false null_resource.CreateTestScriptOnCC1 (remote-exec): Connected! #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>#< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs> null_resource.CreateTestScriptOnCC1 (remote-exec): C:\Users\Administrator>powershell -File c:/temp/xdinst/DATA/Terraform-Test-WinRM.ps1 #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj><Obj S="progress" RefId="1"><TNRef RefId="0" /><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>#< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>null_resource.CreateTestScriptOnCC1: Creation complete after 3s [id=394829982371734209] ...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. 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 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: .tg {border-collapse:collapse;border-spacing:0;width:100%} .tg .tg-5jvq{background-color:#0000b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px; text-align:left;vertical-align:top} PS C:\TACG> gcloud init Welcome! This command will take you through the configuration of gcloud. Settings from your current configuration [default] are: accessibility: screen_reader: 'False' compute: region: us-east1 zone: us-east1-b core: account: XXXXXXXXXX@the-austrian-citrix-guy.at disable_usage_reporting: 'True' project: tacg-gcp-XXXXXXXXXXXX Pick configuration to use: [1] Re-initialize this configuration [default] with new settings [2] Create a new configuration [3] Switch to and re-initialize existing configuration: [tacg-gcp-n] Please enter your numeric choice: 1 Your current configuration has been set to: [default] You can skip diagnostics next time by using the following flag: gcloud init --skip-diagnostics Network diagnostic detects and fixes local network connection issues. Checking network connection...done. Reachability Check passed. Network diagnostic passed (1/1 checks passed). Choose the account you would like to use to perform operations for this configuration: [1] XXXXXXXXXX@XXXXXXXXXX [2] XXXXXXXXXX@the-austrian-citrix-guy.at [3] Log in with a new account Please enter your numeric choice: 2 You are logged in as: [XXXXXXXXXX@the-austrian-citrix-guy.at]. Reauthentication required. Please enter your password: Pick cloud project to use: [1] tacg-gcp-XXXXXXXXXXXX [2] Enter a project ID [3] Create a new project Please enter numeric choice or text value (must exactly match list item): 1 Your current project has been set to: [tacg-gcp-XXXXXXXXXXXX]. Do you want to configure a default Compute Region and Zone? (Y/n)? Y Which Google Compute Engine zone would you like to use as project default? If you do not specify a zone via a command line flag while working with Compute Engine resources, the default is assumed. [1] us-east1-b [2] us-east1-c [3] us-east1-d [4] us-east4-c [5] us-east4-b [6] us-east4-a [7] us-central1-c [8] us-central1-a [9] us-central1-f [10] us-central1-b [11] us-west1-b [12] us-west1-c [13] us-west1-a [14] europe-west4-a [15] europe-west4-b [16] europe-west4-c [17] europe-west1-b [18] europe-west1-d [19] europe-west1-c [20] europe-west3-c [21] europe-west3-a [22] europe-west3-b [23] europe-west2-c [24] europe-west2-b [25] europe-west2-a [26] asia-east1-b [27] asia-east1-a [28] asia-east1-c [29] asia-southeast1-b [30] asia-southeast1-a [31] asia-southeast1-c [32] asia-northeast1-b [33] asia-northeast1-c [34] asia-northeast1-a [35] asia-south1-c [36] asia-south1-b [37] asia-south1-a [38] australia-southeast1-b [39] australia-southeast1-c [40] australia-southeast1-a [41] southamerica-east1-b [42] southamerica-east1-c [43] southamerica-east1-a [44] africa-south1-a [45] africa-south1-b [46] africa-south1-c [47] asia-east2-a [48] asia-east2-b [49] asia-east2-c [50] asia-northeast2-a Did not print [72] options. Too many options [122]. Enter "list" at prompt to print choices fully. Please enter numeric choice or text value (must exactly match list item): 20 Your project default Compute Engine zone has been set to [europe-west3-c]. You can change it by running [gcloud config set compute/zone NAME]. Your project default Compute Engine region has been set to [europe-west3]. You can change it by running [gcloud config set compute/region NAME]. Your Google Cloud SDK is configured and ready to use! * Commands that require authentication will use XXXXXXXXXX@the-austrian-citrix-guy.at by default * Commands will reference project `tacg-gcp-XXXXXXXXXX` by default * Compute Engine commands will use region `europe-west3` by default * Compute Engine commands will use zone `europe-west3-c` by default Run `gcloud help config` to learn how to change individual settings This gcloud configuration is called [default]. You can create additional configurations if you work with multiple accounts and/or projects. Run `gcloud topic configurations` to learn more. Some things to try next: * Run `gcloud --help` to see the Cloud Platform services you can interact with. And run `gcloud help COMMAND` to get help on any gcloud command. * Run `gcloud topic --help` to learn about advanced features of the SDK like arg files and output formatting * Run `gcloud cheat-sheet` to see a roster of go-to `gcloud` commands. PS C:\TACG> 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: .tg {border-collapse:collapse;border-spacing:0;width:100%} .tg .tg-5jvq{background-color:#0000b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px; text-align:left;vertical-align:top} gcloud services enable compute.googleapis.com gcloud services enable cloudresourcemanager.googleapis.com gcloud services enable iam.googleapis.com gcloud services enable cloudbuild.googleapis.com gcloud services enable dns.googleapis.com 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: .tg {border-collapse:collapse;border-spacing:0;width:100%} .tg .tg-5jvq{background-color:#0000b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px; text-align:left;vertical-align:top} PS C:\TACG> gcloud compute networks list NAME SUBNET_MODE BGP_ROUTING_MODE IPV4_RANGE GATEWAY_IPV4 default AUTO REGIONAL PS C:\TACG> PS C:\TACG> gcloud compute networks subnets list --regions europe-west3 NAME REGION NETWORK RANGE STACK_TYPE IPV6_ACCESS_TYPE INTERNAL_IPV6_PREFIX EXTERNAL_IPV6_PREFIX default europe-west3 default 10.156.0.0/20 IPV4_ONLY PS C:\TACG> PS C:\TACG> gcloud compute networks list --uri https://www.googleapis.com/compute/v1/projects/tacg-gcp-XXXXXXXXXX/global/networks/default PS C:\TACG> PS C:\TACG> gcloud compute networks subnets list --uri --regions europe-west3 https://www.googleapis.com/compute/v1/projects/tacg-gcp-XXXXXXXXXX/regions/europe-west3/subnetworks/default PS C:\TACG> 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. Using gcloud to enable GPA and check if GPA is enabled: .tg {border-collapse:collapse;border-spacing:0;width:100%} .tg .tg-5jvq{background-color:#0000b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px; text-align:left;vertical-align:top} PS C:\TACG> gcloud compute networks subnets list --filter=default NAME REGION NETWORK RANGE STACK_TYPE IPV6_ACCESS_TYPE INTERNAL_IPV6_PREFIX EXTERNAL_IPV6_PREFIX default us-central1 default 10.128.0.0/20 IPV4_ONLY default europe-west1 default 10.132.0.0/20 IPV4_ONLY default us-west1 default 10.138.0.0/20 IPV4_ONLY default asia-east1 default 10.140.0.0/20 IPV4_ONLY default us-east1 default 10.142.0.0/20 IPV4_ONLY default asia-northeast1 default 10.146.0.0/20 IPV4_ONLY default asia-southeast1 default 10.148.0.0/20 IPV4_ONLY default us-east4 default 10.150.0.0/20 IPV4_ONLY default australia-southeast1 default 10.152.0.0/20 IPV4_ONLY default europe-west2 default 10.154.0.0/20 IPV4_ONLY default europe-west3 default 10.156.0.0/20 IPV4_ONLY default southamerica-east1 default 10.158.0.0/20 IPV4_ONLY default asia-south1 default 10.160.0.0/20 IPV4_ONLY default northamerica-northeast1 default 10.162.0.0/20 IPV4_ONLY default europe-west4 default 10.164.0.0/20 IPV4_ONLY default europe-north1 default 10.166.0.0/20 IPV4_ONLY default us-west2 default 10.168.0.0/20 IPV4_ONLY default asia-east2 default 10.170.0.0/20 IPV4_ONLY default europe-west6 default 10.172.0.0/20 IPV4_ONLY default asia-northeast2 default 10.174.0.0/20 IPV4_ONLY default asia-northeast3 default 10.178.0.0/20 IPV4_ONLY default us-west3 default 10.180.0.0/20 IPV4_ONLY default us-west4 default 10.182.0.0/20 IPV4_ONLY default asia-southeast2 default 10.184.0.0/20 IPV4_ONLY default europe-central2 default 10.186.0.0/20 IPV4_ONLY default northamerica-northeast2 default 10.188.0.0/20 IPV4_ONLY default asia-south2 default 10.190.0.0/20 IPV4_ONLY default australia-southeast2 default 10.192.0.0/20 IPV4_ONLY default southamerica-west1 default 10.194.0.0/20 IPV4_ONLY default us-east7 default 10.196.0.0/20 IPV4_ONLY default europe-west8 default 10.198.0.0/20 IPV4_ONLY default europe-west9 default 10.200.0.0/20 IPV4_ONLY default us-east5 default 10.202.0.0/20 IPV4_ONLY default europe-southwest1 default 10.204.0.0/20 IPV4_ONLY default us-south1 default 10.206.0.0/20 IPV4_ONLY default me-west1 default 10.208.0.0/20 IPV4_ONLY default europe-west12 default 10.210.0.0/20 IPV4_ONLY default me-central1 default 10.212.0.0/20 IPV4_ONLY default europe-west10 default 10.214.0.0/20 IPV4_ONLY default me-central2 default 10.216.0.0/20 IPV4_ONLY default africa-south1 default 10.218.0.0/20 IPV4_ONLY default us-west8 default 10.220.0.0/20 IPV4_ONLY PS C:\TACG> PS C:\TACG> gcloud compute networks subnets update default --region=europe-west3 --enable-private-ip-google-access Updated [https://www.googleapis.com/compute/v1/projects/tacg-gcp-XXXXXXXXXX/regions/europe-west3/subnetworks/default]. PS C:\TACG> PS C:\TACG> gcloud compute networks subnets describe default --region=europe-west3 --format="get(privateIpGoogleAccess)" True Using gcloud to determine all used internal IPs: .tg {border-collapse:collapse;border-spacing:0;width:100%} .tg .tg-5jvq{background-color:#0000b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px; text-align:left;vertical-align:top} PS C:\TACG> gcloud asset search-all-resources --asset-types='compute.googleapis.com/Instance' --query="networks/default" --format=json | jq ".[].additionalAttributes.internalIPs" [ "10.156.0.2" ] PS C:\TACG> 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: .tg {border-collapse:collapse;border-spacing:0;width:100%} .tg .tg-5jvq{background-color:#0000b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px; text-align:left;vertical-align:top} PS C:\TACG> gcloud compute project-info describe --project tacg-gcp-XXXXXXXXXX commonInstanceMetadata: fingerprint: mhAFevpXe_g= items: - key: serial-port-enable value: 'TRUE' kind: compute#metadata creationTimestamp: '2023-12-01T04:09:39.536-08:00' defaultNetworkTier: PREMIUM defaultServiceAccount: XXXXXXXXXXXX-compute@developer.gserviceaccount.com id: '826031XXXXXXXXXXXX' kind: compute#project name: tacg-gcp-XXXXXXXXXX quotas: - limit: 1000.0 metric: SNAPSHOTS usage: 0.0 - limit: 5.0 metric: NETWORKS usage: 1.0 - limit: 100.0 metric: FIREWALLS usage: 8.0 - limit: 100.0 metric: IMAGES usage: 0.0 - limit: 8.0 metric: STATIC_ADDRESSES usage: 0.0 - limit: 200.0 metric: ROUTES usage: 0.0 - limit: 15.0 metric: FORWARDING_RULES usage: 0.0 - limit: 50.0 metric: TARGET_POOLS usage: 0.0 - limit: 75.0 metric: HEALTH_CHECKS usage: 0.0 - limit: 8.0 metric: IN_USE_ADDRESSES usage: 0.0 - limit: 50.0 metric: TARGET_INSTANCES usage: 0.0 - limit: 10.0 metric: TARGET_HTTP_PROXIES usage: 0.0 - limit: 10.0 metric: URL_MAPS usage: 0.0 - limit: 50.0 metric: BACKEND_SERVICES usage: 0.0 - limit: 100.0 metric: INSTANCE_TEMPLATES usage: 2.0 - limit: 5.0 metric: TARGET_VPN_GATEWAYS usage: 0.0 - limit: 10.0 metric: VPN_TUNNELS usage: 0.0 - limit: 3.0 metric: BACKEND_BUCKETS usage: 0.0 - limit: 10.0 metric: ROUTERS usage: 0.0 - limit: 10.0 metric: TARGET_SSL_PROXIES usage: 0.0 - limit: 10.0 metric: TARGET_HTTPS_PROXIES usage: 0.0 - limit: 10.0 metric: SSL_CERTIFICATES usage: 0.0 - limit: 100.0 metric: SUBNETWORKS usage: 0.0 - limit: 10.0 metric: TARGET_TCP_PROXIES usage: 0.0 - limit: 32.0 metric: CPUS_ALL_REGIONS usage: 0.0 - limit: 10.0 metric: SECURITY_POLICIES usage: 0.0 - limit: 100.0 metric: SECURITY_POLICY_RULES usage: 0.0 - limit: 1000.0 metric: XPN_SERVICE_PROJECTS usage: 0.0 - limit: 20.0 metric: PACKET_MIRRORINGS usage: 0.0 - limit: 100.0 metric: NETWORK_ENDPOINT_GROUPS usage: 0.0 - limit: 6.0 metric: INTERCONNECTS usage: 0.0 - limit: 5000.0 metric: GLOBAL_INTERNAL_ADDRESSES usage: 0.0 - limit: 5.0 metric: VPN_GATEWAYS usage: 0.0 - limit: 100.0 metric: MACHINE_IMAGES usage: 0.0 - limit: 20.0 metric: SECURITY_POLICY_CEVAL_RULES usage: 0.0 - limit: 0.0 metric: GPUS_ALL_REGIONS usage: 0.0 - limit: 5.0 metric: EXTERNAL_VPN_GATEWAYS usage: 0.0 - limit: 1.0 metric: PUBLIC_ADVERTISED_PREFIXES usage: 0.0 - limit: 10.0 metric: PUBLIC_DELEGATED_PREFIXES usage: 0.0 - limit: 128.0 metric: STATIC_BYOIP_ADDRESSES usage: 0.0 - limit: 10.0 metric: NETWORK_FIREWALL_POLICIES usage: 0.0 - limit: 15.0 metric: INTERNAL_TRAFFIC_DIRECTOR_FORWARDING_RULES usage: 0.0 - limit: 15.0 metric: GLOBAL_EXTERNAL_MANAGED_FORWARDING_RULES usage: 0.0 - limit: 50.0 metric: GLOBAL_INTERNAL_MANAGED_BACKEND_SERVICES usage: 0.0 - limit: 50.0 metric: GLOBAL_EXTERNAL_MANAGED_BACKEND_SERVICES usage: 0.0 - limit: 50.0 metric: GLOBAL_EXTERNAL_PROXY_LB_BACKEND_SERVICES usage: 0.0 - limit: 250.0 metric: GLOBAL_INTERNAL_TRAFFIC_DIRECTOR_BACKEND_SERVICES usage: 0.0 selfLink: https://www.googleapis.com/compute/v1/projects/tacg-gcp-XXXXXXXXXX vmDnsSetting: ZONAL_ONLY xpnProjectStatus: UNSPECIFIED_XPN_PROJECT_STATUS PS C:\TACG> You can filter the Quotas or change them using the corresponding Quota pages on the Google Cloud console: Google Cloud Console - Quotas 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: .tg {border-collapse:collapse;border-spacing:0;width:100%} .tg .tg-5jvq{background-color:#0000b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px; text-align:left;vertical-align:top} PS C:\TACG> gcloud compute images list --filter 'family ~ windows' NAME PROJECT FAMILY DEPRECATED STATUS windows-server-2016-dc-core-v20240313 windows-cloud windows-2016-core READY windows-server-2016-dc-v20240313 windows-cloud windows-2016 READY windows-server-2019-dc-core-v20240313 windows-cloud windows-2019-core READY windows-server-2019-dc-v20240313 windows-cloud windows-2019 READY windows-server-2022-dc-core-v20240313 windows-cloud windows-2022-core READY windows-server-2022-dc-v20240313 windows-cloud windows-2022 READY PS C:\TACG> 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): .tg {border-collapse:collapse;border-spacing:0;width:100%} .tg .tg-5jvq{background-color:#0000b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px; text-align:left;vertical-align:top} PS C:\TACG> gcloud compute machine-types list --filter="zone:( europe-west3-c )" NAME ZONE CPUS MEMORY_GB DEPRECATED c2-standard-16 europe-west3-c 16 64.00 c2-standard-30 europe-west3-c 30 120.00 c2-standard-4 europe-west3-c 4 16.00 c2-standard-60 europe-west3-c 60 240.00 c2-standard-8 europe-west3-c 8 32.00 c2d-highcpu-112 europe-west3-c 112 224.00 c2d-highcpu-16 europe-west3-c 16 32.00 c2d-highcpu-2 europe-west3-c 2 4.00 c2d-highcpu-32 europe-west3-c 32 64.00 c2d-highcpu-4 europe-west3-c 4 8.00 c2d-highcpu-56 europe-west3-c 56 112.00 c2d-highcpu-8 europe-west3-c 8 16.00 c2d-highmem-112 europe-west3-c 112 896.00 c2d-highmem-16 europe-west3-c 16 128.00 c2d-highmem-2 europe-west3-c 2 16.00 c2d-highmem-32 europe-west3-c 32 256.00 c2d-highmem-4 europe-west3-c 4 32.00 c2d-highmem-56 europe-west3-c 56 448.00 c2d-highmem-8 europe-west3-c 8 64.00 c2d-standard-112 europe-west3-c 112 448.00 c2d-standard-16 europe-west3-c 16 64.00 c2d-standard-2 europe-west3-c 2 8.00 c2d-standard-32 europe-west3-c 32 128.00 c2d-standard-4 europe-west3-c 4 16.00 c2d-standard-56 europe-west3-c 56 224.00 c2d-standard-8 europe-west3-c 8 32.00 e2-highcpu-16 europe-west3-c 16 16.00 e2-highcpu-2 europe-west3-c 2 2.00 e2-highcpu-32 europe-west3-c 32 32.00 e2-highcpu-4 europe-west3-c 4 4.00 e2-highcpu-8 europe-west3-c 8 8.00 e2-highmem-16 europe-west3-c 16 128.00 e2-highmem-2 europe-west3-c 2 16.00 e2-highmem-4 europe-west3-c 4 32.00 e2-highmem-8 europe-west3-c 8 64.00 e2-medium europe-west3-c 2 4.00 e2-micro europe-west3-c 2 1.00 e2-small europe-west3-c 2 2.00 e2-standard-16 europe-west3-c 16 64.00 e2-standard-2 europe-west3-c 2 8.00 e2-standard-32 europe-west3-c 32 128.00 e2-standard-4 europe-west3-c 4 16.00 e2-standard-8 europe-west3-c 8 32.00 f1-micro europe-west3-c 1 0.60 g1-small europe-west3-c 1 1.70 m1-megamem-96 europe-west3-c 96 1433.60 m1-ultramem-160 europe-west3-c 160 3844.00 m1-ultramem-40 europe-west3-c 40 961.00 m1-ultramem-80 europe-west3-c 80 1922.00 n1-highcpu-16 europe-west3-c 16 14.40 n1-highcpu-2 europe-west3-c 2 1.80 n1-highcpu-32 europe-west3-c 32 28.80 n1-highcpu-4 europe-west3-c 4 3.60 n1-highcpu-64 europe-west3-c 64 57.60 n1-highcpu-8 europe-west3-c 8 7.20 n1-highcpu-96 europe-west3-c 96 86.40 n1-highmem-16 europe-west3-c 16 104.00 n1-highmem-2 europe-west3-c 2 13.00 n1-highmem-32 europe-west3-c 32 208.00 n1-highmem-4 europe-west3-c 4 26.00 n1-highmem-64 europe-west3-c 64 416.00 n1-highmem-8 europe-west3-c 8 52.00 n1-highmem-96 europe-west3-c 96 624.00 n1-megamem-96 europe-west3-c 96 1433.60 DEPRECATED n1-standard-1 europe-west3-c 1 3.75 n1-standard-16 europe-west3-c 16 60.00 n1-standard-2 europe-west3-c 2 7.50 n1-standard-32 europe-west3-c 32 120.00 n1-standard-4 europe-west3-c 4 15.00 n1-standard-64 europe-west3-c 64 240.00 n1-standard-8 europe-west3-c 8 30.00 n1-standard-96 europe-west3-c 96 360.00 n1-ultramem-160 europe-west3-c 160 3844.00 DEPRECATED n1-ultramem-40 europe-west3-c 40 961.00 DEPRECATED n1-ultramem-80 europe-west3-c 80 1922.00 DEPRECATED n2-highcpu-16 europe-west3-c 16 16.00 n2-highcpu-2 europe-west3-c 2 2.00 n2-highcpu-32 europe-west3-c 32 32.00 n2-highcpu-4 europe-west3-c 4 4.00 n2-highcpu-48 europe-west3-c 48 48.00 n2-highcpu-64 europe-west3-c 64 64.00 n2-highcpu-8 europe-west3-c 8 8.00 n2-highcpu-80 europe-west3-c 80 80.00 n2-highcpu-96 europe-west3-c 96 96.00 n2-highmem-128 europe-west3-c 128 864.00 n2-highmem-16 europe-west3-c 16 128.00 n2-highmem-2 europe-west3-c 2 16.00 n2-highmem-32 europe-west3-c 32 256.00 n2-highmem-4 europe-west3-c 4 32.00 n2-highmem-48 europe-west3-c 48 384.00 n2-highmem-64 europe-west3-c 64 512.00 n2-highmem-8 europe-west3-c 8 64.00 n2-highmem-80 europe-west3-c 80 640.00 n2-highmem-96 europe-west3-c 96 768.00 n2-standard-128 europe-west3-c 128 512.00 n2-standard-16 europe-west3-c 16 64.00 n2-standard-2 europe-west3-c 2 8.00 n2-standard-32 europe-west3-c 32 128.00 n2-standard-4 europe-west3-c 4 16.00 n2-standard-48 europe-west3-c 48 192.00 n2-standard-64 europe-west3-c 64 256.00 n2-standard-8 europe-west3-c 8 32.00 n2-standard-80 europe-west3-c 80 320.00 n2-standard-96 europe-west3-c 96 384.00 n2d-highcpu-128 europe-west3-c 128 128.00 n2d-highcpu-16 europe-west3-c 16 16.00 n2d-highcpu-2 europe-west3-c 2 2.00 n2d-highcpu-224 europe-west3-c 224 224.00 n2d-highcpu-32 europe-west3-c 32 32.00 n2d-highcpu-4 europe-west3-c 4 4.00 n2d-highcpu-48 europe-west3-c 48 48.00 n2d-highcpu-64 europe-west3-c 64 64.00 n2d-highcpu-8 europe-west3-c 8 8.00 n2d-highcpu-80 europe-west3-c 80 80.00 n2d-highcpu-96 europe-west3-c 96 96.00 n2d-highmem-16 europe-west3-c 16 128.00 n2d-highmem-2 europe-west3-c 2 16.00 n2d-highmem-32 europe-west3-c 32 256.00 n2d-highmem-4 europe-west3-c 4 32.00 n2d-highmem-48 europe-west3-c 48 384.00 n2d-highmem-64 europe-west3-c 64 512.00 n2d-highmem-8 europe-west3-c 8 64.00 n2d-highmem-80 europe-west3-c 80 640.00 n2d-highmem-96 europe-west3-c 96 768.00 n2d-standard-128 europe-west3-c 128 512.00 n2d-standard-16 europe-west3-c 16 64.00 n2d-standard-2 europe-west3-c 2 8.00 n2d-standard-224 europe-west3-c 224 896.00 n2d-standard-32 europe-west3-c 32 128.00 n2d-standard-4 europe-west3-c 4 16.00 n2d-standard-48 europe-west3-c 48 192.00 n2d-standard-64 europe-west3-c 64 256.00 n2d-standard-8 europe-west3-c 8 32.00 n2d-standard-80 europe-west3-c 80 320.00 n2d-standard-96 europe-west3-c 96 384.00 t2d-standard-1 europe-west3-c 1 4.00 t2d-standard-16 europe-west3-c 16 64.00 t2d-standard-2 europe-west3-c 2 8.00 t2d-standard-32 europe-west3-c 32 128.00 t2d-standard-4 europe-west3-c 4 16.00 t2d-standard-48 europe-west3-c 48 192.00 t2d-standard-60 europe-west3-c 60 240.00 t2d-standard-8 europe-west3-c 8 32.00 PS C:\TACG> 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: .tg {border-collapse:collapse;border-spacing:0;width:100%} .tg .tg-5jvq{background-color:#0000b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px; text-align:left;vertical-align:top} Filtered for CCs and Admin-VM: PS C:\TACG> gcloud compute machine-types list --filter="(CPUS<=2) AND (zone:europe-west3-c)" NAME ZONE CPUS MEMORY_GB DEPRECATED c2d-highcpu-2 europe-west3-c 2 4.00 c2d-highmem-2 europe-west3-c 2 16.00 c2d-standard-2 europe-west3-c 2 8.00 e2-highcpu-2 europe-west3-c 2 2.00 e2-highmem-2 europe-west3-c 2 16.00 e2-medium europe-west3-c 2 4.00 e2-micro europe-west3-c 2 1.00 e2-small europe-west3-c 2 2.00 e2-standard-2 europe-west3-c 2 8.00 f1-micro europe-west3-c 1 0.60 g1-small europe-west3-c 1 1.70 n1-highcpu-2 europe-west3-c 2 1.80 n1-highmem-2 europe-west3-c 2 13.00 n1-standard-1 europe-west3-c 1 3.75 n1-standard-2 europe-west3-c 2 7.50 n2-highcpu-2 europe-west3-c 2 2.00 n2-highmem-2 europe-west3-c 2 16.00 n2-standard-2 europe-west3-c 2 8.00 n2d-highcpu-2 europe-west3-c 2 2.00 n2d-highmem-2 europe-west3-c 2 16.00 n2d-standard-2 europe-west3-c 2 8.00 t2d-standard-1 europe-west3-c 1 4.00 t2d-standard-2 europe-west3-c 2 8.00 PS C:\TACG> We need to filter the results to narrow down usable instances for the Worker VMs - we want to use instances with max. 8 vCPUs: .tg {border-collapse:collapse;border-spacing:0;width:100%} .tg .tg-5jvq{background-color:#0000b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px; text-align:left;vertical-align:top} Filtered for Worker: PS C:\TACG> gcloud compute machine-types list --filter="(CPUS<=8) AND (zone:europe-west3-c)" NAME ZONE CPUS MEMORY_GB DEPRECATED c2-standard-4 europe-west3-c 4 16.00 c2-standard-8 europe-west3-c 8 32.00 c2d-highcpu-2 europe-west3-c 2 4.00 c2d-highcpu-4 europe-west3-c 4 8.00 c2d-highcpu-8 europe-west3-c 8 16.00 c2d-highmem-2 europe-west3-c 2 16.00 c2d-highmem-4 europe-west3-c 4 32.00 c2d-highmem-8 europe-west3-c 8 64.00 c2d-standard-2 europe-west3-c 2 8.00 c2d-standard-4 europe-west3-c 4 16.00 c2d-standard-8 europe-west3-c 8 32.00 e2-highcpu-2 europe-west3-c 2 2.00 e2-highcpu-4 europe-west3-c 4 4.00 e2-highcpu-8 europe-west3-c 8 8.00 e2-highmem-2 europe-west3-c 2 16.00 e2-highmem-4 europe-west3-c 4 32.00 e2-highmem-8 europe-west3-c 8 64.00 e2-medium europe-west3-c 2 4.00 e2-micro europe-west3-c 2 1.00 e2-small europe-west3-c 2 2.00 e2-standard-2 europe-west3-c 2 8.00 e2-standard-4 europe-west3-c 4 16.00 e2-standard-8 europe-west3-c 8 32.00 f1-micro europe-west3-c 1 0.60 g1-small europe-west3-c 1 1.70 n1-highcpu-2 europe-west3-c 2 1.80 n1-highcpu-4 europe-west3-c 4 3.60 n1-highcpu-8 europe-west3-c 8 7.20 n1-highmem-2 europe-west3-c 2 13.00 n1-highmem-4 europe-west3-c 4 26.00 n1-highmem-8 europe-west3-c 8 52.00 n1-standard-1 europe-west3-c 1 3.75 n1-standard-2 europe-west3-c 2 7.50 n1-standard-4 europe-west3-c 4 15.00 n1-standard-8 europe-west3-c 8 30.00 n2-highcpu-2 europe-west3-c 2 2.00 n2-highcpu-4 europe-west3-c 4 4.00 n2-highcpu-8 europe-west3-c 8 8.00 n2-highmem-2 europe-west3-c 2 16.00 n2-highmem-4 europe-west3-c 4 32.00 n2-highmem-8 europe-west3-c 8 64.00 n2-standard-2 europe-west3-c 2 8.00 n2-standard-4 europe-west3-c 4 16.00 n2-standard-8 europe-west3-c 8 32.00 n2d-highcpu-2 europe-west3-c 2 2.00 n2d-highcpu-4 europe-west3-c 4 4.00 n2d-highcpu-8 europe-west3-c 8 8.00 n2d-highmem-2 europe-west3-c 2 16.00 n2d-highmem-4 europe-west3-c 4 32.00 n2d-highmem-8 europe-west3-c 8 64.00 n2d-standard-2 europe-west3-c 2 8.00 n2d-standard-4 europe-west3-c 4 16.00 n2d-standard-8 europe-west3-c 8 32.00 t2d-standard-1 europe-west3-c 1 4.00 t2d-standard-2 europe-west3-c 2 8.00 t2d-standard-4 europe-west3-c 4 16.00 t2d-standard-8 europe-west3-c 8 32.00 PS C:\TACG> Note the Name parameter of the Google Cloud Platform (GCP) instances you want to use. 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: .tg {border-collapse:collapse;border-spacing:0;width:100%} .tg .tg-5jvq{background-color:#0000b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px; text-align:left;vertical-align:top} PS C:\TACG> gcloud projects list --impersonate-service-account=XXXXXXXXXX@XXXXXXXXXX.iam.gserviceaccount.com PROJECT_ID NAME PROJECT_NUMBER tacg-gcp-XXXXXXXXXX TACG-GCP XXXXXXXXXXXX PS C:\TACG> PS C:\TACG> gcloud projects get-iam-policy tacg-gcp-XXXXXXXXXX --flatten="bindings[].members" --format='table(bindings.role)' --filter="bindings.members:XXXXXXXXXX@XXXXXXXXXX.iam.gserviceaccount.com" ROLE roles/compute.admin roles/iam.securityAdmin roles/iam.cloudbuild.builds.editor roles/iam.storage.admin roles/iam.serviceAccountUser roles/iam.datastore.user ... roles/owner PS C:\TACG> 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. Open Identity and Access Management in WebStudio: Click API Access, Secure Clients and put a name in the textbox adjacent to the button Create Client. After entering a name. click Create Client: After the Secure Client is created, copy and write down the shown ID and Secret: The Secret is only visible during creation - after closing the window, you cannot get it anymore. The client-id and client-secret fields are needed by the Citrix Terraform provider. The also needed customer-id field can be found in your Citrix Cloud details. Put the values in the corresponding .auto.tvars.json file: .table_component { overflow: auto; width: 100%; } .table_component table { border: 1px solid #323232; border-radius: 3px; height: 100%; width: 90%; table-layout: fixed; border-collapse: collapse; border-spacing: 1px; text-align: left; } .table_component caption { caption-side: top; text-align: left; } .table_component th { border: 1px solid #323232; background-color: #323232; color: #ffffff; padding: 10px; } .table_component td { border: 1px solid #323232; background-color: #323232; color: #ffffff; padding: 10px; font-family: Consolas,Courier New; font-size:12px; } { ... "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 field access-token: The token is normally valid for 3600 seconds. Put the access-token value in the corresponding .auto.tvars.json file: .table_component { overflow: auto; width: 100%; } .table_component table { border: 1px solid #323232; border-radius: 3px; height: 100%; width: 90%; table-layout: fixed; border-collapse: collapse; border-spacing: 1px; text-align: left; } .table_component caption { caption-side: top; text-align: left; } .table_component th { border: 1px solid #323232; background-color: #323232; color: #ffffff; padding: 10px; } .table_component td { border: 1px solid #323232; background-color: #323232; color: #ffffff; padding: 10px; font-family: Consolas,Courier New; font-size:12px; } { ... "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", ... } You also can use PowerShell to request a Bearer Token - therefore, you need a valid Secure Client stored in Citrix Cloud: .tg {border-collapse:collapse;border-spacing:0;width:100%} .tg .tg-5jvq{background-color:#0000b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px; text-align:left;vertical-align:top} asnp Citrix* $key= "f4eXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" $secret= "VJCXXXXXXXXXXXXXX" $customer= "uXXXXXXXX" $XDStoredCredentials = Set-XDCredentials -StoreAs default -ProfileType CloudApi -CustomerId $customer -APIKey $key -SecretKey $secret $auth = Get-XDAuthentication $BT = $GLOBAL:XDAuthToken | Out-File "<Path where to store the Token\BT.txt" -NoNewline -Encoding Ascii 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. 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 .tg {border-collapse:collapse;border-spacing:0;width:100%} .tg .tg-5jvq{background-color:#0000b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px; text-align:left;vertical-align:top} PS C:\TACG\_CCOnGCP\_CCOnGCP-Creation> terraform init Initializing the backend... Initializing provider plugins... - Finding latest version of hashicorp/template... - Finding hashicorp/google versions matching ">= 5.21.0"... - Finding mastercard/restapi versions matching "1.18.2"... - Finding citrix/citrix versions matching ">= 0.5.4"... - Installing mastercard/restapi v1.18.2... - Installed mastercard/restapi v1.18.2 (self-signed, key ID DCB8C431D71C30AB) - Installing citrix/citrix v0.5.4... - Installed citrix/citrix v0.5.4 (signed by a HashiCorp partner, key ID 25D62DD8407EA386) - Installing hashicorp/template v2.2.0... - Installed hashicorp/template v2.2.0 (signed by HashiCorp) - Installing hashicorp/google v5.21.0... - Installed hashicorp/google v5.21.0 (signed by HashiCorp) Partner and community providers are signed by their developers. If you'd like to know more about provider signing, you can read about it here: https://www.terraform.io/docs/cli/plugins/signing.html Terraform has created a lock file .terraform.lock.hcl to record the provider selections it made above. Include this file in your version control repository so that Terraform can guarantee to make the same selections by default when you run "terraform init" in the future. Terraform has been successfully initialized! You may now begin working with Terraform. Try running "terraform plan" to see any changes that are required for your infrastructure. All Terraform commands should now work. If you ever set or change modules or backend configuration for Terraform, rerun this command to reinitialize your working directory. If you forget, other commands will detect it and remind you to do so if necessary. PS C:\TACG\_CCOnGCP\_CCOnGCP-Creation> terraform plan data.template_file.Initial-Windows-BootScript-NoDC: Reading... data.template_file.Initial-Windows-BootScript-NoDC: Read complete after 0s [id=d6adcc3a41a5c143a357d5555931b532da28a66cb82bfb58fea2801950cb2844] data.google_compute_network.VPC: Reading... data.google_compute_subnetwork.Subnet: Reading... data.google_compute_subnetwork.Subnet: Read complete after 0s [id=projects/tacg-gcp-XXXXXXXXXX/regions/europe-west3/subnetworks/default] data.google_compute_network.VPC: Read complete after 1s [id=projects/tacg-gcp-XXXXXXXXXX/global/networks/default] Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # google_compute_address.internal-ip-adminvm will be created + resource "google_compute_address" "internal-ip-adminvm" { + address = "10.156.0.7" + address_type = "INTERNAL" + creation_timestamp = (known after apply) + effective_labels = (known after apply) + id = (known after apply) + label_fingerprint = (known after apply) + name = "internal-ip-avm" + network_tier = (known after apply) + prefix_length = (known after apply) + project = "tacg-gcp-XXXXXXXXXX" + purpose = (known after apply) + region = "europe-west3" + self_link = (known after apply) + subnetwork = "projects/tacg-gcp-XXXXXXXXXX/regions/europe-west3/subnetworks/default" + terraform_labels = (known after apply) + users = (known after apply) } # google_compute_address.internal-ip-cc1 will be created + resource "google_compute_address" "internal-ip-cc1" { + address = "10.156.0.5" + address_type = "INTERNAL" + creation_timestamp = (known after apply) + effective_labels = (known after apply) + id = (known after apply) + label_fingerprint = (known after apply) + name = "internal-ip-cc1" + network_tier = (known after apply) + prefix_length = (known after apply) + project = "tacg-gcp-XXXXXXXXXX" + purpose = (known after apply) + region = "europe-west3" + self_link = (known after apply) + subnetwork = "projects/tacg-gcp-XXXXXXXXXX/regions/europe-west3/subnetworks/default" + terraform_labels = (known after apply) + users = (known after apply) } # google_compute_address.internal-ip-cc2 will be created + resource "google_compute_address" "internal-ip-cc2" { + address = "10.156.0.6" + address_type = "INTERNAL" + creation_timestamp = (known after apply) + effective_labels = (known after apply) + id = (known after apply) + label_fingerprint = (known after apply) + name = "internal-ip-cc2" + network_tier = (known after apply) + prefix_length = (known after apply) + project = "tacg-gcp-XXXXXXXXXX" + purpose = (known after apply) + region = "europe-west3" + self_link = (known after apply) + subnetwork = "projects/tacg-gcp-XXXXXXXXXX/regions/europe-west3/subnetworks/default" + terraform_labels = (known after apply) + users = (known after apply) } # google_compute_address.internal-ip-wmi will be created + resource "google_compute_address" "internal-ip-wmi" { + address = "10.156.0.8" + address_type = "INTERNAL" + creation_timestamp = (known after apply) + effective_labels = (known after apply) + id = (known after apply) + label_fingerprint = (known after apply) + name = "internal-ip-wmi" + network_tier = (known after apply) + prefix_length = (known after apply) + project = "tacg-gcp-XXXXXXXXXX" + purpose = (known after apply) + region = "europe-west3" + self_link = (known after apply) + subnetwork = "projects/tacg-gcp-XXXXXXXXXX/regions/europe-west3/subnetworks/default" + terraform_labels = (known after apply) + users = (known after apply) } # google_compute_firewall.Allow-All-ICMP will be created + resource "google_compute_firewall" "Allow-All-ICMP" { + creation_timestamp = (known after apply) + destination_ranges = (known after apply) + direction = (known after apply) + enable_logging = (known after apply) + id = (known after apply) + name = "tacg-gcp-tf-fw-allow-all-icmp" + network = "default" + priority = 1000 + project = "tacg-gcp-XXXXXXXXXX" + self_link = (known after apply) + source_ranges = [ + "10.156.0.0/20", ] + target_tags = [ + "all-icmp", ] + allow { + ports = [] + protocol = "icmp" } } # google_compute_firewall.Allow-All-TCP will be created + resource "google_compute_firewall" "Allow-All-TCP" { + creation_timestamp = (known after apply) + destination_ranges = (known after apply) + direction = (known after apply) + enable_logging = (known after apply) + id = (known after apply) + name = "tacg-gcp-tf-fw-allow-all-tcp" + network = "default" + priority = 1000 + project = "tacg-gcp-XXXXXXXXXX" + self_link = (known after apply) + source_ranges = [ + "10.156.0.0/20", ] + target_tags = [ + "all-tcp", ] + allow { + ports = [ + "0-65534", ] + protocol = "tcp" } } # google_compute_firewall.Allow-All-UDP will be created + resource "google_compute_firewall" "Allow-All-UDP" { + creation_timestamp = (known after apply) + destination_ranges = (known after apply) + direction = (known after apply) + enable_logging = (known after apply) + id = (known after apply) + name = "tacg-gcp-tf-fw-allow-all-udp" + network = "default" + priority = 1000 + project = "tacg-gcp-XXXXXXXXXX" + self_link = (known after apply) + source_ranges = [ + "10.156.0.0/20", ] + target_tags = [ + "all-udp", ] + allow { + ports = [ + "0-65534", ] + protocol = "udp" } } # google_compute_firewall.Allow-HTTP will be created + resource "google_compute_firewall" "Allow-HTTP" { + creation_timestamp = (known after apply) + destination_ranges = (known after apply) + direction = (known after apply) + enable_logging = (known after apply) + id = (known after apply) + name = "tacg-gcp-tf-fw-allow-http" + network = "default" + priority = 1000 + project = "tacg-gcp-XXXXXXXXXX" + self_link = (known after apply) + source_ranges = [ + "0.0.0.0/0", ] + target_tags = [ + "http", ] + allow { + ports = [ + "80", ] + protocol = "tcp" } } # google_compute_firewall.Allow-HTTPS will be created + resource "google_compute_firewall" "Allow-HTTPS" { + creation_timestamp = (known after apply) + destination_ranges = (known after apply) + direction = (known after apply) + enable_logging = (known after apply) + id = (known after apply) + name = "tacg-gcp-tf-fw-allow-https" + network = "default" + priority = 1000 + project = "tacg-gcp-XXXXXXXXXX" + self_link = (known after apply) + source_ranges = [ + "0.0.0.0/0", ] + target_tags = [ + "https", ] + allow { + ports = [ + "443", ] + protocol = "tcp" } } # google_compute_firewall.Allow-RDP will be created + resource "google_compute_firewall" "Allow-RDP" { + creation_timestamp = (known after apply) + destination_ranges = (known after apply) + direction = (known after apply) + enable_logging = (known after apply) + id = (known after apply) + name = "tacg-gcp-tf-fw-allow-rdp" + network = "default" + priority = 1000 + project = "tacg-gcp-XXXXXXXXXX" + self_link = (known after apply) + source_ranges = [ + "0.0.0.0/0", ] + target_tags = [ + "rdp", ] + allow { + ports = [ + "3389", ] + protocol = "tcp" } } # google_compute_firewall.Allow-SSH will be created + resource "google_compute_firewall" "Allow-SSH" { + creation_timestamp = (known after apply) + destination_ranges = (known after apply) + direction = (known after apply) + enable_logging = (known after apply) + id = (known after apply) + name = "tacg-gcp-tf-fw-allow-ssh" + network = "default" + priority = 1000 + project = "tacg-gcp-XXXXXXXXXX" + self_link = (known after apply) + source_ranges = [ + "10.156.0.0/20", ] + target_tags = [ + "ssh", ] + allow { + ports = [ + "22", ] + protocol = "tcp" } } # google_compute_firewall.Allow-WinRM will be created + resource "google_compute_firewall" "Allow-WinRM" { + creation_timestamp = (known after apply) + destination_ranges = (known after apply) + direction = (known after apply) + enable_logging = (known after apply) + id = (known after apply) + name = "tacg-gcp-tf-fw-allow-winrm" + network = "default" + priority = 1000 + project = "tacg-gcp-XXXXXXXXXX" + self_link = (known after apply) + source_ranges = [ + "10.156.0.0/20", ] + target_tags = [ + "winrm", ] + allow { + ports = [ + "5985", + "5986", ] + protocol = "tcp" } } # google_compute_instance.AdminVM will be created + resource "google_compute_instance" "AdminVM" { + can_ip_forward = false + cpu_platform = (known after apply) + current_status = (known after apply) + deletion_protection = false + effective_labels = (known after apply) + guest_accelerator = (known after apply) + hostname = "tacg-gcp-tf-avm.gcp.the-austrian-citrix-guy.at" + id = (known after apply) + instance_id = (known after apply) + label_fingerprint = (known after apply) + machine_type = "n2-standard-2" + metadata = { + "sysprep-specialize-script-ps1" = <<-EOT Start-Sleep -Seconds 60 #Set DNS Server address Set-DNSClientServerAddress -ServerAddresses '10.156.0.2' -InterfaceIndex (Get-NetAdapter).InterfaceIndex netdom.exe join $env:COMPUTERNAME /domain:gcp.the-austrian-citrix-guy.at /UserD:XXXXXXXXXX@gcp.the-austrian-citrix-guy.at /PasswordD:XXXXXXXXXXXX /reboot:5 EOT } + metadata_fingerprint = (known after apply) + min_cpu_platform = (known after apply) + name = "tacg-gcp-tf-avm" + project = "tacg-gcp-XXXXXXXXXX" + self_link = (known after apply) + tags = [ + "all-icmp", + "all-tcp", + "all-udp", + "http", + "https", + "rdp", + "winrm", ] + tags_fingerprint = (known after apply) + terraform_labels = (known after apply) + zone = "europe-west3-c" + boot_disk { + auto_delete = true + device_name = (known after apply) + disk_encryption_key_sha256 = (known after apply) + kms_key_self_link = (known after apply) + mode = "READ_WRITE" + source = (known after apply) + initialize_params { + image = "windows-server-2022-dc-v20240313" + labels = (known after apply) + provisioned_iops = (known after apply) + provisioned_throughput = (known after apply) + size = (known after apply) + type = (known after apply) } } + network_interface { + internal_ipv6_prefix_length = (known after apply) + ipv6_access_type = (known after apply) + ipv6_address = (known after apply) + name = (known after apply) + network = "default" + network_ip = "10.156.0.7" + stack_type = (known after apply) + subnetwork = "default" + subnetwork_project = (known after apply) + access_config { + nat_ip = (known after apply) + network_tier = (known after apply) } } } # google_compute_instance.CC1 will be created + resource "google_compute_instance" "CC1" { + can_ip_forward = false + cpu_platform = (known after apply) + current_status = (known after apply) + deletion_protection = false + effective_labels = (known after apply) + guest_accelerator = (known after apply) + hostname = "tacg-gcp-tf-cc1.gcp.the-austrian-citrix-guy.at" + id = (known after apply) + instance_id = (known after apply) + label_fingerprint = (known after apply) + machine_type = "n2-standard-2" + metadata = { + "sysprep-specialize-script-ps1" = <<-EOT Start-Sleep -Seconds 60 #Set DNS Server address Set-DNSClientServerAddress -ServerAddresses '10.156.0.2' -InterfaceIndex (Get-NetAdapter).InterfaceIndex netdom.exe join $env:COMPUTERNAME /domain:gcp.the-austrian-citrix-guy.at /UserD:XXXXXXXXXX@gcp.the-austrian-citrix-guy.at /PasswordD:XXXXXXXXXXXX /reboot:5 EOT } + metadata_fingerprint = (known after apply) + min_cpu_platform = (known after apply) + name = "tacg-gcp-tf-cc1" + project = "tacg-gcp-XXXXXXXXXX" + self_link = (known after apply) + tags = [ + "all-icmp", + "all-tcp", + "all-udp", + "http", + "https", + "rdp", + "winrm", ] + tags_fingerprint = (known after apply) + terraform_labels = (known after apply) + zone = "europe-west3-c" + boot_disk { + auto_delete = true + device_name = (known after apply) + disk_encryption_key_sha256 = (known after apply) + kms_key_self_link = (known after apply) + mode = "READ_WRITE" + source = (known after apply) + initialize_params { + image = "windows-server-2022-dc-v20240313" + labels = (known after apply) + provisioned_iops = (known after apply) + provisioned_throughput = (known after apply) + size = (known after apply) + type = (known after apply) } } + network_interface { + internal_ipv6_prefix_length = (known after apply) + ipv6_access_type = (known after apply) + ipv6_address = (known after apply) + name = (known after apply) + network = "default" + network_ip = "10.156.0.5" + stack_type = (known after apply) + subnetwork = "default" + subnetwork_project = (known after apply) + access_config { + nat_ip = (known after apply) + network_tier = (known after apply) } } } # google_compute_instance.CC2 will be created + resource "google_compute_instance" "CC2" { + can_ip_forward = false + cpu_platform = (known after apply) + current_status = (known after apply) + deletion_protection = false + effective_labels = (known after apply) + guest_accelerator = (known after apply) + hostname = "tacg-gcp-tf-cc2.gcp.the-austrian-citrix-guy.at" + id = (known after apply) + instance_id = (known after apply) + label_fingerprint = (known after apply) + machine_type = "n2-standard-2" + metadata = { + "sysprep-specialize-script-ps1" = <<-EOT Start-Sleep -Seconds 60 #Set DNS Server address Set-DNSClientServerAddress -ServerAddresses '10.156.0.2' -InterfaceIndex (Get-NetAdapter).InterfaceIndex netdom.exe join $env:COMPUTERNAME /domain:gcp.the-austrian-citrix-guy.at /UserD:XXXXXXXXXX@gcp.the-austrian-citrix-guy.at /PasswordD:XXXXXXXXXXXX /reboot:5 EOT } + metadata_fingerprint = (known after apply) + min_cpu_platform = (known after apply) + name = "tacg-gcp-tf-cc2" + project = "tacg-gcp-XXXXXXXXXX" + self_link = (known after apply) + tags = [ + "all-icmp", + "all-tcp", + "all-udp", + "http", + "https", + "rdp", + "winrm", ] + tags_fingerprint = (known after apply) + terraform_labels = (known after apply) + zone = "europe-west3-c" + boot_disk { + auto_delete = true + device_name = (known after apply) + disk_encryption_key_sha256 = (known after apply) + kms_key_self_link = (known after apply) + mode = "READ_WRITE" + source = (known after apply) + initialize_params { + image = "windows-server-2022-dc-v20240313" + labels = (known after apply) + provisioned_iops = (known after apply) + provisioned_throughput = (known after apply) + size = (known after apply) + type = (known after apply) } } + network_interface { + internal_ipv6_prefix_length = (known after apply) + ipv6_access_type = (known after apply) + ipv6_address = (known after apply) + name = (known after apply) + network = "default" + network_ip = "10.156.0.6" + stack_type = (known after apply) + subnetwork = "default" + subnetwork_project = (known after apply) + access_config { + nat_ip = (known after apply) + network_tier = (known after apply) } } } # google_compute_instance.WMI will be created + resource "google_compute_instance" "WMI" { + can_ip_forward = false + cpu_platform = (known after apply) + current_status = (known after apply) + deletion_protection = false + effective_labels = (known after apply) + guest_accelerator = (known after apply) + hostname = "tacg-gcp-tf-wmi.gcp.the-austrian-citrix-guy.at" + id = (known after apply) + instance_id = (known after apply) + label_fingerprint = (known after apply) + machine_type = "n2-standard-2" + metadata = { + "sysprep-specialize-script-ps1" = <<-EOT Start-Sleep -Seconds 60 #Set DNS Server address Set-DNSClientServerAddress -ServerAddresses '10.156.0.2' -InterfaceIndex (Get-NetAdapter).InterfaceIndex netdom.exe join $env:COMPUTERNAME /domain:gcp.the-austrian-citrix-guy.at /UserD:XXXXXXXXXX@gcp.the-austrian-citrix-guy.at /PasswordD:XXXXXXXXXXXX /reboot:5 EOT } + metadata_fingerprint = (known after apply) + min_cpu_platform = (known after apply) + name = "tacg-gcp-tf-wmi" + project = "tacg-gcp-XXXXXXXXXX" + self_link = (known after apply) + tags = [ + "all-icmp", + "all-tcp", + "all-udp", + "http", + "https", + "rdp", + "winrm", ] + tags_fingerprint = (known after apply) + terraform_labels = (known after apply) + zone = "europe-west3-c" + boot_disk { + auto_delete = true + device_name = (known after apply) + disk_encryption_key_sha256 = (known after apply) + kms_key_self_link = (known after apply) + mode = "READ_WRITE" + source = (known after apply) + initialize_params { + image = "windows-server-2022-dc-v20240313" + labels = (known after apply) + provisioned_iops = (known after apply) + provisioned_throughput = (known after apply) + size = (known after apply) + type = (known after apply) } } + network_interface { + internal_ipv6_prefix_length = (known after apply) + ipv6_access_type = (known after apply) + ipv6_address = (known after apply) + name = (known after apply) + network = "default" + network_ip = "10.156.0.8" + stack_type = (known after apply) + subnetwork = "default" + subnetwork_project = (known after apply) + access_config { + nat_ip = (known after apply) + network_tier = (known after apply) } } } Plan: 24 to add, 0 to change, 0 to destroy. Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now. PS C:\TACG\_CCOnGCP\_CCOnGCP-Creation> terraform apply data.template_file.Initial-Windows-BootScript-NoDC: Reading... data.template_file.Initial-Windows-BootScript-NoDC: Read complete after 0s [id=d6adcc3a41a5c143a357d5555931b532da28a66cb82bfb58fea2801950cb2844] data.google_compute_network.VPC: Reading... data.google_compute_subnetwork.Subnet: Reading... data.google_compute_subnetwork.Subnet: Read complete after 0s [id=projects/tacg-gcp-XXXXXXXXXX/regions/europe-west3/subnetworks/default] data.google_compute_network.VPC: Read complete after 1s [id=projects/tacg-gcp-XXXXXXXXXX/global/networks/default] Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # google_compute_address.internal-ip-adminvm will be created + resource "google_compute_address" "internal-ip-adminvm" { + address = "10.156.0.7" + address_type = "INTERNAL" + creation_timestamp = (known after apply) + effective_labels = (known after apply) + id = (known after apply) + label_fingerprint = (known after apply) + name = "internal-ip-avm" + network_tier = (known after apply) + prefix_length = (known after apply) + project = "tacg-gcp-XXXXXXXXXX" + purpose = (known after apply) + region = "europe-west3" + self_link = (known after apply) + subnetwork = "projects/tacg-gcp-XXXXXXXXXX/regions/europe-west3/subnetworks/default" + terraform_labels = (known after apply) + users = (known after apply) } # google_compute_address.internal-ip-cc1 will be created + resource "google_compute_address" "internal-ip-cc1" { + address = "10.156.0.5" + address_type = "INTERNAL" + creation_timestamp = (known after apply) + effective_labels = (known after apply) + id = (known after apply) + label_fingerprint = (known after apply) + name = "internal-ip-cc1" + network_tier = (known after apply) + prefix_length = (known after apply) + project = "tacg-gcp-XXXXXXXXXX" + purpose = (known after apply) + region = "europe-west3" + self_link = (known after apply) + subnetwork = "projects/tacg-gcp-XXXXXXXXXX/regions/europe-west3/subnetworks/default" + terraform_labels = (known after apply) + users = (known after apply) } # google_compute_address.internal-ip-cc2 will be created + resource "google_compute_address" "internal-ip-cc2" { + address = "10.156.0.6" + address_type = "INTERNAL" + creation_timestamp = (known after apply) + effective_labels = (known after apply) + id = (known after apply) + label_fingerprint = (known after apply) + name = "internal-ip-cc2" + network_tier = (known after apply) + prefix_length = (known after apply) + project = "tacg-gcp-XXXXXXXXXX" + purpose = (known after apply) + region = "europe-west3" + self_link = (known after apply) + subnetwork = "projects/tacg-gcp-XXXXXXXXXX/regions/europe-west3/subnetworks/default" + terraform_labels = (known after apply) + users = (known after apply) } # google_compute_address.internal-ip-wmi will be created + resource "google_compute_address" "internal-ip-wmi" { + address = "10.156.0.8" + address_type = "INTERNAL" + creation_timestamp = (known after apply) + effective_labels = (known after apply) + id = (known after apply) + label_fingerprint = (known after apply) + name = "internal-ip-wmi" + network_tier = (known after apply) + prefix_length = (known after apply) + project = "tacg-gcp-XXXXXXXXXX" + purpose = (known after apply) + region = "europe-west3" + self_link = (known after apply) + subnetwork = "projects/tacg-gcp-XXXXXXXXXX/regions/europe-west3/subnetworks/default" + terraform_labels = (known after apply) + users = (known after apply) } # google_compute_firewall.Allow-All-ICMP will be created + resource "google_compute_firewall" "Allow-All-ICMP" { + creation_timestamp = (known after apply) + destination_ranges = (known after apply) + direction = (known after apply) + enable_logging = (known after apply) + id = (known after apply) + name = "tacg-gcp-tf-fw-allow-all-icmp" + network = "default" + priority = 1000 + project = "tacg-gcp-XXXXXXXXXX" + self_link = (known after apply) + source_ranges = [ + "10.156.0.0/20", ] + target_tags = [ + "all-icmp", ] + allow { + ports = [] + protocol = "icmp" } } # google_compute_firewall.Allow-All-TCP will be created + resource "google_compute_firewall" "Allow-All-TCP" { + creation_timestamp = (known after apply) + destination_ranges = (known after apply) + direction = (known after apply) + enable_logging = (known after apply) + id = (known after apply) + name = "tacg-gcp-tf-fw-allow-all-tcp" + network = "default" + priority = 1000 + project = "tacg-gcp-XXXXXXXXXX" + self_link = (known after apply) + source_ranges = [ + "10.156.0.0/20", ] + target_tags = [ + "all-tcp", ] + allow { + ports = [ + "0-65534", ] + protocol = "tcp" } } # google_compute_firewall.Allow-All-UDP will be created + resource "google_compute_firewall" "Allow-All-UDP" { + creation_timestamp = (known after apply) + destination_ranges = (known after apply) + direction = (known after apply) + enable_logging = (known after apply) + id = (known after apply) + name = "tacg-gcp-tf-fw-allow-all-udp" + network = "default" + priority = 1000 + project = "tacg-gcp-XXXXXXXXXX" + self_link = (known after apply) + source_ranges = [ + "10.156.0.0/20", ] + target_tags = [ + "all-udp", ] + allow { + ports = [ + "0-65534", ] + protocol = "udp" } } # google_compute_firewall.Allow-HTTP will be created + resource "google_compute_firewall" "Allow-HTTP" { + creation_timestamp = (known after apply) + destination_ranges = (known after apply) + direction = (known after apply) + enable_logging = (known after apply) + id = (known after apply) + name = "tacg-gcp-tf-fw-allow-http" + network = "default" + priority = 1000 + project = "tacg-gcp-XXXXXXXXXX" + self_link = (known after apply) + source_ranges = [ + "0.0.0.0/0", ] + target_tags = [ + "http", ] + allow { + ports = [ + "80", ] + protocol = "tcp" } } # google_compute_firewall.Allow-HTTPS will be created + resource "google_compute_firewall" "Allow-HTTPS" { + creation_timestamp = (known after apply) + destination_ranges = (known after apply) + direction = (known after apply) + enable_logging = (known after apply) + id = (known after apply) + name = "tacg-gcp-tf-fw-allow-https" + network = "default" + priority = 1000 + project = "tacg-gcp-XXXXXXXXXX" + self_link = (known after apply) + source_ranges = [ + "0.0.0.0/0", ] + target_tags = [ + "https", ] + allow { + ports = [ + "443", ] + protocol = "tcp" } } # google_compute_firewall.Allow-RDP will be created + resource "google_compute_firewall" "Allow-RDP" { + creation_timestamp = (known after apply) + destination_ranges = (known after apply) + direction = (known after apply) + enable_logging = (known after apply) + id = (known after apply) + name = "tacg-gcp-tf-fw-allow-rdp" + network = "default" + priority = 1000 + project = "tacg-gcp-XXXXXXXXXX" + self_link = (known after apply) + source_ranges = [ + "0.0.0.0/0", ] + target_tags = [ + "rdp", ] + allow { + ports = [ + "3389", ] + protocol = "tcp" } } # google_compute_firewall.Allow-SSH will be created + resource "google_compute_firewall" "Allow-SSH" { + creation_timestamp = (known after apply) + destination_ranges = (known after apply) + direction = (known after apply) + enable_logging = (known after apply) + id = (known after apply) + name = "tacg-gcp-tf-fw-allow-ssh" + network = "default" + priority = 1000 + project = "tacg-gcp-XXXXXXXXXX" + self_link = (known after apply) + source_ranges = [ + "10.156.0.0/20", ] + target_tags = [ + "ssh", ] + allow { + ports = [ + "22", ] + protocol = "tcp" } } # google_compute_firewall.Allow-WinRM will be created + resource "google_compute_firewall" "Allow-WinRM" { + creation_timestamp = (known after apply) + destination_ranges = (known after apply) + direction = (known after apply) + enable_logging = (known after apply) + id = (known after apply) + name = "tacg-gcp-tf-fw-allow-winrm" + network = "default" + priority = 1000 + project = "tacg-gcp-XXXXXXXXXX" + self_link = (known after apply) + source_ranges = [ + "10.156.0.0/20", ] + target_tags = [ + "winrm", ] + allow { + ports = [ + "5985", + "5986", ] + protocol = "tcp" } } # google_storage_bucket.Prereqs will be created + resource "google_storage_bucket" "Prereqs" { + effective_labels = (known after apply) + force_destroy = true + id = (known after apply) + location = "EU" + name = "XXXXXXXXXX" + project = (known after apply) + public_access_prevention = (known after apply) + rpo = (known after apply) + self_link = (known after apply) + storage_class = "STANDARD" + terraform_labels = (known after apply) + uniform_bucket_level_access = (known after apply) + url = (known after apply) } # google_storage_bucket_access_control.Prereqs will be created + resource "google_storage_bucket_access_control" "Prereqs" { + bucket = "XXXXXXXXXX" + domain = (known after apply) + email = (known after apply) + entity = "allUsers" + id = (known after apply) + role = "READER" } # google_storage_bucket_iam_binding.iam will be created + resource "google_storage_bucket_iam_binding" "iam" { + bucket = "XXXXXXXXXX" + etag = (known after apply) + id = (known after apply) + members = [ + "allUsers", ] + role = "roles/storage.objectViewer" } # google_storage_bucket_object.Prereqs will be created + resource "google_storage_bucket_object" "Prereqs" { + bucket = "XXXXXXXXXX" + content = (sensitive value) + content_type = (known after apply) + crc32c = (known after apply) + detect_md5hash = "different hash" + id = (known after apply) + kms_key_name = (known after apply) + md5hash = (known after apply) + media_link = (known after apply) + name = "domain-join-script.ps1" + output_name = (known after apply) + self_link = (known after apply) + source = "./TF-Domain-Join-Script.ps1" + storage_class = (known after apply) } Plan: 24 to add, 0 to change, 0 to destroy. Do you want to perform these actions? Terraform will perform the actions described above. Only 'yes' will be accepted to approve. Enter a value: yes google_storage_bucket.Prereqs: Creating... google_storage_bucket.Prereqs: Creation complete after 2s [id=XXXXXXXXXX] google_storage_bucket_access_control.Prereqs: Creating... google_storage_bucket_object.Prereqs: Creating... google_storage_bucket_object.Prereqs: Creation complete after 1s [id=XXXXXXXXXX-domain-join-script.ps1] google_storage_bucket_access_control.Prereqs: Creation complete after 2s [id=XXXXXXXXXX/allUsers] google_compute_address.internal-ip-adminvm: Creating... google_compute_firewall.Allow-All-UDP: Creating... google_compute_firewall.Allow-SSH: Creating... google_compute_firewall.Allow-HTTPS: Creating... google_compute_firewall.Allow-All-TCP: Creating... google_compute_firewall.Allow-HTTP: Creating... google_compute_firewall.Allow-All-ICMP: Creating... google_compute_firewall.Allow-WinRM: Creating... google_compute_firewall.Allow-RDP: Creating... google_compute_address.internal-ip-cc2: Creating... google_compute_address.internal-ip-adminvm: Still creating... [10s elapsed] google_compute_firewall.Allow-All-TCP: Still creating... [10s elapsed] google_compute_firewall.Allow-All-ICMP: Still creating... [10s elapsed] google_compute_firewall.Allow-SSH: Still creating... [10s elapsed] google_compute_firewall.Allow-RDP: Still creating... [10s elapsed] google_compute_firewall.Allow-All-UDP: Still creating... [10s elapsed] google_compute_firewall.Allow-HTTPS: Still creating... [10s elapsed] google_compute_firewall.Allow-WinRM: Still creating... [10s elapsed] google_compute_firewall.Allow-HTTP: Still creating... [10s elapsed] google_storage_bucket.Prereqs: Creating... google_storage_bucket.Prereqs: Creation complete after 3s [id=XXXXXXXXXX] google_storage_bucket_access_control.Prereqs: Creating... google_storage_bucket_object.Prereqs: Creating... google_storage_bucket_iam_binding.iam: Creating... google_storage_bucket_object.Prereqs: Creation complete after 0s [id=XXXXXXXXXX-domain-join-script.ps1] google_storage_bucket_access_control.Prereqs: Creation complete after 1s [id=XXXXXXXXXX/allUsers] google_storage_bucket_iam_binding.iam: Creation complete after 10s [id=b/XXXXXXXXXX/roles/storage.objectViewer] google_compute_address.internal-ip-cc2: Still creating... [10s elapsed] google_compute_address.internal-ip-cc2: Creation complete after 11s [id=projects/tacg-gcp-XXXXXXXXXX/regions/europe-west3/addresses/internal-ip-cc2] google_compute_address.internal-ip-wmi: Creating... google_compute_address.internal-ip-adminvm: Creation complete after 11s [id=projects/tacg-gcp-XXXXXXXXXX/regions/europe-west3/addresses/internal-ip-avm] google_compute_address.internal-ip-cc1: Creating... google_compute_firewall.Allow-All-ICMP: Creation complete after 11s [id=projects/tacg-gcp-XXXXXXXXXX/global/firewalls/tacg-gcp-tf-fw-allow-all-icmp] google_compute_instance.CC2: Creating... ... **Output shortened ** ... google_compute_firewall.Allow-WinRM: Creation complete after 11s [id=projects/tacg-gcp-XXXXXXXXXX/global/firewalls/tacg-gcp-tf-fw-allow-winrm] google_compute_instance.AdminVM: Creating... google_compute_firewall.Allow-All-UDP: Creation complete after 11s [id=projects/tacg-gcp-XXXXXXXXXX/global/firewalls/tacg-gcp-tf-fw-allow-all-udp] google_compute_firewall.Allow-SSH: Creation complete after 11s [id=projects/tacg-gcp-XXXXXXXXXX/global/firewalls/tacg-gcp-tf-fw-allow-ssh] google_compute_firewall.Allow-RDP: Creation complete after 11s [id=projects/tacg-gcp-XXXXXXXXXX/global/firewalls/tacg-gcp-tf-fw-allow-rdp] google_compute_firewall.Allow-HTTP: Creation complete after 11s [id=projects/tacg-gcp-XXXXXXXXXX/global/firewalls/tacg-gcp-tf-fw-allow-http] google_compute_firewall.Allow-HTTPS: Creation complete after 12s [id=projects/tacg-gcp-XXXXXXXXXX/global/firewalls/tacg-gcp-tf-fw-allow-https] google_compute_firewall.Allow-All-TCP: Creation complete after 12s [id=projects/tacg-gcp-XXXXXXXXXX/global/firewalls/tacg-gcp-tf-fw-allow-all-tcp] google_compute_address.internal-ip-wmi: Still creating... [10s elapsed] google_compute_address.internal-ip-cc1: Still creating... [10s elapsed] google_compute_instance.CC2: Still creating... [10s elapsed] google_compute_instance.AdminVM: Still creating... [10s elapsed] google_compute_address.internal-ip-wmi: Creation complete after 11s [id=projects/tacg-gcp-XXXXXXXXXX/regions/europe-west3/addresses/internal-ip-wmi] google_compute_instance.WMI: Creating... google_compute_address.internal-ip-cc1: Creation complete after 11s [id=projects/tacg-gcp-XXXXXXXXXX/regions/europe-west3/addresses/internal-ip-cc1] google_compute_instance.CC1: Creating... google_compute_instance.CC2: Creation complete after 13s [id=projects/tacg-gcp-XXXXXXXXXX/zones/europe-west3-c/instances/tacg-gcp-tf-cc2] google_compute_instance.AdminVM: Creation complete after 13s [id=projects/tacg-gcp-XXXXXXXXXX/zones/europe-west3-c/instances/tacg-gcp-tf-avm] google_compute_instance.WMI: Still creating... [10s elapsed] google_compute_instance.CC1: Still creating... [10s elapsed] google_compute_instance.WMI: Creation complete after 13s [id=projects/tacg-gcp-XXXXXXXXXX/zones/europe-west3-c/instances/tacg-gcp-tf-wmi] google_compute_instance.CC1: Creation complete after 13s [id=projects/tacg-gcp-XXXXXXXXXX/zones/europe-west3-c/instances/tacg-gcp-tf-cc1] Apply complete! Resources: 24 added, 0 changed, 0 destroyed. PS C:\TACG\_CCOnGCP\_CCOnGCP-Creation> 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: If errors during VM creation or Domain-join occur, look at the output of the serial console of the erroneous VM: Example: The startup script could not run: .tg {border-collapse:collapse;border-spacing:0;width:100%} .tg .tg-5jvq{background-color:#0000b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px; text-align:left;vertical-align:top} PS C:\TACG\_CCOnGCP\_CCOnGCP-Creation gcloud compute instances get-serial-port-output tacg-gcp-tf-avm --zone europe-west3-c CSM BBS Table full. BdsDxe: loading Boot0001 "UEFI Google PersistentDisk " from PciRoot(0x0)/Pci(0x3,0x0)/Scsi(0x1,0x0) BdsDxe: starting Boot0001 "UEFI Google PersistentDisk " from PciRoot(0x0)/Pci(0x3,0x0)/Scsi(0x1,0x0) UEFI: Attempting to start image. Description: UEFI Google PersistentDisk FilePath: PciRoot(0x0)/Pci(0x3,0x0)/Scsi(0x1,0x0) OptionNumber: 1. 2024/03/22 12:24:08 GCEGuestAgent: GCE Agent Started (version 20240109.00) 2024/03/22 12:24:09 GCEGuestAgent: Adding route to metadata server on adapter with index 5 2024/03/22 12:24:09 GCEGuestAgent: Starting the scheduler to run jobs 2024/03/22 12:24:09 GCEGuestAgent: Scheduler - start: [] 2024/03/22 12:24:09 GCEGuestAgent: Skipping scheduling credential generation job, failed to reach client credentials endpoint(instance/credentials/certs) with error: error connecting to metadata server, status code: 404 2024/03/22 12:24:09 GCEGuestAgent: Failed to schedule job MTLS_MDS_Credential_Boostrapper with error: ShouldEnable() returned false, cannot schedule job MTLS_MDS_Credential_Boostrapper 2024/03/22 12:24:09 GCEGuestAgent: Starting the scheduler to run jobs 2024/03/22 12:24:10 GCEGuestAgent: Scheduling job: telemetryJobID 2024/03/22 12:24:10 GCEGuestAgent: Scheduling job "telemetryJobID" to run at 24.000000 hr interval 2024/03/22 12:24:10 GCEGuestAgent: Successfully scheduled job telemetryJobID 2024/03/22 12:25:01 GCEInstanceSetup: Enable google_osconfig_agent during the specialize configuration pass. 2024/03/22 12:25:04 GCEInstanceSetup: Starting sysprep specialize phase. 2024/03/22 12:25:05 GCEInstanceSetup: All networks set to DHCP. 2024/03/22 12:25:05 GCEInstanceSetup: VirtIO network adapter detected. 2024/03/22 12:25:08 GCEInstanceSetup: MTU set to 1460 for IPv4 and IPv6 using PowerShell for interface 5 - Google VirtIO Ethernet Adapter. Build 20348 2024/03/22 12:25:08 GCEInstanceSetup: Running 'route' with arguments '/p add 169.254.169.254 mask 255.255.255.255 0.0.0.0 if 5 metric 1' 2024/03/22 12:25:08 GCEInstanceSetup: --> OK! 2024/03/22 12:25:08 GCEInstanceSetup: Added persistent route to metadata netblock to netkvm adapter. 2024/03/22 12:25:08 GCEInstanceSetup: Getting hostname from metadata server. 2024/03/22 12:25:08 GCEInstanceSetup: Renamed from WIN-6LJJ4HG704O to tacg-gcp-tf-avm. 2024/03/22 12:25:08 GCEInstanceSetup: Configuring WinRM... 2024/03/22 12:25:09 GCEInstanceSetup: Running 'C:\Program Files\Google\Compute Engine\tools\certgen.exe' with arguments '-outDir C:\Windows\TEMP\cert -hostname tacg-gcp-tf-avm' 2024/03/22 12:25:10 GCEInstanceSetup: --> written C:\Windows\TEMP\cert\cert.p12 2024/03/22 12:25:10 GCEInstanceSetup: Waiting for WinRM to be running... 2024/03/22 12:25:11 GCEInstanceSetup: Setup of WinRM complete. 2024/03/22 12:25:11 C:\Program Files\Google\Compute Engine\metadata_scripts\GCEMetadataScripts.exe: Starting specialize scripts (version dev). 2024/03/22 12:25:11 C:\Program Files\Google\Compute Engine\metadata_scripts\GCEMetadataScripts.exe: Found sysprep-specialize-script-url in metadata. 2024/03/22 12:25:15 C:\Program Files\Google\Compute Engine\metadata_scripts\GCEMetadataScripts.exe: Failed to download GCS path: error reading object "domain-join-script.ps1": Get "https://storage.googleapis.com/XXXXXXXXXX/domain-join-script.ps1": metadata: GCE metadata "instance/service-accounts/default/token?scopes=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdevstorage.full_control%2Chttps%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcloud-platform" not defined 2024/03/22 12:25:15 C:\Program Files\Google\Compute Engine\metadata_scripts\GCEMetadataScripts.exe: Trying unauthenticated download 2024/03/22 12:25:28 C:\Program Files\Google\Compute Engine\metadata_scripts\GCEMetadataScripts.exe: sysprep-specialize-script-url: The network path was not found. 2024/03/22 12:25:28 C:\Program Files\Google\Compute Engine\metadata_scripts\GCEMetadataScripts.exe: sysprep-specialize-script-url: 2024/03/22 12:25:28 C:\Program Files\Google\Compute Engine\metadata_scripts\GCEMetadataScripts.exe: sysprep-specialize-script-url: The command failed to complete successfully. 2024/03/22 12:25:28 C:\Program Files\Google\Compute Engine\metadata_scripts\GCEMetadataScripts.exe: sysprep-specialize-script-url: 2024/03/22 12:25:28 C:\Program Files\Google\Compute Engine\metadata_scripts\GCEMetadataScripts.exe: sysprep-specialize-script-url exit status 0 2024/03/22 12:25:28 C:\Program Files\Google\Compute Engine\metadata_scripts\GCEMetadataScripts.exe: Finished running specialize scripts. 2024/03/22 12:25:28 GCEInstanceSetup: Finished with sysprep specialize phase, restarting... 2024/03/22 12:25:44 GCEGuestAgent: Error watching metadata: context canceled 2024/03/22 12:25:44 GCEGuestAgent: GCE Agent Stopped CSM BBS Table full. BdsDxe: loading Boot0003 "Windows Boot Manager" from HD(2,GPT,AB360148-CB4B-41C0-88C9-F1583EB09E72,0x8000,0x32000)/\EFI\Microsoft\Boot\bootmgfw.efi BdsDxe: starting Boot0003 "Windows Boot Manager" from HD(2,GPT,AB360148-CB4B-41C0-88C9-F1583EB09E72,0x8000,0x32000)/\EFI\Microsoft\Boot\bootmgfw.efi UEFI: Attempting to start image. Description: Windows Boot Manager FilePath: HD(2,GPT,AB360148-CB4B-41C0-88C9-F1583EB09E72,0x8000,0x32000)/\EFI\Microsoft\Boot\bootmgfw.efi OptionNumber: 3. 2024/03/22 12:26:46 GCEGuestAgent: GCE Agent Started (version 20240109.00) 2024/03/22 12:26:47 GCEGuestAgent: Starting the scheduler to run jobs 2024/03/22 12:26:47 GCEGuestAgent: Scheduler - start: [] 2024/03/22 12:26:47 GCEGuestAgent: Skipping scheduling credential generation job, failed to reach client credentials endpoint(instance/credentials/certs) with error: error connecting to metadata server, status code: 404 2024/03/22 12:26:47 GCEGuestAgent: Failed to schedule job MTLS_MDS_Credential_Boostrapper with error: ShouldEnable() returned false, cannot schedule job MTLS_MDS_Credential_Boostrapper 2024/03/22 12:26:47 GCEGuestAgent: Starting the scheduler to run jobs 2024-03-22T12:26:48.0295Z OSConfigAgent Info: OSConfig Agent (version 20231207.01.0+win@1) started. 2024/03/22 12:26:48 GCEGuestAgent: Scheduling job: telemetryJobID 2024/03/22 12:26:48 GCEGuestAgent: Scheduling job "telemetryJobID" to run at 24.000000 hr interval 2024/03/22 12:26:48 GCEGuestAgent: Successfully scheduled job telemetryJobID 2024/03/22 12:26:48 GCEGuestAgent: Scheduler - added: [now 2024-03-22 12:26:48.2174847 +0000 GMT entry 1 next 2024-03-23 12:26:48 +0000 GMT] Specify --start=6654 in the next get-serial-port-output invocation to get only the new output starting from here. PS C:\TACG\_CCOnGCP\_CCOnGCP-Creation Now, the next step can be started. Module 2: Install and Configure all Resources in Google Cloud Platform (GCP) 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 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: .table_component { overflow: auto; width: 100%; } .table_component table { border: 1px solid #323232; border-radius: 3px; height: 100%; width: 90%; table-layout: fixed; border-collapse: collapse; border-spacing: 1px; text-align: left; } .table_component caption { caption-side: top; text-align: left; } .table_component th { border: 1px solid #323232; background-color: #323232; color: #ffffff; padding: 10px; } .table_component td { border: 1px solid #323232; background-color: #323232; color: #ffffff; padding: 10px; font-family: Consolas,Courier New; font-size:12px; } ### 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: .table_component { overflow: auto; width: 100%; } .table_component table { border: 1px solid #323232; border-radius: 3px; height: 100%; width: 90%; table-layout: fixed; border-collapse: collapse; border-spacing: 1px; text-align: left; } .table_component caption { caption-side: top; text-align: left; } .table_component th { border: 1px solid #323232; background-color: #323232; color: #ffffff; padding: 10px; } .table_component td { border: 1px solid #323232; background-color: #323232; color: #ffffff; padding: 10px; font-family: Consolas,Courier New; font-size:12px; } #### 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 .tg {border-collapse:collapse;border-spacing:0;width:100%} .tg .tg-5jvq{background-color:#0000b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px; text-align:left;vertical-align:top} PS C:\TACG\_CCOnGCP\_CCOnGCP-Install> terraform init Initializing the backend... Initializing provider plugins... - terraform.io/builtin/terraform is built in to Terraform - Finding latest version of hashicorp/time... - Finding latest version of hashicorp/null... - Finding hashicorp/google versions matching ">= 5.21.0"... - Finding citrix/citrix versions matching ">= 0.5.4"... - Finding latest version of hashicorp/local... - Installing hashicorp/time v0.11.1... - Installed hashicorp/time v0.11.1 (signed by HashiCorp) - Installing hashicorp/null v3.2.2... - Installed hashicorp/null v3.2.2 (signed by HashiCorp) - Installing hashicorp/google v5.22.0... - Installed hashicorp/google v5.22.0 (signed by HashiCorp) - Installing citrix/citrix v0.5.4... - Installed citrix/citrix v0.5.4 (signed by a HashiCorp partner, key ID 25D62DD8407EA386) - Installing hashicorp/local v2.5.1... - Installed hashicorp/local v2.5.1 (signed by HashiCorp) Partner and community providers are signed by their developers. If you'd like to know more about provider signing, you can read about it here: https://www.terraform.io/docs/cli/plugins/signing.html Terraform has created a lock file .terraform.lock.hcl to record the provider selections it made above. Include this file in your version control repository so that Terraform can guarantee to make the same selections by default when you run "terraform init" in the future. Terraform has been successfully initialized! You may now begin working with Terraform. Try running "terraform plan" to see any changes that are required for your infrastructure. All Terraform commands should now work. If you ever set or change modules or backend configuration for Terraform, rerun this command to reinitialize your working directory. If you forget, other commands will detect it and remind you to do so if necessary. PS C:\TACG\_CCOnGCP\_CCOnGCP-Install> terraform plan data.google_compute_address.IPOfCC1: Reading... data.google_compute_address.IPOfCC2: Reading... data.google_compute_address.IPOfCC1: Read complete after 0s [id=projects/tacg-gcp-XXXXXXXXXX/regions/europe-west3/addresses/internal-ip-cc1] data.google_compute_address.IPOfCC2: Read complete after 0s [id=projects/tacg-gcp-XXXXXXXXXX/regions/europe-west3/addresses/internal-ip-cc2] Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # local_file.CWC-Configuration will be created + resource "local_file" "CWC-Configuration" { + content = (sensitive value) + content_base64sha256 = (known after apply) + content_base64sha512 = (known after apply) + content_md5 = (known after apply) + content_sha1 = (known after apply) + content_sha256 = (known after apply) + content_sha512 = (known after apply) + directory_permission = "0777" + file_permission = "0777" + filename = "c:/temp/xdinst/DATA/cwc.json" + id = (known after apply) } # local_file.CreateRLScript will be created + resource "local_file" "CreateRLScript" { + content = (sensitive value) + content_base64sha256 = (known after apply) + content_base64sha512 = (known after apply) + content_md5 = (known after apply) + content_sha1 = (known after apply) + content_sha256 = (known after apply) + content_sha512 = (known after apply) + directory_permission = "0777" + file_permission = "0777" + filename = "c:/temp/xdinst/DATA/CreateRL.ps1" + id = (known after apply) } # local_file.CreateValidCWCOnAVM will be created + resource "local_file" "CreateValidCWCOnAVM" { + content = (sensitive value) + content_base64sha256 = (known after apply) + content_base64sha512 = (known after apply) + content_md5 = (known after apply) + content_sha1 = (known after apply) + content_sha256 = (known after apply) + content_sha512 = (known after apply) + directory_permission = "0777" + file_permission = "0777" + filename = "c:/temp/xdinst/DATA/CreateValidCWCOnAVM.ps1" + id = (known after apply) } # local_file.GetBearerToken will be created + resource "local_file" "GetBearerToken" { + content = (sensitive value) + content_base64sha256 = (known after apply) + content_base64sha512 = (known after apply) + content_md5 = (known after apply) + content_sha1 = (known after apply) + content_sha256 = (known after apply) + content_sha512 = (known after apply) + directory_permission = "0777" + file_permission = "0777" + filename = "c:/temp/xdinst/DATA/GetBT.ps1" + id = (known after apply) } # local_file.GetSiteIDScript will be created + resource "local_file" "GetSiteIDScript" { + content = (sensitive value) + content_base64sha256 = (known after apply) + content_base64sha512 = (known after apply) + content_md5 = (known after apply) + content_sha1 = (known after apply) + content_sha256 = (known after apply) + content_sha512 = (known after apply) + directory_permission = "0777" + file_permission = "0777" + filename = "c:/temp/xdinst/DATA/GetSiteID.ps1" + id = (known after apply) } # local_file.GetZoneIDScript will be created + resource "local_file" "GetZoneIDScript" { + content = (sensitive value) + content_base64sha256 = (known after apply) + content_base64sha512 = (known after apply) + content_md5 = (known after apply) + content_sha1 = (known after apply) + content_sha256 = (known after apply) + content_sha512 = (known after apply) + directory_permission = "0777" + file_permission = "0777" + filename = "c:/temp/xdinst/DATA/GetZoneID.ps1" + id = (known after apply) } # local_file.InstallCWCOnCC will be created + resource "local_file" "InstallCWCOnCC" { + content = (sensitive value) + content_base64sha256 = (known after apply) + content_base64sha512 = (known after apply) + content_md5 = (known after apply) + content_sha1 = (known after apply) + content_sha256 = (known after apply) + content_sha512 = (known after apply) + directory_permission = "0777" + file_permission = "0777" + filename = "c:/temp/xdinst/DATA/InstallCWCOnCC.ps1" + id = (known after apply) } # local_file.InstallPoSHSDKOnAVM will be created + resource "local_file" "InstallPoSHSDKOnAVM" { + content = (sensitive value) + content_base64sha256 = (known after apply) + content_base64sha512 = (known after apply) + content_md5 = (known after apply) + content_sha1 = (known after apply) + content_sha256 = (known after apply) + content_sha512 = (known after apply) + directory_permission = "0777" + file_permission = "0777" + filename = "c:/temp/xdinst/DATA/InstallPoSHSDKOnAVM.ps1" + id = (known after apply) } # local_file.Log will be created + resource "local_file" "Log" { + content = "Directory created." + content_base64sha256 = (known after apply) + content_base64sha512 = (known after apply) + content_md5 = (known after apply) + content_sha1 = (known after apply) + content_sha256 = (known after apply) + content_sha512 = (known after apply) + directory_permission = "0777" + file_permission = "0777" + filename = "c:/temp/xdinst/log.txt" + id = (known after apply) } # local_file.LogData will be created + resource "local_file" "LogData" { + content = "Directory created." + content_base64sha256 = (known after apply) + content_base64sha512 = (known after apply) + content_md5 = (known after apply) + content_sha1 = (known after apply) + content_sha256 = (known after apply) + content_sha512 = (known after apply) + directory_permission = "0777" + file_permission = "0777" + filename = "c:/temp/xdinst/DATA/log.txt" + id = (known after apply) } # local_file.RestartCC will be created + resource "local_file" "RestartCC" { + content = (sensitive value) + content_base64sha256 = (known after apply) + content_base64sha512 = (known after apply) + content_md5 = (known after apply) + content_sha1 = (known after apply) + content_sha256 = (known after apply) + content_sha512 = (known after apply) + directory_permission = "0777" + file_permission = "0777" + filename = "c:/temp/xdinst/DATA/RestartCC.ps1" + id = (known after apply) } # null_resource.CallRebootScriptOnCC1 will be created + resource "null_resource" "CallRebootScriptOnCC1" { + id = (known after apply) } # null_resource.CallRebootScriptOnCC2 will be created + resource "null_resource" "CallRebootScriptOnCC2" { + id = (known after apply) } # null_resource.CallRequiredScriptsOnCC1 will be created + resource "null_resource" "CallRequiredScriptsOnCC1" { + id = (known after apply) } # null_resource.CallRequiredScriptsOnCC2 will be created + resource "null_resource" "CallRequiredScriptsOnCC2" { + id = (known after apply) } # null_resource.ExecuteInstallPoSHSDKOnAVM will be created + resource "null_resource" "ExecuteInstallPoSHSDKOnAVM" { + id = (known after apply) } # null_resource.UploadRequiredComponentsToCC1 will be created + resource "null_resource" "UploadRequiredComponentsToCC1" { + id = (known after apply) } # null_resource.UploadRequiredComponentsToCC2 will be created + resource "null_resource" "UploadRequiredComponentsToCC2" { + id = (known after apply) } # terraform_data.ExecuteCreateValidCWCOnAVM will be created + resource "terraform_data" "ExecuteCreateValidCWCOnAVM" { + id = (known after apply) } # terraform_data.GetBT will be created + resource "terraform_data" "GetBT" { + id = (known after apply) } # terraform_data.ResourceLocation will be created + resource "terraform_data" "ResourceLocation" { + id = (known after apply) } # terraform_data.SiteID will be created + resource "terraform_data" "SiteID" { + id = (known after apply) } # terraform_data.ZoneID will be created + resource "terraform_data" "ZoneID" { + id = (known after apply) } # terraform_data.ZoneID2 will be created + resource "terraform_data" "ZoneID2" { + id = (known after apply) } # time_sleep.wait_1800_seconds_CC1 will be created + resource "time_sleep" "wait_1800_seconds_CC1" { + create_duration = "1800s" + id = (known after apply) } # time_sleep.wait_1800_seconds_CC2 will be created + resource "time_sleep" "wait_1800_seconds_CC2" { + create_duration = "1800s" + id = (known after apply) } # time_sleep.wait_60_seconds will be created + resource "time_sleep" "wait_60_seconds" { + create_duration = "60s" + id = (known after apply) } # time_sleep.wait_900_seconds will be created + resource "time_sleep" "wait_900_seconds" { + create_duration = "900s" + id = (known after apply) } Plan: 30 to add, 0 to change, 0 to destroy. Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now. PS C:\TACG\_CCOnGCP\_CCOnGCP-Install> terraform apply data.google_compute_address.IPOfCC2: Reading... data.google_compute_address.IPOfCC1: Reading... data.google_compute_address.IPOfCC1: Read complete after 0s [id=projects/tacg-gcp-XXXXXXXXXX/regions/europe-west3/addresses/internal-ip-cc1] data.google_compute_address.IPOfCC2: Read complete after 0s [id=projects/tacg-gcp-XXXXXXXXXX/regions/europe-west3/addresses/internal-ip-cc2] Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # local_file.CWC-Configuration will be created + resource "local_file" "CWC-Configuration" { + content = (sensitive value) + content_base64sha256 = (known after apply) + content_base64sha512 = (known after apply) + content_md5 = (known after apply) + content_sha1 = (known after apply) + content_sha256 = (known after apply) + content_sha512 = (known after apply) + directory_permission = "0777" + file_permission = "0777" + filename = "c:/temp/xdinst/DATA/cwc.json" + id = (known after apply) } # local_file.CreateRLScript will be created + resource "local_file" "CreateRLScript" { + content = (sensitive value) + content_base64sha256 = (known after apply) + content_base64sha512 = (known after apply) + content_md5 = (known after apply) + content_sha1 = (known after apply) + content_sha256 = (known after apply) + content_sha512 = (known after apply) + directory_permission = "0777" + file_permission = "0777" + filename = "c:/temp/xdinst/DATA/CreateRL.ps1" + id = (known after apply) } # local_file.CreateValidCWCOnAVM will be created + resource "local_file" "CreateValidCWCOnAVM" { + content = (sensitive value) + content_base64sha256 = (known after apply) + content_base64sha512 = (known after apply) + content_md5 = (known after apply) + content_sha1 = (known after apply) + content_sha256 = (known after apply) + content_sha512 = (known after apply) + directory_permission = "0777" + file_permission = "0777" + filename = "c:/temp/xdinst/DATA/CreateValidCWCOnAVM.ps1" + id = (known after apply) } # local_file.GetBearerToken will be created + resource "local_file" "GetBearerToken" { + content = (sensitive value) + content_base64sha256 = (known after apply) + content_base64sha512 = (known after apply) + content_md5 = (known after apply) + content_sha1 = (known after apply) + content_sha256 = (known after apply) + content_sha512 = (known after apply) + directory_permission = "0777" + file_permission = "0777" + filename = "c:/temp/xdinst/DATA/GetBT.ps1" + id = (known after apply) } # local_file.GetSiteIDScript will be created + resource "local_file" "GetSiteIDScript" { + content = (sensitive value) + content_base64sha256 = (known after apply) + content_base64sha512 = (known after apply) + content_md5 = (known after apply) + content_sha1 = (known after apply) + content_sha256 = (known after apply) + content_sha512 = (known after apply) + directory_permission = "0777" + file_permission = "0777" + filename = "c:/temp/xdinst/DATA/GetSiteID.ps1" + id = (known after apply) } # local_file.GetZoneIDScript will be created + resource "local_file" "GetZoneIDScript" { + content = (sensitive value) + content_base64sha256 = (known after apply) + content_base64sha512 = (known after apply) + content_md5 = (known after apply) + content_sha1 = (known after apply) + content_sha256 = (known after apply) + content_sha512 = (known after apply) + directory_permission = "0777" + file_permission = "0777" + filename = "c:/temp/xdinst/DATA/GetZoneID.ps1" + id = (known after apply) } # local_file.InstallCWCOnCC will be created + resource "local_file" "InstallCWCOnCC" { + content = (sensitive value) + content_base64sha256 = (known after apply) + content_base64sha512 = (known after apply) + content_md5 = (known after apply) + content_sha1 = (known after apply) + content_sha256 = (known after apply) + content_sha512 = (known after apply) + directory_permission = "0777" + file_permission = "0777" + filename = "c:/temp/xdinst/DATA/InstallCWCOnCC.ps1" + id = (known after apply) } # local_file.InstallPoSHSDKOnAVM will be created + resource "local_file" "InstallPoSHSDKOnAVM" { + content = (sensitive value) + content_base64sha256 = (known after apply) + content_base64sha512 = (known after apply) + content_md5 = (known after apply) + content_sha1 = (known after apply) + content_sha256 = (known after apply) + content_sha512 = (known after apply) + directory_permission = "0777" + file_permission = "0777" + filename = "c:/temp/xdinst/DATA/InstallPoSHSDKOnAVM.ps1" + id = (known after apply) } # local_file.Log will be created + resource "local_file" "Log" { + content = "Directory created." + content_base64sha256 = (known after apply) + content_base64sha512 = (known after apply) + content_md5 = (known after apply) + content_sha1 = (known after apply) + content_sha256 = (known after apply) + content_sha512 = (known after apply) + directory_permission = "0777" + file_permission = "0777" + filename = "c:/temp/xdinst/log.txt" + id = (known after apply) } # local_file.LogData will be created + resource "local_file" "LogData" { + content = "Directory created." + content_base64sha256 = (known after apply) + content_base64sha512 = (known after apply) + content_md5 = (known after apply) + content_sha1 = (known after apply) + content_sha256 = (known after apply) + content_sha512 = (known after apply) + directory_permission = "0777" + file_permission = "0777" + filename = "c:/temp/xdinst/DATA/log.txt" + id = (known after apply) } # null_resource.CallRequiredScriptsOnCC1 will be created + resource "null_resource" "CallRequiredScriptsOnCC1" { + id = (known after apply) } # null_resource.CallRequiredScriptsOnCC2 will be created + resource "null_resource" "CallRequiredScriptsOnCC2" { + id = (known after apply) } # null_resource.ExecuteInstallPoSHSDKOnAVM will be created + resource "null_resource" "ExecuteInstallPoSHSDKOnAVM" { + id = (known after apply) } # null_resource.UploadRequiredComponentsToCC1 will be created + resource "null_resource" "UploadRequiredComponentsToCC1" { + id = (known after apply) } # null_resource.UploadRequiredComponentsToCC2 will be created + resource "null_resource" "UploadRequiredComponentsToCC2" { + id = (known after apply) } # terraform_data.ExecuteCreateValidCWCOnAVM will be created + resource "terraform_data" "ExecuteCreateValidCWCOnAVM" { + id = (known after apply) } # terraform_data.GetBT will be created + resource "terraform_data" "GetBT" { + id = (known after apply) } # terraform_data.ResourceLocation will be created + resource "terraform_data" "ResourceLocation" { + id = (known after apply) } # terraform_data.SiteID will be created + resource "terraform_data" "SiteID" { + id = (known after apply) } # terraform_data.ZoneID2 will be created + resource "terraform_data" "ZoneID2" { + id = (known after apply) } # time_sleep.wait_60_seconds will be created + resource "time_sleep" "wait_60_seconds" { + create_duration = "60s" + id = (known after apply) } # time_sleep.wait_900_seconds will be created + resource "time_sleep" "wait_900_seconds" { + create_duration = "900s" + id = (known after apply) } # local_file.InstallCWCOnCC will be created + resource "local_file" "InstallCWCOnCC" { + content = (sensitive value) + content_base64sha256 = (known after apply) + content_base64sha512 = (known after apply) + content_md5 = (known after apply) + content_sha1 = (known after apply) + content_sha256 = (known after apply) + content_sha512 = (known after apply) + directory_permission = "0777" + file_permission = "0777" + filename = "c:/temp/xdinst/DATA/InstallCWCOnCC.ps1" + id = (known after apply) } # local_file.Log will be created + resource "local_file" "Log" { + content = "Directory created." + content_base64sha256 = (known after apply) + content_base64sha512 = (known after apply) + content_md5 = (known after apply) + content_sha1 = (known after apply) + content_sha256 = (known after apply) + content_sha512 = (known after apply) + directory_permission = "0777" + file_permission = "0777" + filename = "c:/temp/xdinst/log.txt" + id = (known after apply) } # local_file.LogData will be created + resource "local_file" "LogData" { + content = "Directory created." + content_base64sha256 = (known after apply) + content_base64sha512 = (known after apply) + content_md5 = (known after apply) + content_sha1 = (known after apply) + content_sha256 = (known after apply) + content_sha512 = (known after apply) + directory_permission = "0777" + file_permission = "0777" + filename = "c:/temp/xdinst/DATA/log.txt" + id = (known after apply) } # null_resource.CallRequiredScriptsOnCC1 will be created + resource "null_resource" "CallRequiredScriptsOnCC1" { + id = (known after apply) } # null_resource.CallRequiredScriptsOnCC2 will be created + resource "null_resource" "CallRequiredScriptsOnCC2" { + id = (known after apply) } # null_resource.UploadRequiredComponentsToCC1 will be created + resource "null_resource" "UploadRequiredComponentsToCC1" { + id = (known after apply) } # null_resource.UploadRequiredComponentsToCC2 will be created + resource "null_resource" "UploadRequiredComponentsToCC2" { + id = (known after apply) } Plan: 30 to add, 0 to change, 0 to destroy. Do you want to perform these actions? Terraform will perform the actions described above. Only 'yes' will be accepted to approve. Enter a value: yes null_resource.ExecuteInstallPoSHSDKOnAVM: Creating... null_resource.ExecuteInstallPoSHSDKOnAVM: Provisioning with 'local-exec'... null_resource.ExecuteInstallPoSHSDKOnAVM (local-exec): Executing: ["PowerShell" "-Command" " c:/temp/xdinst/DATA/InstallPoSHSDKOnAVM.ps1"] local_file.GetBearerToken: Creating... local_file.Log: Creating... local_file.InstallPoSHSDKOnAVM: Creating... local_file.Log: Creation complete after 0s [id=d725ce92ca8335439a5d83acf47f3ca2c957a515] local_file.CWC-Configuration: Creating... local_file.InstallPoSHSDKOnAVM: Creation complete after 0s [id=e384e2f2f26dd62c79ac8e6f0c25b98c1a82008c] local_file.GetBearerToken: Creation complete after 0s [id=d97ec7cb0f4a1247b43864df53c69c7956507eb5] local_file.CWC-Configuration: Creation complete after 0s [id=0257d2b92f197fa4d043b6cd4c4959be284dddc2] local_file.LogData: Creating... local_file.LogData: Creation complete after 0s [id=d725ce92ca8335439a5d83acf47f3ca2c957a515] null_resource.ExecuteInstallPoSHSDKOnAVM: Still creating... [10s elapsed] null_resource.ExecuteInstallPoSHSDKOnAVM: Still creating... [20s elapsed] ... ** Output shortened ** ... null_resource.ExecuteInstallPoSHSDKOnAVM: Still creating... [3m50s elapsed] null_resource.ExecuteInstallPoSHSDKOnAVM: Still creating... [4m0s elapsed] null_resource.ExecuteInstallPoSHSDKOnAVM: Creation complete after 4m4s [id=963532241712303576] terraform_data.GetBT: Creating... terraform_data.GetBT: Provisioning with 'local-exec'... terraform_data.GetBT (local-exec): Executing: ["PowerShell" "-File" "c:/temp/xdinst/DATA/GetBT.ps1"] terraform_data.GetBT: Creation complete after 10s [id=60c49067-1da2-caf5-1893-d4978527619f] local_file.CreateRLScript: Creating... local_file.CreateRLScript: Creation complete after 0s [id=e71a25d1aa907068420b19c3c61b58f54e256512] terraform_data.ResourceLocation: Creating... terraform_data.ResourceLocation: Provisioning with 'local-exec'... terraform_data.ResourceLocation (local-exec): Executing: ["PowerShell" "-File" "c:/temp/xdinst/DATA/CreateRL.ps1"] terraform_data.ResourceLocation: Creation complete after 1s [id=3d058474-6e42-69d2-a211-ce552bef2775] time_sleep.wait_900_seconds: Creating... time_sleep.wait_60_seconds: Creating... time_sleep.wait_900_seconds: Still creating... [10s elapsed] time_sleep.wait_60_seconds: Still creating... [10s elapsed] time_sleep.wait_900_seconds: Still creating... [20s elapsed] time_sleep.wait_60_seconds: Still creating... [20s elapsed] time_sleep.wait_900_seconds: Still creating... [30s elapsed] time_sleep.wait_60_seconds: Still creating... [30s elapsed] time_sleep.wait_900_seconds: Still creating... [40s elapsed] time_sleep.wait_60_seconds: Still creating... [40s elapsed] time_sleep.wait_900_seconds: Still creating... [50s elapsed] time_sleep.wait_60_seconds: Still creating... [51s elapsed] time_sleep.wait_900_seconds: Still creating... [1m0s elapsed] time_sleep.wait_60_seconds: Still creating... [1m1s elapsed] time_sleep.wait_60_seconds: Creation complete after 1m1s [id=2024-03-27T14:27:37Z] local_file.GetSiteIDScript: Creating... local_file.GetSiteIDScript: Creation complete after 0s [id=9d4d1c344ddddcdeacfbbf4e84525c1cbc6a8059] terraform_data.SiteID: Creating... terraform_data.SiteID: Provisioning with 'local-exec'... terraform_data.SiteID (local-exec): Executing: ["PowerShell" "-File" "c:/temp/xdinst/DATA/GetSiteID.ps1"] terraform_data.SiteID: Creation complete after 1s [id=6a5d6c03-cfa2-8b27-0ca4-c144fb1b44ee] time_sleep.wait_900_seconds: Still creating... [1m11s elapsed] time_sleep.wait_900_seconds: Still creating... [1m21s elapsed] time_sleep.wait_900_seconds: Still creating... [1m31s elapsed] ... ** Output shortened ** ... time_sleep.wait_900_seconds: Still creating... [14m41s elapsed] time_sleep.wait_900_seconds: Still creating... [14m51s elapsed] time_sleep.wait_900_seconds: Creation complete after 15m0s [id=2024-03-27T14:41:37Z] local_file.GetZoneIDScript: Creating... local_file.GetZoneIDScript: Creation complete after 0s [id=fe4217fc0b2887ee7d949cbbd15e3f338ce5ec13] terraform_data.ZoneID: Creating... terraform_data.ZoneID: Provisioning with 'local-exec'... terraform_data.ZoneID (local-exec): Executing: ["PowerShell" "-File" "c:/temp/xdinst/DATA/GetZoneID.ps1"] terraform_data.ZoneID: Creation complete after 1s [id=605cf1d3-df21-50fd-eea4-2b378bbc75de] terraform_data.ZoneID2: Creating... terraform_data.ZoneID2: Provisioning with 'local-exec'... terraform_data.ZoneID2 (local-exec): Executing: ["PowerShell" "-File" "c:/temp/xdinst/DATA/GetZoneID.ps1"] terraform_data.ZoneID2: Creation complete after 1s [id=8407eaeb-8b82-c81f-8f18-91e1c17c856e] local_file.CreateValidCWCOnAVM: Creating... local_file.CreateValidCWCOnAVM: Creation complete after 0s [id=8589dd5c009bc8ec60afe66f005270a5d6716790] terraform_data.ExecuteCreateValidCWCOnAVM: Creating... terraform_data.ExecuteCreateValidCWCOnAVM: Provisioning with 'local-exec'... terraform_data.ExecuteCreateValidCWCOnAVM (local-exec): Executing: ["PowerShell" "-File" "c:/temp/xdinst/DATA/CreateValidCWCOnAVM.ps1"] terraform_data.ExecuteCreateValidCWCOnAVM: Creation complete after 1s [id=fe29d6ab-b636-20c4-2546-36ee395f6ced] local_file.RestartCC: Creating... local_file.InstallCWCOnCC: Creating... local_file.Log: Creating... local_file.InstallCWCOnCC: Creation complete after 0s [id=83c2658962b3ca50411ecd6d7d3ab22958819dd1] local_file.RestartCC: Creation complete after 0s [id=c33d06fc1d6b6fec7b65e720320d1cbc71f02225] local_file.Log: Creation complete after 0s [id=d725ce92ca8335439a5d83acf47f3ca2c957a515] local_file.LogData: Creating... local_file.LogData: Creation complete after 0s [id=d725ce92ca8335439a5d83acf47f3ca2c957a515] null_resource.UploadRequiredComponentsToCC1: Creating... null_resource.UploadRequiredComponentsToCC1: Provisioning with 'file'... null_resource.UploadRequiredComponentsToCC2: Creating... null_resource.UploadRequiredComponentsToCC2: Provisioning with 'file'... #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>#< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>null_resource.UploadRequiredComponentsToCC1: Provisioning with 'file'... #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>#< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>#< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>null_resource.UploadRequiredComponentsToCC2: Provisioning with 'file'... #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>null_resource.UploadRequiredComponentsToCC1: Provisioning with 'file'... #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>#< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>#< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>null_resource.UploadRequiredComponentsToCC2: Provisioning with 'file'... #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>null_resource.UploadRequiredComponentsToCC1: Provisioning with 'file'... #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>#< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>#< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>null_resource.UploadRequiredComponentsToCC2: Provisioning with 'file'... #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>null_resource.UploadRequiredComponentsToCC1: Provisioning with 'file'... null_resource.UploadRequiredComponentsToCC1: Still creating... [10s elapsed] null_resource.UploadRequiredComponentsToCC2: Still creating... [10s elapsed] #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>#< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>#< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>null_resource.UploadRequiredComponentsToCC2: Provisioning with 'file'... #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>null_resource.UploadRequiredComponentsToCC1: Provisioning with 'file'... #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>#< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>#< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>null_resource.UploadRequiredComponentsToCC2: Provisioning with 'file'... #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>null_resource.UploadRequiredComponentsToCC1: Creation complete after 13s [id=3185822947916041730] null_resource.CallRequiredScriptsOnCC1: Creating... null_resource.CallRequiredScriptsOnCC1: Provisioning with 'remote-exec'... null_resource.CallRequiredScriptsOnCC1 (remote-exec): Connecting to remote host via WinRM... null_resource.CallRequiredScriptsOnCC1 (remote-exec): Host: 10.156.0.5 null_resource.CallRequiredScriptsOnCC1 (remote-exec): Port: 5985 null_resource.CallRequiredScriptsOnCC1 (remote-exec): User: administrator null_resource.CallRequiredScriptsOnCC1 (remote-exec): Password: true null_resource.CallRequiredScriptsOnCC1 (remote-exec): HTTPS: false null_resource.CallRequiredScriptsOnCC1 (remote-exec): Insecure: false null_resource.CallRequiredScriptsOnCC1 (remote-exec): NTLM: false null_resource.CallRequiredScriptsOnCC1 (remote-exec): CACert: false null_resource.CallRequiredScriptsOnCC1 (remote-exec): Connected! #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>#< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>#< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>null_resource.UploadRequiredComponentsToCC2: Creation complete after 15s [id=979308782134187951] null_resource.CallRequiredScriptsOnCC2: Creating... null_resource.CallRequiredScriptsOnCC2: Provisioning with 'remote-exec'... null_resource.CallRequiredScriptsOnCC2 (remote-exec): Connecting to remote host via WinRM... null_resource.CallRequiredScriptsOnCC2 (remote-exec): Host: 10.156.0.6 null_resource.CallRequiredScriptsOnCC2 (remote-exec): Port: 5985 null_resource.CallRequiredScriptsOnCC2 (remote-exec): User: administrator null_resource.CallRequiredScriptsOnCC2 (remote-exec): Password: true null_resource.CallRequiredScriptsOnCC2 (remote-exec): HTTPS: false null_resource.CallRequiredScriptsOnCC2 (remote-exec): Insecure: false null_resource.CallRequiredScriptsOnCC2 (remote-exec): NTLM: false null_resource.CallRequiredScriptsOnCC2 (remote-exec): CACert: false null_resource.CallRequiredScriptsOnCC2 (remote-exec): Connected! #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>#< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs> null_resource.CallRequiredScriptsOnCC1 (remote-exec): C:\Users\Administrator>powershell -File c:/temp/xdinst/DATA/InstallCWCOnCC.ps1 #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs> null_resource.CallRequiredScriptsOnCC2 (remote-exec): C:\Users\Administrator.TACG-GCP-TF-CC2>powershell -File c:/temp/xdinst/DATA/InstallCWCOnCC.ps1 null_resource.CallRequiredScriptsOnCC1: Still creating... [10s elapsed] null_resource.CallRequiredScriptsOnCC2: Still creating... [10s elapsed] null_resource.CallRequiredScriptsOnCC1: Still creating... [20s elapsed] null_resource.CallRequiredScriptsOnCC2: Still creating... [20s elapsed] null_resource.CallRequiredScriptsOnCC1: Still creating... [30s elapsed] null_resource.CallRequiredScriptsOnCC2: Still creating... [30s elapsed] null_resource.CallRequiredScriptsOnCC1: Still creating... [40s elapsed] null_resource.CallRequiredScriptsOnCC2: Still creating... [40s elapsed] null_resource.CallRequiredScriptsOnCC1: Still creating... [50s elapsed] null_resource.CallRequiredScriptsOnCC2: Still creating... [50s elapsed] null_resource.CallRequiredScriptsOnCC1: Still creating... [1m0s elapsed] null_resource.CallRequiredScriptsOnCC2: Still creating... [1m0s elapsed] null_resource.CallRequiredScriptsOnCC1: Still creating... [1m10s elapsed] null_resource.CallRequiredScriptsOnCC2: Still creating... [1m10s elapsed] null_resource.CallRequiredScriptsOnCC1: Still creating... [1m20s elapsed] null_resource.CallRequiredScriptsOnCC2: Still creating... [1m20s elapsed] null_resource.CallRequiredScriptsOnCC1: Still creating... [1m30s elapsed] null_resource.CallRequiredScriptsOnCC2: Still creating... [1m30s elapsed] #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj><Obj S="progress" RefId="1"><TNRef RefId="0" /><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>null_resource.CallRequiredScriptsOnCC1: Still creating... [1m40s elapsed] #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj><Obj S="progress" RefId="1"><TNRef RefId="0" /><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>#< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>null_resource.CallRequiredScriptsOnCC1: Creation complete after 1m41s [id=1683817289421392956] time_sleep.wait_1800_seconds_CC1: Creating... #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>null_resource.CallRequiredScriptsOnCC2: Creation complete after 1m39s [id=5495494345692724821] time_sleep.wait_1800_seconds_CC2: Creating... time_sleep.wait_1800_seconds_CC1: Still creating... [10s elapsed] time_sleep.wait_1800_seconds_CC2: Still creating... [11s elapsed] time_sleep.wait_1800_seconds_CC1: Still creating... [20s elapsed] time_sleep.wait_1800_seconds_CC2: Still creating... [21s elapsed] time_sleep.wait_1800_seconds_CC1: Still creating... [30s elapsed] time_sleep.wait_1800_seconds_CC2: Still creating... [31s elapsed] time_sleep.wait_1800_seconds_CC1: Still creating... [40s elapsed] time_sleep.wait_1800_seconds_CC2: Still creating... [41s elapsed] time_sleep.wait_1800_seconds_CC1: Still creating... [50s elapsed] time_sleep.wait_1800_seconds_CC2: Still creating... [51s elapsed] ... ** Output shortened ** ... time_sleep.wait_1800_seconds_CC2: Still creating... [29m42s elapsed] time_sleep.wait_1800_seconds_CC1: Still creating... [29m52s elapsed] time_sleep.wait_1800_seconds_CC2: Still creating... [29m52s elapsed] time_sleep.wait_1800_seconds_CC1: Creation complete after 30m0s [id=2024-03-28T10:41:32Z] null_resource.CallRebootScriptOnCC1: Creating... null_resource.CallRebootScriptOnCC1: Provisioning with 'remote-exec'... null_resource.CallRebootScriptOnCC1 (remote-exec): Connecting to remote host via WinRM... null_resource.CallRebootScriptOnCC1 (remote-exec): Host: 10.156.0.5 null_resource.CallRebootScriptOnCC1 (remote-exec): Port: 5985 null_resource.CallRebootScriptOnCC1 (remote-exec): User: administrator null_resource.CallRebootScriptOnCC1 (remote-exec): Password: true null_resource.CallRebootScriptOnCC1 (remote-exec): HTTPS: false null_resource.CallRebootScriptOnCC1 (remote-exec): Insecure: false null_resource.CallRebootScriptOnCC1 (remote-exec): NTLM: false null_resource.CallRebootScriptOnCC1 (remote-exec): CACert: false time_sleep.wait_1800_seconds_CC2: Creation complete after 30m1s [id=2024-03-28T10:41:32Z] null_resource.CallRebootScriptOnCC2: Creating... null_resource.CallRebootScriptOnCC2: Provisioning with 'remote-exec'... null_resource.CallRebootScriptOnCC2 (remote-exec): Connecting to remote host via WinRM... null_resource.CallRebootScriptOnCC2 (remote-exec): Host: 10.156.0.6 null_resource.CallRebootScriptOnCC2 (remote-exec): Port: 5985 null_resource.CallRebootScriptOnCC2 (remote-exec): User: administrator null_resource.CallRebootScriptOnCC2 (remote-exec): Password: true null_resource.CallRebootScriptOnCC2 (remote-exec): HTTPS: false null_resource.CallRebootScriptOnCC2 (remote-exec): Insecure: false null_resource.CallRebootScriptOnCC2 (remote-exec): NTLM: false null_resource.CallRebootScriptOnCC2 (remote-exec): CACert: false null_resource.CallRebootScriptOnCC1 (remote-exec): Connected! null_resource.CallRebootScriptOnCC2 (remote-exec): Connected! #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>#< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>#< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs> null_resource.CallRebootScriptOnCC1 (remote-exec): C:\Users\Administrator>powershell -File c:/temp/xdinst/DATA/RebootCC.ps1 null_resource.CallRebootScriptOnCC1 (remote-exec): Windows PowerShell null_resource.CallRebootScriptOnCC1 (remote-exec): Copyright (C) Microsoft Corporation. All rights reserved. null_resource.CallRebootScriptOnCC1 (remote-exec): Install the latest PowerShell for new features and improvements! https://aka.ms/PSWindows #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>null_resource.CallRebootScriptOnCC2: Still creating... [10s elapsed] ... ** Output shortened ** ... Apply complete! Resources: 30 added, 0 changed, 0 destroyed. PS C:\TACG\_CCOnGCP\_CCOnGCP-Install> 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. 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 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: .tg {border-collapse:collapse;border-spacing:0;width:100%} .tg .tg-5jvq{background-color:#0000b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px; text-align:left;vertical-align:top} Import-Module "C:\TACG\Supportability Pack\Tools\Scout\Current\Utilities\Citrix.GroupPolicy.commands.psm1" -force new-psdrive -name LocalFarmGpo -psprovider CitrixGroupPolicy -controller localhost \ Get-PSDrive cd LocalFarmGpo: Get-CtxGroupPolicyConfiguration Type: User ProfileLoadTimeMonitoring_Threshold ICALatencyMonitoring_Enable ICALatencyMonitoring_Period ICALatencyMonitoring_Threshold EnableLossless ProGraphics FRVideos_Part FRVideosPath_Part FRStartMenu_Part FRStartMenuPath_Part FRSearches_Part FRSearchesPath_Part FRSavedGames_Part FRSavedGamesPath_Part FRPictures_Part FRPicturesPath_Part FRMusic_Part FRMusicPath_Part FRLinks_Part FRLinksPath_Part FRFavorites_Part FRFavoritesPath_Part FRDownloads_Part FRDownloadsPath_Part FRDocuments_Part FRDocumentsPath_Part FRDesktop_Part FRDesktopPath_Part FRContacts_Part FRContactsPath_Part FRAdminAccess_Part FRIncDomainName_Part FRAppData_Part FRAppDataPath_Part StorefrontAccountsList AllowFidoRedirection AllowWIARedirection ClientClipboardWriteAllowedFormats ClipboardRedirection ClipboardSelectionUpdateMode DesktopLaunchForNonAdmins DragDrop LimitClipboardTransferC2H LimitClipboardTransferH2C LossTolerantModeAvailable NonPublishedProgramLaunching PrimarySelectionUpdateMode ReadonlyClipboard RestrictClientClipboardWrite RestrictSessionClipboardWrite SessionClipboardWriteAllowedFormats FramesPerSecond PreferredColorDepthForSimpleGraphics VisualQuality ExtraColorCompression ExtraColorCompressionThreshold LossyCompressionLevel LossyCompressionThreshold ProgressiveHeavyweightCompression MinimumAdaptiveDisplayJpegQuality MovingImageCompressionConfiguration ProgressiveCompressionLevel ProgressiveCompressionThreshold TargetedMinimumFramesPerSecond ClientUsbDeviceOptimizationRules UsbConnectExistingDevices UsbConnectNewDevices UsbDeviceRedirection UsbDeviceRedirectionRules USBDeviceRulesV2 UsbPlugAndPlayRedirection TwainCompressionLevel TwainRedirection LocalTimeEstimation RestoreServerTime SessionTimeZone EnableSessionWatermark WatermarkStyle WatermarkTransparency WatermarkCustomText WatermarkIncludeClientIPAddress WatermarkIncludeConnectTime WatermarkIncludeLogonUsername WatermarkIncludeVDAHostName WatermarkIncludeVDAIPAddress EnableRemotePCDisconnectTimer SessionConnectionTimer SessionConnectionTimerInterval SessionDisconnectTimer SessionDisconnectTimerInterval SessionIdleTimer SessionIdleTimerInterval LossTolerantThresholds EnableServerConnectionTimer EnableServerDisconnectionTimer EnableServerIdleTimer ServerConnectionTimerInterval ServerDisconnectionTimerInterval ServerIdleTimerInterval MinimumEncryptionLevel AutoCreationEventLogPreference ClientPrinterRedirection DefaultClientPrinter PrinterAssignments SessionPrinters WaitForPrintersToBeCreated UpsPrintStreamInputBandwidthLimit DPILimit EMFProcessingMode ImageCompressionLimit UniversalPrintingPreviewPreference UPDCompressionDefaults InboxDriverAutoInstallation UniversalDriverPriority UniversalPrintDriverUsage AutoCreatePDFPrinter ClientPrinterAutoCreation ClientPrinterNames DirectConnectionsToPrintServers GenericUniversalPrinterAutoCreation PrinterDriverMappings PrinterPropertiesRetention ClientComPortRedirection ClientComPortsAutoConnection ClientLptPortRedirection ClientLptPortsAutoConnection MaxSpeexQuality MSTeamsRedirection MultimediaOptimization UseGPUForMultimediaOptimization VideoLoadManagement VideoQuality WebBrowserRedirectionAcl WebBrowserRedirectionAuthenticationSites WebBrowserRedirectionBlacklist WebBrowserRedirectionIwaSupport WebBrowserRedirectionProxy WebBrowserRedirectionProxyAuth MultiStream AutoKeyboardPopUp ComboboxRemoting MobileDesktop TabletModeToggle ClientKeyboardLayoutSyncAndIME EnableUnicodeKeyboardLayoutMapping HideKeyboardLayoutSwitchPopupMessageBox AllowVisuallyLosslessCompression DisplayLosslessIndicator OptimizeFor3dWorkload ScreenSharing UseHardwareEncodingForVideoCodec UseVideoCodecForCompression EnableFramehawkDisplayChannel AllowFileDownload AllowFileTransfer AllowFileUpload AsynchronousWrites AutoConnectDrives ClientDriveLetterPreservation ClientDriveRedirection ClientFixedDrives ClientFloppyDrives ClientNetworkDrives ClientOpticalDrives ClientRemoveableDrives HostToClientRedirection ReadOnlyMappedDrive SpecialFolderRedirection AeroRedirection DesktopWallpaper GraphicsQuality MenuAnimation WindowContentsVisibleWhileDragging AllowLocationServices AllowBidirectionalContentRedirection BidirectionalRedirectionConfig ClientURLs VDAURLs AudioBandwidthLimit AudioBandwidthPercent ClipboardBandwidthLimit ClipboardBandwidthPercent ComPortBandwidthLimit ComPortBandwidthPercent FileRedirectionBandwidthLimit FileRedirectionBandwidthPercent HDXMultimediaBandwidthLimit HDXMultimediaBandwidthPercent LptBandwidthLimit LptBandwidthLimitPercent OverallBandwidthLimit PrinterBandwidthLimit PrinterBandwidthPercent TwainBandwidthLimit TwainBandwidthPercent USBBandwidthLimit USBBandwidthPercent AllowRtpAudio AudioPlugNPlay AudioQuality ClientAudioRedirection EnableAdaptiveAudio MicrophoneRedirection FlashAcceleration FlashBackwardsCompatibility FlashDefaultBehavior FlashEventLogging FlashIntelligentFallback FlashLatencyThreshold FlashServerSideContentFetchingWhitelist FlashUrlColorList FlashUrlCompatibilityList HDXFlashLoadManagement HDXFlashLoadManagementErrorSwf Type: Computer WemCloudConnectorList VirtualLoopbackPrograms VirtualLoopbackSupport EnableAutoUpdateOfControllers AppFailureExclusionList EnableProcessMonitoring EnableResourceMonitoring EnableWorkstationVDAFaultMonitoring SelectedFailureLevel CPUUsageMonitoring_Enable CPUUsageMonitoring_Period CPUUsageMonitoring_Threshold VdcPolicyEnable EnableClipboardMetadataCollection EnableVdaDiagnosticsCollection XenAppOptimizationDefinitionPathData XenAppOptimizationEnabled ExclusionList_Part IncludeListRegistry_Part LastKnownGoodRegistry DefaultExclusionList ExclusionDefaultReg01 ExclusionDefaultReg02 ExclusionDefaultReg03 PSAlwaysCache PSAlwaysCache_Part PSEnabled PSForFoldersEnabled PSForPendingAreaEnabled PSPendingLockTimeout PSUserGroups_Part StreamingExclusionList_Part ApplicationProfilesAutoMigration DeleteCachedProfilesOnLogoff LocalProfileConflictHandling_Part MigrateWindowsProfilesToUserStore_Part ProfileDeleteDelay_Part TemplateProfileIsMandatory TemplateProfileOverridesLocalProfile TemplateProfileOverridesRoamingProfile TemplateProfilePath DisableConcurrentAccessToOneDriveContainer DisableConcurrentAccessToProfileContainer EnableVHDAutoExtend EnableVHDDiskCompaction GroupsToAccessProfileContainer_Part PreventLoginWhenMountFailed_Part ProfileContainerExclusionListDir_Part ProfileContainerExclusionListFile_Part ProfileContainerInclusionListDir_Part ProfileContainerInclusionListFile_Part ProfileContainerLocalCache DebugFilePath_Part DebugMode LogLevel_ActiveDirectoryActions LogLevel_FileSystemActions LogLevel_FileSystemNotification LogLevel_Information LogLevel_Logoff LogLevel_Logon LogLevel_PolicyUserLogon LogLevel_RegistryActions LogLevel_RegistryDifference LogLevel_UserName LogLevel_Warnings MaxLogSize_Part LargeFileHandlingList_Part LogonExclusionCheck_Part AccelerateFolderMirroring MirrorFoldersList_Part ProfileContainer_Part SyncDirList_Part SyncFileList_Part ExclusionListSyncDir_Part ExclusionListSyncFiles_Part DefaultExclusionListSyncDir ExclusionDefaultDir01 ExclusionDefaultDir02 ExclusionDefaultDir03 ExclusionDefaultDir04 ExclusionDefaultDir05 ExclusionDefaultDir06 ExclusionDefaultDir07 ExclusionDefaultDir08 ExclusionDefaultDir09 ExclusionDefaultDir10 ExclusionDefaultDir11 ExclusionDefaultDir12 ExclusionDefaultDir13 ExclusionDefaultDir14 ExclusionDefaultDir15 ExclusionDefaultDir16 ExclusionDefaultDir17 ExclusionDefaultDir18 ExclusionDefaultDir19 ExclusionDefaultDir20 ExclusionDefaultDir21 ExclusionDefaultDir22 ExclusionDefaultDir23 ExclusionDefaultDir24 ExclusionDefaultDir25 ExclusionDefaultDir26 ExclusionDefaultDir27 ExclusionDefaultDir28 ExclusionDefaultDir29 ExclusionDefaultDir30 SharedStoreFileExclusionList_Part SharedStoreFileInclusionList_Part SharedStoreProfileContainerFileSizeLimit_Part CPEnable CPMigrationFromBaseProfileToCPStore CPPathData CPSchemaPathData CPUserGroups_Part DATPath_Part ExcludedGroups_Part MigrateUserStore_Part OfflineSupport ProcessAdmins ProcessedGroups_Part PSMidSessionWriteBack PSMidSessionWriteBackReg PSMidSessionWriteBackSessionLock ServiceActive AppAccessControl_Part CEIPEnabled CredBasedAccessEnabled DisableDynamicConfig EnableVolumeReattach FreeRatio4Compaction_Part FSLogixProfileContainerSupport LoadRetries_Part LogoffRatherThanTempProfile MultiSiteReplication_Part NDefrag4Compaction NLogoffs4Compaction_Part OneDriveContainer_Part OrderedGroups_Part OutlookEdbBackupEnabled OutlookSearchRoamingConcurrentSession OutlookSearchRoamingConcurrentSession_Part OutlookSearchRoamingEnabled ProcessCookieFiles SyncGpoStateEnabled UserGroupLevelConfigEnabled UserStoreSelection_Part UwpAppsRoaming VhdAutoExpansionIncrement_Part VhdAutoExpansionLimit_Part VhdAutoExpansionThreshold_Part VhdContainerCapacity_Part VhdStorePath_Part UplCustomizedUserLayerSizeInGb UplGroupsUsingCustomizedUserLayerSize UplRepositoryPath UplUserExclusions UplUserLayerSizeInGb ConcurrentLogonsTolerance CPUUsage CPUUsageExcludedProcessPriority DiskUsage MaximumNumberOfSessions MemoryUsage MemoryUsageBaseLoad ApplicationLaunchWaitTimeout HDXAdaptiveTransport HDXDirect HDXDirectMode HDXDirectPortRange IcaListenerPortNumber IcaListenerTimeout LogoffCheckerStartupDelay RemoteCredentialGuard RendezvousProtocol RendezvousProxy SecureHDX VdaUpgradeProxy VirtualChannelWhiteList VirtualChannelWhiteListLogging VirtualChannelWhiteListLogThrottling AcceptWebSocketsConnections WebSocketsPort WSTrustedOriginServerList SessionReliabilityConnections SessionReliabilityPort SessionReliabilityTimeout IdleTimerInterval LoadBalancedPrintServers PrintServersOutOfServiceThreshold UpcHttpConnectTimeout UpcHttpReceiveTimeout UpcHttpSendTimeout UpcSslCgpPort UpcSslCipherSuite UpcSslComplianceMode UpcSslEnable UpcSslFips UpcSslHttpsPort UpcSslProtocolVersion UpsCgpPort UpsEnable UpsHttpPort HTML5VideoRedirection MultimediaAcceleration MultimediaAccelerationDefaultBufferSize MultimediaAccelerationEnableCSF MultimediaAccelerationUseDefaultBufferSize MultimediaConferencing WebBrowserRedirection MultiPortPolicy MultiStreamAssignment MultiStreamPolicy RtpAudioPortRange UDPAudioOnServer AllowLocalAppAccess URLRedirectionBlackList URLRedirectionWhiteList IcaKeepAlives IcaKeepAliveTimeout DisplayDegradePreference DisplayDegradeUserNotification DisplayMemoryLimit DynamicPreview ImageCaching LegacyGraphicsMode MaximumColorDepth QueueingAndTossing FramehawkDisplayChannelPortRange PersistentCache EnhancedDesktopExperience IcaRoundTripCalculation IcaRoundTripCalculationInterval IcaRoundTripCalculationWhenIdle ACRTimeout AutoClientReconnect AutoClientReconnectAuthenticationRequired AutoClientReconnectLogging ReconnectionUiTransparencyLevel AppProtectionPostureCheck AdvanceWarningFrequency AdvanceWarningMessageTitle AdvanceWarningPeriod AgentTaskInterval FinalForceLogoffMessageBody FinalForceLogoffMessageTitle ForceLogoffGracePeriod ForceLogoffMessageTitle ImageProviderIntegrationEnabled RebootMessageBody Using these names allows the deployment of policies by using the Terraform provider. Please make sure you have configured the variables according to your needs. Caution: 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 .tg {border-collapse:collapse;border-spacing:0;width:100%} .tg .tg-5jvq{background-color:#0000b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px; text-align:left;vertical-align:top} PS C:\TACG\_CCOnGCP\_CCOnGCP-CCStuff> terraform init Initializing the backend... Initializing provider plugins... - Finding citrix/citrix versions matching ">= 0.5.4"... - Finding hashicorp/google versions matching ">= 5.21.0"... - Finding latest version of hashicorp/time... - Finding latest version of hashicorp/local... - Installing citrix/citrix v0.5.4... - Installed citrix/citrix v0.5.4 (signed by a HashiCorp partner, key ID 25D62DD8407EA386) - Installing hashicorp/google v5.23.0... - Installed hashicorp/google v5.23.0 (signed by HashiCorp) - Installing hashicorp/time v0.11.1... - Installed hashicorp/time v0.11.1 (signed by HashiCorp) - Installing hashicorp/local v2.5.1... - Installed hashicorp/local v2.5.1 (signed by HashiCorp) Partner and community providers are signed by their developers. If you'd like to know more about provider signing, you can read about it here: https://www.terraform.io/docs/cli/plugins/signing.html Terraform has created a lock file .terraform.lock.hcl to record the provider selections it made above. Include this file in your version control repository so that Terraform can guarantee to make the same selections by default when you run "terraform init" in the future. Terraform has been successfully initialized! You may now begin working with Terraform. Try running "terraform plan" to see any changes that are required for your infrastructure. All Terraform commands should now work. If you ever set or change modules or backend configuration for Terraform, rerun this command to reinitialize your working directory. If you forget, other commands will detect it and remind you to do so if necessary. PS C:\TACG\_CCOnGCP\_CCOnGCP-CCStuff> terraform plan data.local_file.LoadZoneID: Reading... data.local_file.GCPCredentials: Reading... data.local_file.LoadZoneID: Read complete after 0s [id=fec7404d513834378890c62d7e34d9d692f11957] data.local_file.GCPCredentials: Read complete after 0s [id=e153c83ee291d1531ec30503aacbb705602bde95] data.google_compute_network.GCPVPC: Reading... data.google_compute_network.GCPSubnet: Reading... data.google_compute_network.GCPSubnet: Read complete after 0s [id=projects/tacg-gcp-XXXXXXXXXX/global/networks/default] data.google_compute_network.GCPVPC: Read complete after 0s [id=projects/tacg-gcp-XXXXXXXXXX/global/networks/default] Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # citrix_delivery_group.CreateDG will be created + resource "citrix_delivery_group" "CreateDG" { + associated_machine_catalogs = [ + { + machine_catalog = (known after apply) + machine_count = 1 }, ] + autoscale_settings = { + autoscale_enabled = true + disconnect_off_peak_idle_session_after_seconds = 0 + disconnect_peak_idle_session_after_seconds = 300 + log_off_off_peak_disconnected_session_after_seconds = 0 + log_off_peak_disconnected_session_after_seconds = 300 + off_peak_buffer_size_percent = 0 + off_peak_disconnect_action = "Nothing" + off_peak_disconnect_timeout_minutes = 0 + off_peak_extended_disconnect_action = "Nothing" + off_peak_extended_disconnect_timeout_minutes = 0 + off_peak_log_off_action = "Nothing" + peak_buffer_size_percent = 0 + peak_disconnect_action = "Nothing" + peak_disconnect_timeout_minutes = 0 + peak_extended_disconnect_action = "Nothing" + peak_extended_disconnect_timeout_minutes = 0 + peak_log_off_action = "Nothing" + power_off_delay_minutes = 30 + power_time_schemes = [ + { + days_of_week = [ + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", ] + display_name = "TACG-GCP-TF-AS-Weekdays" + peak_time_ranges = [ + "09:00-17:00", ] + pool_size_schedules = [ + { + pool_size = 1 + time_range = "09:00-17:00" }, ] + pool_using_percentage = false }, ] } + desktops = [ + { + description = "Terraform-based Delivery Group running on GCP" + enable_session_roaming = true + enabled = true + published_name = "DG-TF-TACG-GCP" + restricted_access_users = { + allow_list = [ + "TACG-GCP\\vdaallowed", ] } }, ] + id = (known after apply) + minimum_functional_level = "L7_20" + name = "DG-TF-TACG-GCP" + reboot_schedules = [ + { + days_in_week = [ + "Sunday", ] + frequency = "Weekly" + frequency_factor = 1 + ignore_maintenance_mode = true + name = "TACG-GCP-Reboot Schedule" + natural_reboot_schedule = false + reboot_duration_minutes = 0 + reboot_schedule_enabled = true + start_date = "2024-01-01" + start_time = "02:00" }, ] + restricted_access_users = { + allow_list = [ + "TACG-GCP\\vdaallowed", ] } + total_machines = (known after apply) } # citrix_gcp_hypervisor.CreateHypervisorConnection will be created + resource "citrix_gcp_hypervisor" "CreateHypervisorConnection" { + id = (known after apply) + name = "TACG-GCP-TF-HypConn" + service_account_credentials = (sensitive value) + service_account_id = "XXXXXXXXXX@XXXXXXXXXX.iam.gserviceaccount.com" + zone = "XXXXXXXX-XXXX-XXXX-XXXX-f6a1f864d69a" } # citrix_gcp_hypervisor_resource_pool.CreateHypervisorPool will be created + resource "citrix_gcp_hypervisor_resource_pool" "CreateHypervisorPool" { + hypervisor = (known after apply) + id = (known after apply) + name = "TACG-GCP-TF-HypConnPool" + project_name = "TACG-GCP" + region = "europe-west3" + subnets = [ + "default", ] + vpc = "default" } # citrix_machine_catalog.CreateMCSCatalog will be created + resource "citrix_machine_catalog" "CreateMCSCatalog" { + allocation_type = "Random" + description = "Terraform-based Machine Catalog" + id = (known after apply) + is_power_managed = true + is_remote_pc = false + minimum_functional_level = "L7_20" + name = "MC-TACG-GCP-TF" + provisioning_scheme = { + gcp_machine_config = { + master_image = "tacg-gcp-tf-wmi" + storage_type = "pd-standard" } + hypervisor = (known after apply) + hypervisor_resource_pool = (known after apply) + identity_type = "ActiveDirectory" + machine_account_creation_rules = { + naming_scheme = "TACG-GCP-W###" + naming_scheme_type = "Numeric" } + machine_domain_identity = { + domain = "gcp.the-austrian-citrix-guy.at" + domain_ou = "CN=Computers,DC=gcp,DC=the-austrian-citrix-guy,DC=at" + service_account = "Administrator" + service_account_password = (sensitive value) } + number_of_total_machines = 1 } + provisioning_type = "MCS" + session_support = "MultiSession" + zone = "XXXXXXXX-XXXX-XXXX-XXXX-f6a1f864d69a" } # time_sleep.wait_60_seconds will be created + resource "time_sleep" "wait_60_seconds" { + create_duration = "60s" + id = (known after apply) } # time_sleep.wait_60_seconds_1 will be created + resource "time_sleep" "wait_60_seconds_1" { + create_duration = "60s" + id = (known after apply) } Plan: 6 to add, 0 to change, 0 to destroy. Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now. PS C:\TACG\_CCOnGCP\_CCOnGCP-CCStuff> terraform apply data.local_file.GCPCredentials: Reading... data.local_file.LoadZoneID: Reading... data.local_file.LoadZoneID: Read complete after 0s [id=fec7404d513834378890c62d7e34d9d692f11957] data.local_file.GCPCredentials: Read complete after 0s [id=e153c83ee291d1531ec30503aacbb705602bde95] data.google_compute_network.GCPSubnet: Reading... data.google_compute_network.GCPVPC: Reading... data.google_compute_network.GCPSubnet: Read complete after 1s [id=projects/tacg-gcp-XXXXXXXXXX/global/networks/default] data.google_compute_network.GCPVPC: Read complete after 1s [id=projects/tacg-gcp-XXXXXXXXXX/global/networks/default] Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # citrix_delivery_group.CreateDG will be created + resource "citrix_delivery_group" "CreateDG" { + associated_machine_catalogs = [ + { + machine_catalog = (known after apply) + machine_count = 1 }, ] + autoscale_settings = { + autoscale_enabled = true + disconnect_off_peak_idle_session_after_seconds = 0 + disconnect_peak_idle_session_after_seconds = 300 + log_off_off_peak_disconnected_session_after_seconds = 0 + log_off_peak_disconnected_session_after_seconds = 300 + off_peak_buffer_size_percent = 0 + off_peak_disconnect_action = "Nothing" + off_peak_disconnect_timeout_minutes = 0 + off_peak_extended_disconnect_action = "Nothing" + off_peak_extended_disconnect_timeout_minutes = 0 + off_peak_log_off_action = "Nothing" + peak_buffer_size_percent = 0 + peak_disconnect_action = "Nothing" + peak_disconnect_timeout_minutes = 0 + peak_extended_disconnect_action = "Nothing" + peak_extended_disconnect_timeout_minutes = 0 + peak_log_off_action = "Nothing" + power_off_delay_minutes = 30 + power_time_schemes = [ + { + days_of_week = [ + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", ] + display_name = "TACG-GCP-TF-AS-Weekdays" + peak_time_ranges = [ + "09:00-17:00", ] + pool_size_schedules = [ + { + pool_size = 1 + time_range = "09:00-17:00" }, ] + pool_using_percentage = false }, ] } + desktops = [ + { + description = "Terraform-based Delivery Group running on GCP" + enable_session_roaming = true + enabled = true + published_name = "DG-TF-TACG-GCP" + restricted_access_users = { + allow_list = [ + "TACG-GCP\\vdaallowed", ] } }, ] + id = (known after apply) + minimum_functional_level = "L7_20" + name = "DG-TF-TACG-GCP" + reboot_schedules = [ + { + days_in_week = [ + "Sunday", ] + frequency = "Weekly" + frequency_factor = 1 + ignore_maintenance_mode = true + name = "TACG-GCP-Reboot Schedule" + natural_reboot_schedule = false + reboot_duration_minutes = 0 + reboot_schedule_enabled = true + start_date = "2024-01-01" + start_time = "02:00" }, ] + restricted_access_users = { + allow_list = [ + "TACG-GCP\\vdaallowed", ] } + total_machines = (known after apply) } # citrix_gcp_hypervisor.CreateHypervisorConnection will be created + resource "citrix_gcp_hypervisor" "CreateHypervisorConnection" { + id = (known after apply) + name = "TACG-GCP-TF-HypConn" + service_account_credentials = (sensitive value) + service_account_id = "XXXXXXXXXX@XXXXXXXXXX.iam.gserviceaccount.com" + zone = "XXXXXXXX-XXXX-XXXX-XXXX-f6a1f864d69a" } # citrix_gcp_hypervisor_resource_pool.CreateHypervisorPool will be created + resource "citrix_gcp_hypervisor_resource_pool" "CreateHypervisorPool" { + hypervisor = (known after apply) + id = (known after apply) + name = "TACG-GCP-TF-HypConnPool" + project_name = "TACG-GCP" + region = "europe-west3" + subnets = [ + "default", ] + vpc = "default" } # citrix_machine_catalog.CreateMCSCatalog will be created + resource "citrix_machine_catalog" "CreateMCSCatalog" { + allocation_type = "Random" + description = "Terraform-based Machine Catalog" + id = (known after apply) + is_power_managed = true + is_remote_pc = false + minimum_functional_level = "L7_20" + name = "MC-TACG-GCP-TF" + provisioning_scheme = { + gcp_machine_config = { + master_image = "tacg-gcp-tf-wmi" + storage_type = "pd-standard" } + hypervisor = (known after apply) + hypervisor_resource_pool = (known after apply) + identity_type = "ActiveDirectory" + machine_account_creation_rules = { + naming_scheme = "TACG-GCP-W###" + naming_scheme_type = "Numeric" } + machine_domain_identity = { + domain = "gcp.the-austrian-citrix-guy.at" + domain_ou = "CN=Computers,DC=gcp,DC=the-austrian-citrix-guy,DC=at" + service_account = "XXXXXXXXXX" + service_account_password = (sensitive value) } + number_of_total_machines = 1 } + provisioning_type = "MCS" + session_support = "MultiSession" + zone = "XXXXXXXX-XXXX-XXXX-XXXX-f6a1f864d69a" } # time_sleep.wait_60_seconds will be created + resource "time_sleep" "wait_60_seconds" { + create_duration = "60s" + id = (known after apply) } # time_sleep.wait_60_seconds_1 will be created + resource "time_sleep" "wait_60_seconds_1" { + create_duration = "60s" + id = (known after apply) } Plan: 6 to add, 0 to change, 0 to destroy. Do you want to perform these actions? Terraform will perform the actions described above. Only 'yes' will be accepted to approve. Enter a value: yes citrix_gcp_hypervisor.CreateHypervisorConnection: Creating... citrix_gcp_hypervisor.CreateHypervisorConnection: Still creating... [10s elapsed] citrix_gcp_hypervisor.CreateHypervisorConnection: Still creating... [20s elapsed] citrix_gcp_hypervisor.CreateHypervisorConnection: Still creating... [30s elapsed] citrix_gcp_hypervisor.CreateHypervisorConnection: Still creating... [40s elapsed] citrix_gcp_hypervisor.CreateHypervisorConnection: Creation complete after 41s [id=ceb89ce1-b09f-467d-8fe8-93e59e31bff6] citrix_gcp_hypervisor_resource_pool.CreateHypervisorPool: Creating... citrix_gcp_hypervisor_resource_pool.CreateHypervisorPool: Still creating... [10s elapsed] citrix_gcp_hypervisor_resource_pool.CreateHypervisorPool: Creation complete after 11s [id=506a698f-22bf-4b5f-a910-4618b41890f7] time_sleep.wait_60_seconds: Creating... time_sleep.wait_60_seconds: Still creating... [10s elapsed] time_sleep.wait_60_seconds: Still creating... [20s elapsed] time_sleep.wait_60_seconds: Still creating... [30s elapsed] time_sleep.wait_60_seconds: Still creating... [40s elapsed] time_sleep.wait_60_seconds: Still creating... [51s elapsed] time_sleep.wait_60_seconds: Creation complete after 1m0s [id=2024-04-04T07:33:16Z] citrix_machine_catalog.CreateMCSCatalog: Creating... citrix_machine_catalog.CreateMCSCatalog: Still creating... [10s elapsed] citrix_machine_catalog.CreateMCSCatalog: Still creating... [20s elapsed] ... ** Output shortened ** ... citrix_machine_catalog.CreateMCSCatalog: Still creating... [20m20s elapsed] citrix_machine_catalog.CreateMCSCatalog: Still creating... [20m30s elapsed] citrix_machine_catalog.CreateMCSCatalog: Creation complete after 20m35s [id=1410b841-877a-4c7d-bf57-15e310865f36] time_sleep.wait_60_seconds_1: Creating... time_sleep.wait_60_seconds_1: Still creating... [10s elapsed] time_sleep.wait_60_seconds_1: Still creating... [20s elapsed] time_sleep.wait_60_seconds_1: Still creating... [30s elapsed] time_sleep.wait_60_seconds_1: Still creating... [40s elapsed] time_sleep.wait_60_seconds_1: Still creating... [50s elapsed] time_sleep.wait_60_seconds_1: Creation complete after 1m0s [id=2024-04-04T07:54:51Z] citrix_delivery_group.CreateDG: Creating... citrix_delivery_group.CreateDG: Creation complete after 5s [id=9f458d5f-594f-49b5-b2c2-cb920442dc27] Apply complete! Resources: 6 added, 0 changed, 0 destroyed. PS C:\TACG\_CCOnGCP\_CCOnGCP-CCStuff> 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 Resource Location: The Hypervisor Connection and the Hypervisor Pool: The Machine Catalog: The Delivery Group and the Worker-VM: The Worker-VM: The AutoScale settings of the Delivery Group: The Desktop in the Library: The Desktop in Workspace App: 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 .table_component { overflow: auto; width: 100%; } .table_component table { border: 1px solid #323232; border-radius: 3px; height: 100%; width: 90%; table-layout: fixed; border-collapse: collapse; border-spacing: 1px; text-align: left; } .table_component caption { caption-side: top; text-align: left; } .table_component th { border: 1px solid #323232; background-color: #323232; color: #ffffff; padding: 10px; } .table_component td { border: 1px solid #323232; background-color: #323232; color: #ffffff; padding: 10px; font-family: Consolas,Courier New; font-size:12px; } # 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 .table_component { overflow: auto; width: 100%; } .table_component table { border: 1px solid #323232; border-radius: 3px; height: 100%; width: 90%; table-layout: fixed; border-collapse: collapse; border-spacing: 1px; text-align: left; } .table_component caption { caption-side: top; text-align: left; } .table_component th { border: 1px solid #323232; background-color: #323232; color: #ffffff; padding: 10px; } .table_component td { border: 1px solid #323232; background-color: #323232; color: #ffffff; padding: 10px; font-family: Consolas,Courier New; font-size:12px; } ### 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 .table_component { overflow: auto; width: 100%; } .table_component table { border: 1px solid #323232; border-radius: 3px; height: 100%; width: 90%; table-layout: fixed; border-collapse: collapse; border-spacing: 1px; text-align: left; } .table_component caption { caption-side: top; text-align: left; } .table_component th { border: 1px solid #323232; background-color: #323232; color: #ffffff; padding: 10px; } .table_component td { border: 1px solid #323232; background-color: #323232; color: #ffffff; padding: 10px; font-family: Consolas,Courier New; font-size:12px; } # 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 .table_component { overflow: auto; width: 100%; } .table_component table { border: 1px solid #323232; border-radius: 3px; height: 100%; width: 90%; table-layout: fixed; border-collapse: collapse; border-spacing: 1px; text-align: left; } .table_component caption { caption-side: top; text-align: left; } .table_component th { border: 1px solid #323232; background-color: #323232; color: #ffffff; padding: 10px; } .table_component td { border: 1px solid #323232; background-color: #323232; color: #ffffff; padding: 10px; font-family: Consolas,Courier New; font-size:12px; } # 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 .table_component { overflow: auto; width: 100%; } .table_component table { border: 1px solid #323232; border-radius: 3px; height: 100%; width: 90%; table-layout: fixed; border-collapse: collapse; border-spacing: 1px; text-align: left; } .table_component caption { caption-side: top; text-align: left; } .table_component th { border: 1px solid #323232; background-color: #323232; color: #ffffff; padding: 10px; } .table_component td { border: 1px solid #323232; background-color: #323232; color: #ffffff; padding: 10px; font-family: Consolas,Courier New; font-size:12px; } # 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 .table_component { overflow: auto; width: 100%; } .table_component table { border: 1px solid #323232; border-radius: 3px; height: 100%; width: 90%; table-layout: fixed; border-collapse: collapse; border-spacing: 1px; text-align: left; } .table_component caption { caption-side: top; text-align: left; } .table_component th { border: 1px solid #323232; background-color: #323232; color: #ffffff; padding: 10px; } .table_component td { border: 1px solid #323232; background-color: #323232; color: #ffffff; padding: 10px; font-family: Consolas,Courier New; font-size:12px; } # 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 .table_component { overflow: auto; width: 100%; } .table_component table { border: 1px solid #323232; border-radius: 3px; height: 100%; width: 90%; table-layout: fixed; border-collapse: collapse; border-spacing: 1px; text-align: left; } .table_component caption { caption-side: top; text-align: left; } .table_component th { border: 1px solid #323232; background-color: #323232; color: #ffffff; padding: 10px; } .table_component td { border: 1px solid #323232; background-color: #323232; color: #ffffff; padding: 10px; font-family: Consolas,Courier New; font-size:12px; } # 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 } .table_component { overflow: auto; width: 100%; } .table_component table { border: 1px solid #323232; border-radius: 3px; height: 100%; width: 90%; table-layout: fixed; border-collapse: collapse; border-spacing: 1px; text-align: left; } .table_component caption { caption-side: top; text-align: left; } .table_component th { border: 1px solid #323232; background-color: #323232; color: #ffffff; padding: 10px; } .table_component td { border: 1px solid #323232; background-color: #323232; color: #ffffff; padding: 10px; font-family: Consolas,Courier New; font-size:12px; }
  2. Overview This guide aims to provide an overview of using Terraform to create a complete Citrix DaaS Resource Location on Amazon EC2. At the end of the process, you created: A new Citrix Cloud Resource Location (RL) running on Amazon EC2 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 Amazon EC2 A Machine Catalog based on the uploaded Master Image VHD or on an Amazon EC2-based Master Image A Delivery Group based on the Machine Catalog with full Autoscale Support Example policies and policy scope bound to the Delivery Group 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: .tg {border-collapse:collapse;border-spacing:0;width:100%} .tg .tg-5jvq{background-color:#0000b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px; text-align:left;vertical-align:top} 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: .tg {border-collapse:collapse;border-spacing:0;width:100%} .tg .tg-5jvq{background-color:#0000b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px; text-align:left;vertical-align:top} PS C:\TACG> 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)) Forcing web requests to allow TLS v1.2 (Required for requests to Chocolatey.org) Getting latest version of the Chocolatey package for download. Not using proxy. Getting Chocolatey from https://community.chocolatey.org/api/v2/package/chocolatey/2.2.2. Downloading https://community.chocolatey.org/api/v2/package/chocolatey/2.2.2 to C:\TACG\AppData\Local\Temp\chocolatey\chocoInstall\chocolatey.zip Not using proxy. Extracting C:\TACG\AppData\Local\Temp\chocolatey\chocoInstall\chocolatey.zip to C:\TACG\AppData\Local\Temp\chocolatey\chocoInstall Installing Chocolatey on the local machine Creating ChocolateyInstall as an environment variable (targeting 'Machine') Setting ChocolateyInstall to 'C:\ProgramData\chocolatey' WARNING: It's very likely you will need to close and reopen your shell before you can use choco. Restricting write permissions to Administrators We are setting up the Chocolatey package repository. The packages themselves go to 'C:\ProgramData\chocolatey\lib' (i.e. C:\ProgramData\chocolatey\lib\yourPackageName). A shim file for the command line goes to 'C:\ProgramData\chocolatey\bin' and points to an executable in 'C:\ProgramData\chocolatey\lib\yourPackageName'. Creating Chocolatey folders if they do not already exist. chocolatey.nupkg file not installed in lib. Attempting to locate it from bootstrapper. PATH environment variable does not have C:\ProgramData\chocolatey\bin in it. Adding... WARNING: Not setting tab completion: Profile file does not exist at 'C:\TACG\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1'. Chocolatey (choco.exe) is now ready. You can call choco from anywhere, command line or powershell by typing choco. Run choco /? for a list of functions. You may need to shut down and restart powershell and/or consoles first prior to using choco. Ensuring Chocolatey commands are on the path Ensuring chocolatey.nupkg is in the lib folder PS C:\TACG> Run choco --v to check if Chocolatey was installed successfully: .tg {border-collapse:collapse;border-spacing:0;width:100%} .tg .tg-5jvq{background-color:#0000b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px; text-align:left;vertical-align:top} PS C:\TACG> choco --v Chocolatey v2.2.2 PS C:\TACG> Installation of Terraform After the successful installation of Chocolatey you can install Terraform by running this command on the PowerShell session: .tg {border-collapse:collapse;border-spacing:0;width:100%} .tg .tg-5jvq{background-color:#0000b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px; text-align:left;vertical-align:top} PS C:\TACG> choco install terraform Chocolatey v2.2.2 Installing the following packages: terraform By installing, you accept licenses for the packages. Progress: Downloading terraform 1.6.4... 100% terraform v1.7.4 [Approved] terraform package files install completed. Performing other installation steps. The package terraform wants to run 'chocolateyInstall.ps1'. Note: If you don't run this script, the installation will fail. Note: To confirm automatically next time, use '-y' or consider: choco feature enable -n allowGlobalConfirmation Do you want to run the script?([Y]es/[A]ll - yes to all/[N]o/[P]rint): A Removing old terraform plug-ins Downloading terraform 64 bit from 'https://releases.hashicorp.com/terraform/1.7.4/terraform_1.7.4_windows_amd64.zip' Progress: 100% - Completed download of C:\TACG\AppData\Local\Temp\chocolatey\terraform\1.7.4\terraform_1.7.4_windows_amd64.zip (25.05 MB). Download of terraform_1.7.4_windows_amd64.zip (25.05 MB) completed. Hashes match. Extracting C:\TACG\AppData\Local\Temp\chocolatey\terraform\1.7.4\terraform_1.7.4_windows_amd64.zip to C:\ProgramData\chocolatey\lib\terraform\tools... C:\ProgramData\chocolatey\lib\terraform\tools ShimGen has successfully created a shim for terraform.exe The install of terraform was successful. Software installed to 'C:\ProgramData\chocolatey\lib\terraform\tools' Chocolatey installed 1/1 packages. See the log for details (C:\ProgramData\chocolatey\logs\chocolatey.log). PS C:\TACG> Run terraform -version to check if Terraform installed successfully: .tg {border-collapse:collapse;border-spacing:0;width:100%} .tg .tg-5jvq{background-color:#0000b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px; text-align:left;vertical-align:top} PS C:\TACG> terraform -version Terraform v1.7.4 on windows_amd64 PS C:\TACG> 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 that 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 an NIC of an Amazon EC2 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. Using Terraform with Citrix' provider, you can manage your Citrix products via Infrastructure as Code. Terraform is giving you higher efficiency and consistency on infrastructure management and better reusability on infrastructure configuration. The provider defines individual units of infrastructure and currently supports both Citrix Virtual Apps and Desktops and Citrix Desktop as a Service solutions. You can automate the creation of a site setup including host connections, machine catalogs, and delivery groups. You can deploy resources in Amazon EC2, AWS, and GCP in addition to supported on-premises Hypervisors. 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 in it. Initialize the working directory by using the command: .tg {border-collapse:collapse;border-spacing:0;width:100%} .tg .tg-5jvq{background-color:#0000b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px; text-align:left;vertical-align:top} PS C:\TACG> terraform init Initializing the backend... Successfully configured the backend "local"! Terraform will automatically use this backend unless the backend configuration changes. Initializing provider plug-ins... - Finding citrix/citrix versions matching ">= 0.5.3"... - Installing citrix/citrix v0.5.3... - Installed citrix/citrix v0.5.3 (signed by a HashiCorp partner, key ID 25D62DD8407EA386) Partner and community providers are signed by their developers. If you'd like to know more about provider signing, you can read about it here: https://www.terraform.io/docs/cli/plug-ins/signing.html Terraform has created a lock file .terraform.lock.hcl to record the provider selections it made above. Include this file in your version control repository so that Terraform can guarantee to make the same selections by default when you run "terraform init" in the future. Terraform has been successfully initialized! You may now begin working with Terraform. Try running "terraform plan" to see any changes that are required for your infrastructure. All Terraform commands should now work. If you ever set or change modules or backend configuration for Terraform, rerun this command to reinitialize your working directory. If you forget, other commands will detect it and remind you to do so if necessary. PS C:\TACG> 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: # Cloud Provider provider "Citrix" { customer_id = "idofthexxxxxxxxx" ‘Citrix Cloud Customer ID client_id = "3edrxxxx-XXXX-XXXX-XXXX-XXXXXXXXXX" ‘ID of the Secure API client planned to use client_secret = "*********************" ‘Secret of the Secure API client planned to use } A guide for the creation of a secure API client can be found in Citrix Developer Docs and is shown later as well. # On-Premises Provider provider "Citrix" { hostname = "10.0.0.6" client_id = "foo.local\\admin" client_secret = "foo" } 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 the Domain-Admin Username. For Citrix Cloud customers: Use this variable to specify Cloud 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 the Domain-Admin Password. For Citrix Cloud customers: Use this variable to specify Cloud 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 to true to skip SSL verification only when the target DDC does not have a valid SSL certificate issued by a trusted CA. When set to true, please make sure that your provider config is set for a known DDC hostname. 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 Controller hostname. For Citrix Cloud customers (Optional): Use this variable to override the Citrix DaaS service hostname. Can be set via the Environment Variable CITRIX_HOSTNAME.| Deploying a Citrix Cloud Resource location on Amazon EC2 using Terraform Overview This guide aims to showcase the possibility of creating a complete Citrix Cloud Resource Location on Amazon EC2 using Terraform. We want to reduce manual interventions to the absolute minimum. All Terraform configuration files can be found late on GitHub - we update this guide when the GitHub repository is ready. In this guide we use an existing domain and will not deploy a new domain - for further instructions for deploying a new domain refer to the guide Citrix DaaS and Terraform - Automatic Deployment of a Resource Location on Microsoft Azure. 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 by using IPSec-based Site-to-Site VPNs. Each Resource Location has its own sub-domain. The Terraform flow is split into different parts: 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 Amazon EC2: Creating all needed IAM roles on Amazon EC2 Creating all needed IAM Instance profiles on Amazon EC2 Creating all needed IAM policies on Amazon EC2 Creating all needed Secret Manager configurations on Amazon EC2 Creating all needed DHCP configurations on Amazon EC2 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 used as Cloud Connector VMs in step 2 Creating a Windows Server 2022-based VM acting as a Administrative workstation for running the Terraform steps 2 and 3 - this is necessary because of using 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 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 Amazon EC2: 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 Configuring the 2 CCs as Cloud Connectors Registering the 2 CCs 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 Amazon EC2 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 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 the following command: .tg {border-collapse:collapse;border-spacing:0;width:100%} .tg .tg-5jvq{background-color:#0000b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px; text-align:left;vertical-align:top} PS C:\TACG> test-wsman -ComputerName <IP-Address of the computer you want to reach> -Credential <IP-Address of the computer you want to reach>\administrator -Authentication Basic The response should look like: wsmid : http://schemas.dmtf.org/wbem/wsman/identity/1/wsmanidentity.xsd ProtocolVersion : http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd ProductVendor : Microsoft Corporation ProductVersion : OS: 0.0.0 SP: 0.0 Stack: 3.0 Another possibility is to open a PowerShell console and type: Enter-PSSession -ComputerName <IP-Address of the computer you want to reach> -Credential <IP-Address of the computer you want to reach>\administrator The response should look like: [172.31.22.104]: PS C:\Users\Administrator\Documents> 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 in the Terraform console something like...: .tg {border-collapse:collapse;border-spacing:0;width:100%} .tg .tg-5jvq{background-color:#0000b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px; text-align:left;vertical-align:top} null_resource.CreateTestScriptOnCC1: Creating... null_resource.CreateTestScriptOnCC1: Provisioning with 'remote-exec'... null_resource.CreateTestScriptOnCC1 (remote-exec): Connecting to remote host via WinRM... null_resource.CreateTestScriptOnCC1 (remote-exec): Host: 172.31.22.103 null_resource.CreateTestScriptOnCC1 (remote-exec): Port: 5985 null_resource.CreateTestScriptOnCC1 (remote-exec): User: administrator null_resource.CreateTestScriptOnCC1 (remote-exec): Password: true null_resource.CreateTestScriptOnCC1 (remote-exec): HTTPS: false null_resource.CreateTestScriptOnCC1 (remote-exec): Insecure: false null_resource.CreateTestScriptOnCC1 (remote-exec): NTLM: false null_resource.CreateTestScriptOnCC1 (remote-exec): CACert: false null_resource.CreateTestScriptOnCC1 (remote-exec): Connected! #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>#< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs> null_resource.CreateTestScriptOnCC1 (remote-exec): C:\Users\Administrator>powershell -File c:/temp/xdinst/DATA/Terraform-Test-WinRM.ps1 #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj><Obj S="progress" RefId="1"><TNRef RefId="0" /><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>#< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>null_resource.CreateTestScriptOnCC1: Creation complete after 3s [id=1571484748961023525] ...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 which 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. File System structure Root-Directory Module 1: _CConAWS-Creation: Filename Purpose _CConAWS-Creation-Create.tf Resource configuration and primary flow definition _CConAWS-Creation-Create-variables.tf Definition of Variables _CConAWS-Creation-Create.auto.tfvars.json Setting the values of the Variables _CConAWS-Creation-Provider.tf Provider definition and configuration _CConAWS-Creation-Provider-variables.tf Definition of Variables _CConAWS-Creation-Provider.auto.tfvars.json Setting the values of the Variables Add-EC2InstanceToDomainAdminVM.ps1 Powershell-Script for joining the Admin-VM to the Domain Add-EC2InstanceToDomainCC1.ps1 Powershell-Script for joining the CC1-VM to the Domain Add-EC2InstanceToDomainCC2.ps1 Powershell-Script for joining the CC2-VM to the Domain Add-EC2InstanceToDomainWMI.ps1 Powershell-Script for joining the CC2-VM to the Domain DATA-Directory Place to put files to upload using file provisioning (NOT RECOMMENDED - see later explanation Module 2: _CConAWS-Install: Filename Purpose _CCOnAWS-Install-CreatePreReqs.tf Resource configuration and primary flow definition _CCOnAWS-Install-CreatePreReqs-variables.tf Definition of Variables _CCOnAWS-Install-CreatePreReqs.auto.tfvars.json Setting the values of the Variables _CConAWS-Install-Provider.tf Provider definition and configuration _CConAWS-Install-Provider-variables.tf Definition of Variables _CConAWS-Install-Provider.auto.tfvars.json Setting the values of the Variables GetSiteID.ps1 PowerShell script to make a REST-API call to determine the CC-Site-ID GetZoneID.ps1 PowerShell script to make a REST-API call to determine the CC-Zone-ID DATA/InstallPreReqsOnAVM1.ps1 PowerShell script to deploy needed pre-requisites on the Admin-VM DATA/InstallPreReqsOnAVM2.ps1 PowerShell script to deploy needed pre-requisites on the Admin-VM DATA/InstallPreReqsOnCC.ps1 PowerShell script to deploy needed pre-requisites on the Admin-VM Module 3: _CConAWS-CCStuff: Filename Purpose _CCOnAWS-CCStuff-CreateCCEntities.tf Resource configuration and primary flow definition _CCOnAWS-CCStuff-CreateCCEntities-variables.tf Definition of Variables _CCOnAWS-CCStuff-CreateCCEntities.auto.tfvars.json Setting the values of the Variables _CConAWS-CCStuff-Provider.tf Provider definition and configuration _CConAWS-CCStuff-Provider-variables.tf Definition of Variables _CConAWS-CCStuff-Provider.auto.tfvars.json Setting the values of the Variables Change the settings in the .json files according to your needs. The following prerequisites are needed before setting the corresponding settings or running the Terraform workflow to ensure a smooth and error-free build. Prerequisites Installing AWS.Tools for PowerShell and AWS CLI In this guide, we use AWS CLI and PowerShell cmdlets to determine further needed information. Further information about AWS CLI and the installation/configuration can be found at AWS Command Line Interface, further information about the PowerShell cmdlets for AWS can be found at Installing the AWS Tools for PowerShell on Windows. .tg {border-collapse:collapse;border-spacing:0;width:100%} .tg .tg-5jvq{background-color:#0000b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px; text-align:left;vertical-align:top} Examples: PS C:\TACG> Install-AWSToolsModule AWS.Tools.EC2 -Force Installing module AWS.Tools.EC2 version 4.1.533.0 PS C:\TACG> Install-AWSToolsModule AWS.Tools.IdentityManagement -Force Installing module AWS.Tools.IdentityManagement version 4.1.533.0 PS C:\TACG> Install-AWSToolsModule AWS.Tools.SimpleSystemsManagement -Force Installing module AWS.Tools.SimpleSystemsManagement version 4.1.533.0 PS C:\TACG> Existing Amazon EC2 entities We anticipate that the following resources are existing and are already configured on Amazon EC2: A working tenant All needed rights for the IAM user on the tenant A VPC 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 how to create the key later on) We can get the needed information about the VPC by using PowerShell: .tg {border-collapse:collapse;border-spacing:0;width:100%} .tg .tg-5jvq{background-color:#0000b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px; text-align:left;vertical-align:top} PS C:\TACG> Get-EC2VPC CidrBlock : 172.31.0.0/16 CidrBlockAssociationSet : {vpc-cidr-assoc-0a91XXXXXXXXXXX} DhcpOptionsId : dopt-0a71XXXXXXXXXXX InstanceTenancy : default Ipv6CidrBlockAssociationSet : {} IsDefault : True OwnerId : 968XXXXXX State : available Tags : {} VpcId : vpc-0f9XXXXXXXXXXXX Note the VpcId and put it into the corresponding .auto.tvars.json file. We can get the needed information about one or more subnets also by using PowerShell: .tg {border-collapse:collapse;border-spacing:0;width:100%} .tg .tg-5jvq{background-color:#0000b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px; text-align:left;vertical-align:top} PS C:\TACG> Get-EC2Subnet AssignIpv6AddressOnCreation : False AvailabilityZone : eu-central-1b AvailabilityZoneId : euc1-az3 AvailableIpAddressCount : 4091 CidrBlock : 172.31.32.0/20 CustomerOwnedIpv4Pool : DefaultForAz : True EnableDns64 : False EnableLniAtDeviceIndex : 0 Ipv6CidrBlockAssociationSet : {} Ipv6Native : False MapCustomerOwnedIpOnLaunch : False MapPublicIpOnLaunch : True OutpostArn : OwnerId : 968XXXXXX PrivateDnsNameOptionsOnLaunch : Amazon.EC2.Model.PrivateDnsNameOptionsOnLaunch State : available SubnetArn : arn:aws:ec2:eu-central-1:968XXXXXX:subnet/subnet-02e91c49df134f849 SubnetId : subnet-02eXXXXXXXXXX Tags : {Name} VpcId : vpc-0f9XXXXXXXXXXXX AssignIpv6AddressOnCreation : False AvailabilityZone : eu-central-1a AvailabilityZoneId : euc1-az2 AvailableIpAddressCount : 4089 CidrBlock : 172.31.16.0/20 CustomerOwnedIpv4Pool : DefaultForAz : True EnableDns64 : False EnableLniAtDeviceIndex : 0 Ipv6CidrBlockAssociationSet : {} Ipv6Native : False MapCustomerOwnedIpOnLaunch : False MapPublicIpOnLaunch : True OutpostArn : OwnerId : 968XXXXXX PrivateDnsNameOptionsOnLaunch : Amazon.EC2.Model.PrivateDnsNameOptionsOnLaunch State : available SubnetArn : arn:aws:ec2:eu-central-1:968XXXXXX:subnet/subnet-07eXXXXXXXXXX SubnetId : subnet-07eXXXXXXXXXX Tags : {Name} VpcId : vpc-0f9XXXXXXXXXXXX AssignIpv6AddressOnCreation : False AvailabilityZone : eu-central-1c AvailabilityZoneId : euc1-az1 AvailableIpAddressCount : 4090 CidrBlock : 172.31.0.0/20 CustomerOwnedIpv4Pool : DefaultForAz : True EnableDns64 : False EnableLniAtDeviceIndex : 0 Ipv6CidrBlockAssociationSet : {} Ipv6Native : False MapCustomerOwnedIpOnLaunch : False MapPublicIpOnLaunch : True OutpostArn : OwnerId : 968XXXXXX PrivateDnsNameOptionsOnLaunch : Amazon.EC2.Model.PrivateDnsNameOptionsOnLaunch State : available SubnetArn : arn:aws:ec2:eu-central-1:968XXXXXX:subnet/subnet-0359XXXXXXXXXXX SubnetId : subnet-0359XXXXXXXXXXX Tags : {Name} VpcId : vpc-0f9XXXXXXXXXXXX Note the SubnetID of the Availability Zone that you want to use and put it into the corresponding .auto.tvars.json file. Getting the region in Amazon EC2 where the resources will be deployed The currently configured default region can be found by using for example AWS CLI - open a PowerShell window and type: .tg {border-collapse:collapse;border-spacing:0;width:100%} .tg .tg-5jvq{background-color:#0000b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px; text-align:left;vertical-align:top} PS C:\TACG> aws configure get region eu-central-1 PS C:\TACG> Write down the Location as we need to assign it to variables. Getting the available AMI Image-IDs from Amazon EC2 We want to automatically deploy the virtual machines necessary for the DC and the CCs - so we need detailed configuration settings: Set the credentials needed for allowing PowerShell to access the EC2 tenant: .tg {border-collapse:collapse;border-spacing:0;width:100%} .tg .tg-5jvq{background-color:#0000b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px; text-align:left;vertical-align:top} PS C:\TACG> Set-AWSCredential -AccessKey AKIXXXXXXXXXXXXXXXX -SecretKey RiXXXXXXXXXXXXXXXXXXXXXXXXXX -StoreAs default Get the available AMI images: .tg {border-collapse:collapse;border-spacing:0;width:100%} .tg .tg-5jvq{background-color:#0000b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px; text-align:left;vertical-align:top} PS C:\TACG> Initialize-AWSDefaultConfiguration -ProfileName default -Region eu-central-1 PS C:\TACG> Get-SSMLatestEC2Image -Path ami-windows-latest -ProfileName default -Region eu-central-1 Name Value ---- ----- EC2LaunchV2-Windows_Server-2016-English-Full-Base ami-05da8c5b8c31e1071 Windows_Server-2016-English-Full-SQL_2014_SP3_Enterprise ami-0792b126a5682d6e8 Windows_Server-2016-German-Full-Base ami-04482c384c5f44eba Windows_Server-2016-Japanese-Full-SQL_2016_SP3_Standard ami-06bae50c6434d597c Windows_Server-2016-Japanese-Full-SQL_2017_Web ami-069867bf028ce1d11 Windows_Server-2019-English-Core-EKS_Optimized-1.25 ami-0dc34920ee17ff0c7 Windows_Server-2019-Italian-Full-Base ami-0f6d5ffbe2b4e6daa Windows_Server-2022-Japanese-Full-SQL_2019_Enterprise ami-0ce4c5ab9a9ee18e0 Windows_Server-2022-Portuguese_Brazil-Full-Base ami-0fe6028dce619a01c amzn2-ami-hvm-2.0.20191217.0-x86_64-gp2-mono ami-0f7a4c9d36399c73f Windows_Server-2016-English-Deep-Learning ami-0873c2c3320a70d5b Windows_Server-2016-Japanese-Full-SQL_2016_SP3_Web ami-08565efb3c4b556ba Windows_Server-2016-Korean-Full-Base ami-08a0270377841480d Windows_Server-2019-English-STIG-Core ami-0b4eb638a465efce5 Windows_Server-2019-French-Full-Base ami-0443c855ecad9de50 Windows_Server-2022-English-Full-Base ami-0ad8b6fa068e0299a ... Note the value of the AMI that you want to use - for example ami-0ad8b6fa068e0299a. Getting the available VM sizes from Amazon EC2 We need to determine the available VM sizes. A PowerShell script helps us to list the available instance types on Amazon EC2: .tg {border-collapse:collapse;border-spacing:0;width:100%} .tg .tg-5jvq{background-color:#0000b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px; text-align:left;vertical-align:top} PS C:\TACG> PS C:\TACG> (Get-EC2InstanceType -Region eu-central-1).Count 694 We need to filter the results to narrow down usable instances - we want to use instances with max. 4 vCPUs and max. 8 GB RAM: .tg {border-collapse:collapse;border-spacing:0;width:100%} .tg .tg-5jvq{background-color:#0000b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px; text-align:left;vertical-align:top} PS C:\TACG> Get-EC2InstanceType -Region eu-central-1 | Select-Object -Property InstanceType, @{Name="vCPUs"; Expression={$_.VCpuInfo.DefaultVCpus}}, @{Name="Memory in GB"; Expression={$_.MemoryInfo.SizeInMiB / 1024}} | Where-Object {$_vCPUs -le 4 -and $_."Memory in GB" -le 8 } | Sort-Object InstanceType | Format-Table InstanceType,vCPUs,"Memory in GB" InstanceType vCPUs Memory in GB ------------ ----- ------------ a1.large 2 4 a1.medium 1 2 a1.xlarge 4 8 c3.large 2 3,75 c3.xlarge 4 7,5 c4.large 2 3,75 c4.xlarge 4 7,5 c5.large 2 4 c5.xlarge 4 8 c5a.large 2 4 c5a.xlarge 4 8 c5ad.large 2 4 c5ad.xlarge 4 8 c5d.large 2 4 c5d.xlarge 4 8 c5n.large 2 5,25 c6a.large 2 4 c6a.xlarge 4 8 c6g.large 2 4 c6g.medium 1 2 c6g.xlarge 4 8 c6gd.large 2 4 c6gd.medium 1 2 c6gd.xlarge 4 8 c6gn.large 2 4 c6gn.medium 1 2 c6gn.xlarge 4 8 c6i.large 2 4 c6i.xlarge 4 8 c6id.large 2 4 c6id.xlarge 4 8 c6in.large 2 4 c6in.xlarge 4 8 c7a.large 2 4 c7a.medium 1 2 c7a.xlarge 4 8 c7g.large 2 4 c7g.medium 1 2 c7g.xlarge 4 8 c7gd.large 2 4 c7gd.medium 1 2 c7gd.xlarge 4 8 c7i.large 2 4 c7i.xlarge 4 8 t2.large 2 8 t2.medium 2 4 t2.micro 1 1 t2.nano 1 0,5 t2.small 1 2 t3.large 2 8 t3.medium 2 4 t3.micro 2 1 t3.nano 2 0,5 t3.small 2 2 t3a.large 2 8 t3a.medium 2 4 t3a.micro 2 1 t3a.nano 2 0,5 t3a.small 2 2 t4g.large 2 8 t4g.medium 2 4 t4g.micro 2 1 t4g.nano 2 0,5 t4g.small 2 2 ... PS C:\TACG> Note the Instancetype parameter of the Amazon EC2 Resource Location that you want to use. We use t3.medium and t3.large. Be sure to use the Citrix Terraform Provider version 0.5.3 or higher or we need to pass the Instance type to the Terraform provider in a different format (examples): Amazon EC2 syntax: Needed format: t3.nano T3 Nano Instance t3.small T3 Small Instance t3.medium T3 Medium Instance t3.large T3 Large Instance e2.small E2 Small Instance e2.medium E2 Medium Instance Example: Checking the current quotas for the instances (N-family) which we plan to use in this guide: .tg {border-collapse:collapse;border-spacing:0;width:100%} .tg .tg-5jvq{background-color:#0000b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px; text-align:left;vertical-align:top} PS C:\TACG> Install-AWSToolsModule AWS.Tools.ServiceQuotas PS C:\TACG> Get-SQServiceList ServiceCode ServiceName ----------- ----------- AWSCloudMap AWS Cloud Map access-analyzer Access Analyzer acm AWS Certificate Manager (ACM) acm-pca AWS Private Certificate Authority ... ec2 Amazon Elastic Compute Cloud (Amazon EC2) ec2-ipam IPAM ec2fastlaunch EC2 Fast Launch ... PS C:\TACG> Get-SQServiceQuota -ServiceCode ec2 -QuotaCode L-1216C47A Adjustable : True ErrorReason : GlobalQuota : False Period : QuotaAppliedAtLevel : ACCOUNT QuotaArn : arn:aws:servicequotas:eu-central-1:9XXXXXXXXX:ec2/L-1216C47A QuotaCode : L-1216C47A QuotaContext : QuotaName : Running On-Demand Standard (A, C, D, H, I, M, R, T, Z) instances ServiceCode : ec2 ServiceName : Amazon Elastic Compute Cloud (Amazon EC2) Unit : None UsageMetric : Amazon.ServiceQuotas.Model.MetricInfo Value : 256 PS C:\TACG> The output of the cmdlet shows that we should have enough available resources on the instance level. The quotas can be increased using the Amazon EC2 Console or PowerShell. More information about increasing vCPU quotas can be found here: Amazon Elastic Compute Cloud (Amazon EC2) quotas. 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 - we use an Azure Storage Blob 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://wmwblob.blob.core.windows.net/tfdata/cwcconnector.exe" For the Remote PowerShell SDK: "CC_Install_RPoSHURI":"https://wmwblob.blob.core.windows.net/tfdata/CitrixPoshSdk.exe" Creating an Access Key with a Secret for Amazon EC2 Authentication in AWS CLI and/or AWS.Tools for PowerShell Access Keys are long-term credentials for an IAM user or the root user which are used for Authentication and Authorization in EC2. They consist of two parts: an Access Key-ID and a Secret Access Key. Both are needed for Authentication and Authorization. Further information about Access Key management and the configuration can be found in Managing access keys for IAM users. The needed security information for the IAM Policies is stored in an EC2-Secrets Manager: Further information about EC2-Secrets Manager and the configuration can be found at AWS Secrets Manager. Creating a Secure Client in Citrix Cloud The Secure Client in Citrix Cloud is the same as the Access Key in Amazon EC2. It is used for Authentication. 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 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. To create an API client, select the Identity and Access Management option from the menu. 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. Open Identity and Access Management in WebStudio: Creating an API Client in Citrix Cloud Click API Access, Secure Clients and put a name in the textbox next to the button Create Client. After entering a name. click Create Client: Creating an API Client in Citrix Cloud After the Secure Client is created, copy and write down the shown ID and Secret: Creating an API Client in Citrix Cloud The Secret is only visible during creation - after closing the window you are not able to get it anymore. The client-id and client-secret fields are needed by the Citrix Terraform provider. The also needed customer-id field can be found in your Citrix Cloud details. Put the values in the corresponding .auto.tvars.json file: ... "cc-customerId": "uzXXXXXXXX", "cc-apikey-clientId": "f4eXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", "cc-apikey-clientSecret": "VXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "cc-apikey-type": "client_credentials", ... Creating a Bearer Token in Citrix Cloud The Bearer Token is needed for the Authorization of some REST-API calls in Citrix Cloud. As the Citrix provider currently 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 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. Put the values in the corresponding .auto.tvars.json file: ... "cc-apikey-type": "client_credentials", "cc-apikey-bearer": "CWSAuth bearer=eyJhbGciOiJSUzI1NiIsI...0q0IW7SZFVzeBittWnEwTYOZ7Q " ... You can also use PowerShell to request a Bearer Token - therefore you need a valid Secure Client stored in Citrix Cloud: .tg {border-collapse:collapse;border-spacing:0;width:100%} .tg .tg-5jvq{background-color:#0000b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px; text-align:left;vertical-align:top} asnp Citrix* $key= "f4eXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" $secret= "VJCXXXXXXXXXXXXXX" $customer= "uXXXXXXXX" $XDStoredCredentials = Set-XDCredentials -StoreAs default -ProfileType CloudApi -CustomerId $customer -APIKey $key -SecretKey $secret $auth = Get-XDAuthentication $BT = $GLOBAL:XDAuthToken | Out-File "<Path where to store the Token\BT.txt" Module 1: Create the initially needed Resources on Amazon EC2 This module is split into the following configuration parts: Creating the initially needed Resources on Amazon EC2: Creating all needed IAM roles on Amazon EC2 Creating all needed IAM Instance profiles on Amazon EC2 Creating all needed IAM policies on Amazon EC2 Creating all needed Secret Manager configurations on Amazon EC2 Creating all needed DHCP configurations on Amazon EC2 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 the Terraform steps 2 and 3 - this is necessary because of using 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 subdomain Fetching and saving a valid Bearer Token All these steps are automatically done by Terraform. 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 .tg {border-collapse:collapse;border-spacing:0;width:100%} .tg .tg-5jvq{background-color:#0000b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px; text-align:left;vertical-align:top} PS C:\TACG\_CCOnAWS\_CCOnAWS-Creation> terraform init Initializing the backend... Initializing provider plugins... - terraform.io/builtin/terraform is built in to Terraform - Finding mastercard/restapi versions matching "1.18.2"... - Finding citrix/citrix versions matching ">= 0.5.0"... - Finding hashicorp/aws versions matching ">= 5.4.0"... - Finding latest version of hashicorp/local... - Finding latest version of hashicorp/template... - Installing hashicorp/local v2.5.1... - Installed hashicorp/local v2.5.1 (signed by HashiCorp) - Installing hashicorp/template v2.2.0... - Installed hashicorp/template v2.2.0 (signed by HashiCorp) - Installing mastercard/restapi v1.18.2... - Installed mastercard/restapi v1.18.2 (self-signed, key ID DCB8C431D71C30AB) - Installing citrix/citrix v0.5.2... - Installed citrix/citrix v0.5.2 (signed by a HashiCorp partner, key ID 25D62DD8407EA386) - Installing hashicorp/aws v5.41.0... - Installed hashicorp/aws v5.41.0 (signed by HashiCorp) Partner and community providers are signed by their developers. If you'd like to know more about provider signing, you can read about it here: https://www.terraform.io/docs/cli/plugins/signing.html Terraform has created a lock file .terraform.lock.hcl to record the provider selections it made above. Include this file in your version control repository so that Terraform can guarantee to make the same selections by default when you run "terraform init" in the future. Terraform has been successfully initialized! You may now begin working with Terraform. Try running "terraform plan" to see any changes that are required for your infrastructure. All Terraform commands should now work. If you ever set or change modules or backend configuration for Terraform, rerun this command to reinitialize your working directory. If you forget, other commands will detect it and remind you to do so if necessary. PS C:\TACG\_CCOnAWS\_CCOnAWS-Creation> PS C:\TACG\_CCOnAWS\_CCOnAWS-Creation> terraform plan data.template_file.Add-EC2InstanceToDomainScriptCC2: Reading... data.template_file.Add-EC2InstanceToDomainScriptWMI: Reading... data.template_file.Add-EC2InstanceToDomainScriptAdminVM: Reading... data.template_file.Add-EC2InstanceToDomainScriptCC1: Reading... data.template_file.Add-EC2InstanceToDomainScriptWMI: Read complete after 0s [id=85de6bac9e35231cbd60a4c1636a554940abb789938916a626a5193f27f22498] data.template_file.Add-EC2InstanceToDomainScriptCC1: Read complete after 0s [id=24ee722eca6982b33be472de4f84edbae000d0bff0a139dec2ce97c8ea14a0ca] data.template_file.Add-EC2InstanceToDomainScriptAdminVM: Read complete after 0s [id=91b3ae99f8d4a2effb377f35dda69a583de194739c8191ee665c96e663ad8615] data.template_file.Add-EC2InstanceToDomainScriptCC2: Read complete after 0s [id=15075f0d18ca3e200ab603e397339245a8ff055fd688facfc5165dd5e455d151] data.aws_iam_policy_document.ec2_assume_role: Reading... data.aws_iam_policy_document.ec2_assume_role: Read complete after 0s [id=2851119427] Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create <= read (data resources) Terraform will perform the following actions: # data.local_file.Retrieve_BT will be read during apply # (depends on a resource or a module with changes pending) <= data "local_file" "Retrieve_BT" { + content = (known after apply) + content_base64 = (known after apply) + content_base64sha256 = (known after apply) + content_base64sha512 = (known after apply) + content_md5 = (known after apply) + content_sha1 = (known after apply) + content_sha256 = (known after apply) + content_sha512 = (known after apply) + filename = "./GetBT.txt" + id = (known after apply) } # aws_iam_instance_profile.ec2_profile will be created + resource "aws_iam_instance_profile" "ec2_profile" { + arn = (known after apply) + create_date = (known after apply) + id = (known after apply) + name = "ec2-profile" + name_prefix = (known after apply) + path = "/" + role = "ec2-iam-role" + tags_all = (known after apply) + unique_id = (known after apply) } # aws_iam_policy.secret_manager_ec2_policy will be created + resource "aws_iam_policy" "secret_manager_ec2_policy" { + arn = (known after apply) + description = "Secret Manager EC2 policy" + id = (known after apply) + name = "secret-manager-ec2-policy" + name_prefix = (known after apply) + path = "/" + policy = jsonencode( { + Statement = [ + { + Action = [ + "secretsmanager:*", ] + Effect = "Allow" + Resource = "*" }, ] + Version = "2012-10-17" } ) + policy_id = (known after apply) + tags_all = (known after apply) } # aws_iam_policy_attachment.api_secret_manager_ec2_attach will be created + resource "aws_iam_policy_attachment" "api_secret_manager_ec2_attach" { + id = (known after apply) + name = "secret-manager-ec2-attachment" + policy_arn = (known after apply) + roles = (known after apply) } # aws_iam_policy_attachment.ec2_attach1 will be created + resource "aws_iam_policy_attachment" "ec2_attach1" { + id = (known after apply) + name = "ec2-iam-attachment" + policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" + roles = (known after apply) } # aws_iam_policy_attachment.ec2_attach2 will be created + resource "aws_iam_policy_attachment" "ec2_attach2" { + id = (known after apply) + name = "ec2-iam-attachment" + policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM" + roles = (known after apply) } # aws_iam_role.ec2_iam_role will be created + resource "aws_iam_role" "ec2_iam_role" { + arn = (known after apply) + assume_role_policy = jsonencode( { + Statement = [ + { + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + Service = "ec2.amazonaws.com" } }, ] + Version = "2012-10-17" } ) + create_date = (known after apply) + force_detach_policies = false + id = (known after apply) + managed_policy_arns = (known after apply) + max_session_duration = 3600 + name = "ec2-iam-role" + name_prefix = (known after apply) + path = "/" + tags_all = (known after apply) + unique_id = (known after apply) } # aws_instance.AdminVM will be created + resource "aws_instance" "AdminVM" { + ami = "ami-0ad8b6fa068e0299a" + arn = (known after apply) + associate_public_ip_address = (known after apply) + availability_zone = (known after apply) + cpu_core_count = (known after apply) + cpu_threads_per_core = (known after apply) + disable_api_stop = (known after apply) + disable_api_termination = (known after apply) + ebs_optimized = (known after apply) + get_password_data = false + host_id = (known after apply) + host_resource_group_arn = (known after apply) + iam_instance_profile = (known after apply) + id = (known after apply) + instance_initiated_shutdown_behavior = (known after apply) + instance_lifecycle = (known after apply) + instance_state = (known after apply) + instance_type = "t2.large" + ipv6_address_count = (known after apply) + ipv6_addresses = (known after apply) + key_name = (sensitive value) + monitoring = (known after apply) + outpost_arn = (known after apply) + password_data = (known after apply) + placement_group = (known after apply) + placement_partition_number = (known after apply) + primary_network_interface_id = (known after apply) + private_dns = (known after apply) + private_ip = "172.31.22.107" + public_dns = (known after apply) + public_ip = (known after apply) + secondary_private_ips = (known after apply) + security_groups = (known after apply) + source_dest_check = true + spot_instance_request_id = (known after apply) + subnet_id = "subnet-07e1XXXXXXXXXX" + tags = { + "Name" = "TACG-AWS-AVM" } + tags_all = { + "Name" = "TACG-AWS-AVM" } + tenancy = (known after apply) + user_data = "975296c878XXXXXXXXXX" + user_data_base64 = (known after apply) + user_data_replace_on_change = false + vpc_security_group_ids = [ + "sg-072eXXXXXXXXXX", ] } # aws_instance.CC1 will be created + resource "aws_instance" "CC1" { + ami = "ami-0ad8b6fa068e0299a" + arn = (known after apply) + associate_public_ip_address = (known after apply) + availability_zone = (known after apply) + cpu_core_count = (known after apply) + cpu_threads_per_core = (known after apply) + disable_api_stop = (known after apply) + disable_api_termination = (known after apply) + ebs_optimized = (known after apply) + get_password_data = false + host_id = (known after apply) + host_resource_group_arn = (known after apply) + iam_instance_profile = (known after apply) + id = (known after apply) + instance_initiated_shutdown_behavior = (known after apply) + instance_lifecycle = (known after apply) + instance_state = (known after apply) + instance_type = "t2.medium" + ipv6_address_count = (known after apply) + ipv6_addresses = (known after apply) + key_name = (sensitive value) + monitoring = (known after apply) + outpost_arn = (known after apply) + password_data = (known after apply) + placement_group = (known after apply) + placement_partition_number = (known after apply) + primary_network_interface_id = (known after apply) + private_dns = (known after apply) + private_ip = "172.31.22.104" + public_dns = (known after apply) + public_ip = (known after apply) + secondary_private_ips = (known after apply) + security_groups = (known after apply) + source_dest_check = true + spot_instance_request_id = (known after apply) + subnet_id = "subnet-07e1XXXXXXXXXX" + tags = { + "Name" = "TACG-AWS-CC1" } + tags_all = { + "Name" = "TACG-AWS-CC1" } + tenancy = (known after apply) + user_data = "5daf6ab616e8eXXXXXXXXXX" + user_data_base64 = (known after apply) + user_data_replace_on_change = false + vpc_security_group_ids = [ + "sg-072eXXXXXXXXXX", ] } # aws_instance.CC2 will be created + resource "aws_instance" "CC2" { + ami = "ami-0ad8b6fa068e0299a" + arn = (known after apply) + associate_public_ip_address = (known after apply) + availability_zone = (known after apply) + cpu_core_count = (known after apply) + cpu_threads_per_core = (known after apply) + disable_api_stop = (known after apply) + disable_api_termination = (known after apply) + ebs_optimized = (known after apply) + get_password_data = false + host_id = (known after apply) + host_resource_group_arn = (known after apply) + iam_instance_profile = (known after apply) + id = (known after apply) + instance_initiated_shutdown_behavior = (known after apply) + instance_lifecycle = (known after apply) + instance_state = (known after apply) + instance_type = "t2.medium" + ipv6_address_count = (known after apply) + ipv6_addresses = (known after apply) + key_name = (sensitive value) + monitoring = (known after apply) + outpost_arn = (known after apply) + password_data = (known after apply) + placement_group = (known after apply) + placement_partition_number = (known after apply) + primary_network_interface_id = (known after apply) + private_dns = (known after apply) + private_ip = "172.31.22.105" + public_dns = (known after apply) + public_ip = (known after apply) + secondary_private_ips = (known after apply) + security_groups = (known after apply) + source_dest_check = true + spot_instance_request_id = (known after apply) + subnet_id = "subnet-07e1XXXXXXXXXX" + tags = { + "Name" = "TACG-AWS-CC2" } + tags_all = { + "Name" = "TACG-AWS-CC2" } + tenancy = (known after apply) + user_data = "71b8c58dcf57XXXXXXXXXX" + user_data_base64 = (known after apply) + user_data_replace_on_change = false + vpc_security_group_ids = [ + "sg-072eXXXXXXXXXX", ] } # aws_instance.WMI will be created + resource "aws_instance" "WMI" { + ami = "ami-0ad8b6fa068e0299a" + arn = (known after apply) + associate_public_ip_address = (known after apply) + availability_zone = (known after apply) + cpu_core_count = (known after apply) + cpu_threads_per_core = (known after apply) + disable_api_stop = (known after apply) + disable_api_termination = (known after apply) + ebs_optimized = (known after apply) + get_password_data = false + host_id = (known after apply) + host_resource_group_arn = (known after apply) + iam_instance_profile = (known after apply) + id = (known after apply) + instance_initiated_shutdown_behavior = (known after apply) + instance_lifecycle = (known after apply) + instance_state = (known after apply) + instance_type = "t2.medium" + ipv6_address_count = (known after apply) + ipv6_addresses = (known after apply) + key_name = (sensitive value) + monitoring = (known after apply) + outpost_arn = (known after apply) + password_data = (known after apply) + placement_group = (known after apply) + placement_partition_number = (known after apply) + primary_network_interface_id = (known after apply) + private_dns = (known after apply) + private_ip = "172.31.22.106" + public_dns = (known after apply) + public_ip = (known after apply) + secondary_private_ips = (known after apply) + security_groups = (known after apply) + source_dest_check = true + spot_instance_request_id = (known after apply) + subnet_id = "subnet-07e168f0c2a28edf3" + tags = { + "Name" = "TACG-AWS-WMI" } + tags_all = { + "Name" = "TACG-AWS-WMI" } + tenancy = (known after apply) + user_data = "bd599dcdaa3dXXXXXXXXXXX" + user_data_base64 = (known after apply) + user_data_replace_on_change = false + vpc_security_group_ids = [ + "sg-072eXXXXXXXXXX", ] } # aws_vpc_dhcp_options.vpc-dhcp-options will be created + resource "aws_vpc_dhcp_options" "vpc-dhcp-options" { + arn = (known after apply) + domain_name_servers = [ + "172.31.22.103", ] + id = (known after apply) + owner_id = (known after apply) + tags_all = (known after apply) } # aws_vpc_dhcp_options_association.dns_resolver will be created + resource "aws_vpc_dhcp_options_association" "dns_resolver" { + dhcp_options_id = (known after apply) + id = (known after apply) + vpc_id = "vpc-0f9aXXXXXXXXXX" } # local_file.GetBearerToken will be created + resource "local_file" "GetBearerToken" { + content = (sensitive value) + content_base64sha256 = (known after apply) + content_base64sha512 = (known after apply) + content_md5 = (known after apply) + content_sha1 = (known after apply) + content_sha256 = (known after apply) + content_sha512 = (known after apply) + directory_permission = "0777" + file_permission = "0777" + filename = "./GetBT.ps1" + id = (known after apply) } # terraform_data.GetBT will be created + resource "terraform_data" "GetBT" { + id = (known after apply) } Plan: 14 to add, 0 to change, 0 to destroy. PS C:\TACG\_CCOnAWS\_CCOnAWS-Creation> PS C:\TACG\_CCOnAWS\_CCOnAWS-Creation> terraform apply data.template_file.Add-EC2InstanceToDomainScriptCC2: Reading... data.template_file.Add-EC2InstanceToDomainScriptWMI: Reading... data.template_file.Add-EC2InstanceToDomainScriptAdminVM: Reading... data.template_file.Add-EC2InstanceToDomainScriptCC1: Reading... data.template_file.Add-EC2InstanceToDomainScriptWMI: Read complete after 0s [id=85de6bac9e35231cbd60a4c1636a554940abb789938916a626a5193f27f22498] data.template_file.Add-EC2InstanceToDomainScriptCC1: Read complete after 0s [id=24ee722eca6982b33be472de4f84edbae000d0bff0a139dec2ce97c8ea14a0ca] data.template_file.Add-EC2InstanceToDomainScriptAdminVM: Read complete after 0s [id=91b3ae99f8d4a2effb377f35dda69a583de194739c8191ee665c96e663ad8615] data.template_file.Add-EC2InstanceToDomainScriptCC2: Read complete after 0s [id=15075f0d18ca3e200ab603e397339245a8ff055fd688facfc5165dd5e455d151] data.aws_iam_policy_document.ec2_assume_role: Reading... data.aws_iam_policy_document.ec2_assume_role: Read complete after 0s [id=2851119427] Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create <= read (data resources) Terraform will perform the following actions: # data.local_file.Retrieve_BT will be read during apply # (depends on a resource or a module with changes pending) <= data "local_file" "Retrieve_BT" { + content = (known after apply) + content_base64 = (known after apply) + content_base64sha256 = (known after apply) + content_base64sha512 = (known after apply) + content_md5 = (known after apply) + content_sha1 = (known after apply) + content_sha256 = (known after apply) + content_sha512 = (known after apply) + filename = "./GetBT.txt" + id = (known after apply) } ... ** Output shortened **... # terraform_data.GetBT will be created + resource "terraform_data" "GetBT" { + id = (known after apply) } Plan: 14 to add, 0 to change, 0 to destroy. Do you want to perform these actions? Terraform will perform the actions described above. Only 'yes' will be accepted to approve. Enter a value: yes aws_instance.AdminVM: Creating... aws_instance.AdminVM: Still creating... [10s elapsed] aws_instance.AdminVM: Still creating... [20s elapsed] aws_instance.AdminVM: Creation complete after 22s [id=i-0ad3352d673db8068] ... ** Output shortened **... aws_instance.WMI: Creating... aws_instance.WMI: Still creating... [10s elapsed] aws_instance.WMI: Still creating... [20s elapsed] aws_instance.WMI: Still creating... [30s elapsed] aws_instance.WMI: Creation complete after 34s [id=i-0ad3352d673db8068] Apply complete! Resources: 14 added, 0 changed, 0 destroyed. PS C:\TACG\_CCOnAWS\_CCOnAWS-Creation> As no errors occurred, Terraform has completed the creation and partial configuration of the relevant prerequisites on Amazon EC2. Example of successful creation: All VMs were successfully created and registered on the Domain Controller: Now the next step can be started. Module 2: Install and Configure all Resources in Amazon EC2 This module is split into the following configuration parts: Configuring the three previously created Virtual Machines on Amazon EC2: 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 Configuring the 2 CCs as Cloud Connectors Registering the 2 CCs in the newly created Resource Location Our provider currently does not support creating a Resource Location on Citrix Cloud. Therefore we use a PowerShell script to create it 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 configuration of the CCs to determine needed information like 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: Examples of necessary scripts: At first, Terraform writes the configuration file without the Resource Location ID: #### 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, Terraform runs a PowerShell script to get the needed ID and updates the configuration file for the CWC installer: #### Create PowerShell file for determining the correct RL-ID resource "local_file" "InstallPreReqsOnAVM2-ps1" { 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}" # Correct the Resource Location ID in cwc.json file $requestUri = "https://api-eu.cloud.com/resourcelocations" $headers = @{ "Accept"="application/json"; "Authorization" = "${var.CC_APIKey-Bearer}"; "Citrix-CustomerId" = "${var.CC_CustomerID}"} $response = Invoke-RestMethod -Uri $requestUri -Method GET -Headers $headers | Convertto-Json $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 Add-Content ${var.CC_Install_LogPath}/DATA/GetRLID.txt $RLID Add-Content ${var.CC_Install_LogPath}/log.txt "`ncwc.json corrected." Add-Content ${var.CC_Install_LogPath}/log.txt "`nScript completed." } EOT filename = "${path.module}/DATA/InstallPreReqsOnAVM2.ps1" } The Terraform configuration contains some idle time slots to make sure that background operations on Amazon EC2 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 Amazon EC2 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 .tg {border-collapse:collapse;border-spacing:0;width:100%} .tg .tg-5jvq{background-color:#0000b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px; text-align:left;vertical-align:top} PS C:\TACG\_CCOnAWS\_CCOnAWS-Install> terraform init Initializing the backend... Initializing provider plugins... - terraform.io/builtin/terraform is built in to Terraform - Finding latest version of hashicorp/random... - Finding latest version of hashicorp/time... - Finding latest version of hashicorp/local... - Finding latest version of hashicorp/null... - Finding hashicorp/aws versions matching ">= 5.4.0"... - Finding mastercard/restapi versions matching "1.18.2"... - Finding citrix/citrix versions matching ">= 0.5.2"... - Installing citrix/citrix v0.5.2... - Installed citrix/citrix v0.5.2 (signed by a HashiCorp partner, key ID 25D62DD8407EA386) - Installing hashicorp/random v3.6.0... - Installed hashicorp/random v3.6.0 (signed by HashiCorp) - Installing hashicorp/time v0.11.1... - Installed hashicorp/time v0.11.1 (signed by HashiCorp) - Installing hashicorp/local v2.5.1... - Installed hashicorp/local v2.5.1 (signed by HashiCorp) - Installing hashicorp/null v3.2.2... - Installed hashicorp/null v3.2.2 (signed by HashiCorp) - Installing hashicorp/aws v5.41.0... - Installed hashicorp/aws v5.41.0 (signed by HashiCorp) - Installing mastercard/restapi v1.18.2... - Installed mastercard/restapi v1.18.2 (self-signed, key ID DCB8C431D71C30AB) Partner and community providers are signed by their developers. If you'd like to know more about provider signing, you can read about it here: https://www.terraform.io/docs/cli/plugins/signing.html Terraform has created a lock file .terraform.lock.hcl to record the provider selections it made above. Include this file in your version control repository so that Terraform can guarantee to make the same selections by default when you run "terraform init" in the future. Terraform has been successfully initialized! You may now begin working with Terraform. Try running "terraform plan" to see any changes that are required for your infrastructure. All Terraform commands should now work. If you ever set or change modules or backend configuration for Terraform, rerun this command to reinitialize your working directory. If you forget, other commands will detect it and remind you to do so if necessary. PS C:\TACG\_CCOnAWS\_CCOnAWS-Install> PS C:\TACG\_CCOnAWS\_CCOnAWS-Install> terraform plan Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create <= read (data resources) Terraform will perform the following actions: # data.local_file.input_site will be read during apply # (depends on a resource or a module with changes pending) <= data "local_file" "input_site" { + content = (known after apply) + content_base64 = (known after apply) + content_base64sha256 = (known after apply) + content_base64sha512 = (known after apply) + content_md5 = (known after apply) + content_sha1 = (known after apply) + content_sha256 = (known after apply) + content_sha512 = (known after apply) + filename = "c:/temp/xdinst/DATA/GetSiteID.txt" + id = (known after apply) } ... ** Output shortened **... # terraform_data.SiteID will be created + resource "terraform_data" "SiteID" { + id = (known after apply) } # terraform_data.ZoneID will be created + resource "terraform_data" "ZoneID" { + id = (known after apply) } # time_sleep.wait_300_seconds will be created + resource "time_sleep" "wait_300_seconds" { + create_duration = "300s" + id = (known after apply) } Plan: 18 to add, 0 to change, 0 to destroy. ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now. PS C:\TACG\_CCOnAWS\_CCOnAWS-Install> PS C:\TACG\_CCOnAWS\_CCOnAWS-Install> terraform apply Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create <= read (data resources) Terraform will perform the following actions: # data.local_file.input_site will be read during apply # (depends on a resource or a module with changes pending) <= data "local_file" "input_site" { + content = (known after apply) + content_base64 = (known after apply) + content_base64sha256 = (known after apply) + content_base64sha512 = (known after apply) + content_md5 = (known after apply) + content_sha1 = (known after apply) + content_sha256 = (known after apply) + content_sha512 = (known after apply) + filename = "c:/temp/xdinst/DATA/GetSiteID.txt" + id = (known after apply) } # local_file.CWC-Configuration will be created + resource "local_file" "CWC-Configuration" { + content = (sensitive value) + content_base64sha256 = (known after apply) + content_base64sha512 = (known after apply) + content_md5 = (known after apply) + content_sha1 = (known after apply) + content_sha256 = (known after apply) + content_sha512 = (known after apply) + directory_permission = "0777" + file_permission = "0777" + filename = "c:/temp/xdinst/DATA/cwc.json" + id = (known after apply) } ... ** Output shortened **... # terraform_data.SiteID will be created + resource "terraform_data" "SiteID" { + id = (known after apply) } # terraform_data.ZoneID will be created + resource "terraform_data" "ZoneID" { + id = (known after apply) } # time_sleep.wait_300_seconds will be created + resource "time_sleep" "wait_300_seconds" { + create_duration = "300s" + id = (known after apply) } Plan: 18 to add, 0 to change, 0 to destroy. Do you want to perform these actions? Terraform will perform the actions described above. Only 'yes' will be accepted to approve. Enter a value: yes time_sleep.wait_300_seconds: Creating... random_uuid.IDforCCRL: Creating... random_uuid.IDforCCRL: Creation complete after 0s [id=76ef126a-4b14-cc24-e067-62dbf8c98d5c] local_file.InstallPreReqsOnCC-ps1: Creating... local_file.InstallPreReqsOnAVM1-ps1: Creating... local_file.InstallPreReqsOnAVM2-ps1: Creating... local_file.InstallPreReqsOnAVM1-ps1: Creation complete after 0s [id=c4e0ae63ee7a4c5ce89827fcf741942c19ae7aa0] local_file.InstallPreReqsOnCC-ps1: Creation complete after 0s [id=1927ce7666e1be3a10049619e515d97c2f1e031d] restapi_object.CreateRL: Creating... local_file.InstallPreReqsOnAVM2-ps1: Creation complete after 0s [id=dccb6ef781887e459e04d6c3866871fbc11d9868] restapi_object.CreateRL: Creation complete after 1s [id=76ef126a-4b14-cc24-e067-62dbf8c98d5c] local_file.CWC-Configuration: Creating... local_file.CWC-Configuration: Creation complete after 0s [id=0257d2b92f197fa4d043b6cd4c4959be284dddc2] time_sleep.wait_300_seconds: Still creating... [10s elapsed] time_sleep.wait_300_seconds: Still creating... [20s elapsed] time_sleep.wait_300_seconds: Still creating... [30s elapsed] time_sleep.wait_300_seconds: Still creating... [40s elapsed] time_sleep.wait_300_seconds: Still creating... [50s elapsed] ... ** Output shortened **... time_sleep.wait_300_seconds: Still creating... [4m31s elapsed] time_sleep.wait_300_seconds: Still creating... [4m41s elapsed] time_sleep.wait_300_seconds: Still creating... [4m51s elapsed] time_sleep.wait_300_seconds: Creation complete after 5m0s [id=2024-03-15T09:29:54Z] local_file.GetSiteIDScript: Creating... local_file.GetSiteIDScript: Creation complete after 0s [id=11e3d863bb343b8e10beca1d1b8d2e1918aff757] terraform_data.SiteID: Creating... terraform_data.SiteID: Provisioning with 'local-exec'... terraform_data.SiteID (local-exec): Executing: ["PowerShell" "-File" "GetSiteID.ps1"] null_resource.UploadRequiredComponentsToAVM: Creating... null_resource.UploadRequiredComponentsToAVM: Provisioning with 'file'... #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>terraform_data.SiteID: Creation complete after 2s [id=d8069268-d023-dc1f-b6e3-5eb4a96d7e34] data.local_file.input_site: Reading... data.local_file.input_site: Read complete after 0s [id=f684114a2b93cc095c4ac5f81999ee1a111d53b9] local_file.GetZoneIDScript: Creating... local_file.GetZoneIDScript: Creation complete after 0s [id=acf533cb047cc8f963f8bf53b792f236eb8d9cd3] terraform_data.ZoneID: Creating... terraform_data.ZoneID: Provisioning with 'local-exec'... terraform_data.ZoneID (local-exec): Executing: ["PowerShell" "-File" "GetZoneID.ps1"] #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>null_resource.UploadRequiredComponentsToAVM: Provisioning with 'file'... terraform_data.ZoneID: Creation complete after 0s [id=560905f8-976a-56ff-a73a-95f287a69761] #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>#< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>null_resource.UploadRequiredComponentsToAVM: Creation complete after 3s [id=1331150174844471895] null_resource.CallRequiredScriptsOnAVM1: Creating... null_resource.CallRequiredScriptsOnAVM1: Provisioning with 'remote-exec'... null_resource.CallRequiredScriptsOnAVM1 (remote-exec): Connecting to remote host via WinRM... null_resource.CallRequiredScriptsOnAVM1 (remote-exec): Host: 172.31.22.107 null_resource.CallRequiredScriptsOnAVM1 (remote-exec): Port: 5985 null_resource.CallRequiredScriptsOnAVM1 (remote-exec): User: administrator null_resource.CallRequiredScriptsOnAVM1 (remote-exec): Password: true null_resource.CallRequiredScriptsOnAVM1 (remote-exec): HTTPS: false null_resource.CallRequiredScriptsOnAVM1 (remote-exec): Insecure: false null_resource.CallRequiredScriptsOnAVM1 (remote-exec): NTLM: false null_resource.CallRequiredScriptsOnAVM1 (remote-exec): CACert: false null_resource.CallRequiredScriptsOnAVM1 (remote-exec): Connected! #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>#< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs> null_resource.CallRequiredScriptsOnAVM1 (remote-exec): C:\Users\Administrator>powershell -File c:/temp/xdinst/DATA/InstallPreReqsOnAVM1.ps1 null_resource.CallRequiredScriptsOnAVM1: Still creating... [10s elapsed] null_resource.CallRequiredScriptsOnAVM1: Still creating... [20s elapsed] null_resource.CallRequiredScriptsOnAVM1: Still creating... [30s elapsed] null_resource.CallRequiredScriptsOnAVM1: Still creating... [40s elapsed] null_resource.CallRequiredScriptsOnAVM1: Still creating... [50s elapsed] null_resource.CallRequiredScriptsOnAVM1: Still creating... [1m0s elapsed] null_resource.CallRequiredScriptsOnAVM1: Still creating... [1m11s elapsed] null_resource.CallRequiredScriptsOnAVM1: Still creating... [1m21s elapsed] null_resource.CallRequiredScriptsOnAVM1: Still creating... [1m31s elapsed] #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj><Obj S="progress" RefId="1"><TNRef RefId="0" /><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>#< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>null_resource.CallRequiredScriptsOnAVM1: Creation complete after 1m32s [id=1765564400293269918] null_resource.CallRequiredScriptsOnAVM2: Creating... null_resource.CallRequiredScriptsOnAVM2: Provisioning with 'remote-exec'... null_resource.CallRequiredScriptsOnAVM2 (remote-exec): Connecting to remote host via WinRM... null_resource.CallRequiredScriptsOnAVM2 (remote-exec): Host: 172.31.22.107 null_resource.CallRequiredScriptsOnAVM2 (remote-exec): Port: 5985 null_resource.CallRequiredScriptsOnAVM2 (remote-exec): User: administrator null_resource.CallRequiredScriptsOnAVM2 (remote-exec): Password: true null_resource.CallRequiredScriptsOnAVM2 (remote-exec): HTTPS: false null_resource.CallRequiredScriptsOnAVM2 (remote-exec): Insecure: false null_resource.CallRequiredScriptsOnAVM2 (remote-exec): NTLM: false null_resource.CallRequiredScriptsOnAVM2 (remote-exec): CACert: false null_resource.CallRequiredScriptsOnAVM2 (remote-exec): Connected! #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>#< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs> null_resource.CallRequiredScriptsOnAVM2 (remote-exec): C:\Users\Administrator>powershell -File c:/temp/xdinst/DATA/InstallPreReqsOnAVM2.ps1 #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj><Obj S="progress" RefId="1"><TNRef RefId="0" /><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>#< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>null_resource.CallRequiredScriptsOnAVM2: Creation complete after 3s [id=1571484748961023525] null_resource.UploadRequiredComponentsToCC1: Creating... null_resource.UploadRequiredComponentsToCC2: Creating... null_resource.UploadRequiredComponentsToCC1: Provisioning with 'file'... null_resource.UploadRequiredComponentsToCC2: Provisioning with 'file'... #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>#< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>null_resource.UploadRequiredComponentsToCC2: Provisioning with 'file'... #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>#< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>null_resource.UploadRequiredComponentsToCC2: Provisioning with 'file'... ... ** Output shortened **... #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>null_resource.UploadRequiredComponentsToCC1: Provisioning with 'file'... #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>#< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>null_resource.UploadRequiredComponentsToCC2: Creation complete after 8s [id=2593060114553604983] null_resource.CallRequiredScriptsOnCC2: Creating... null_resource.CallRequiredScriptsOnCC2: Provisioning with 'remote-exec'... null_resource.CallRequiredScriptsOnCC2 (remote-exec): Connecting to remote host via WinRM... null_resource.CallRequiredScriptsOnCC2 (remote-exec): Host: 172.31.22.107 null_resource.CallRequiredScriptsOnCC2 (remote-exec): Port: 5985 null_resource.CallRequiredScriptsOnCC2 (remote-exec): User: administrator null_resource.CallRequiredScriptsOnCC2 (remote-exec): Password: true null_resource.CallRequiredScriptsOnCC2 (remote-exec): HTTPS: false null_resource.CallRequiredScriptsOnCC2 (remote-exec): Insecure: false null_resource.CallRequiredScriptsOnCC2 (remote-exec): NTLM: false null_resource.CallRequiredScriptsOnCC2 (remote-exec): CACert: false null_resource.CallRequiredScriptsOnCC2 (remote-exec): Connected! #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>#< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj><Obj S="progress" RefId="1"><TNRef RefId="0" /><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>#< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs> null_resource.CallRequiredScriptsOnCC2 (remote-exec): C:\Users\Administrator>powershell -File c:/temp/xdinst/InstallPreReqsOnCC.ps1} null_resource.CallRequiredScriptsOnCC2 (remote-exec): Windows PowerShell null_resource.CallRequiredScriptsOnCC2 (remote-exec): Processing -File 'c:/temp/xdinst/InstallPreReqsOnCC.ps1}' null_resource.CallRequiredScriptsOnCC2 (remote-exec): Copyright (C) Microsoft Corporation. All rights reserved. null_resource.CallRequiredScriptsOnCC2 (remote-exec): Install the latest PowerShell for new features and improvements! https://aka.ms/PSWindows null_resource.UploadRequiredComponentsToCC1: Still creating... [10s elapsed] #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>null_resource.UploadRequiredComponentsToCC1: Provisioning with 'file'... #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>#< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>null_resource.UploadRequiredComponentsToCC1: Provisioning with 'file'... #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>#< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>null_resource.UploadRequiredComponentsToCC1: Creation complete after 15s [id=2911410724639395293] null_resource.CallRequiredScriptsOnCC1: Creating... null_resource.CallRequiredScriptsOnCC1: Provisioning with 'remote-exec'... null_resource.CallRequiredScriptsOnCC1 (remote-exec): Connecting to remote host via WinRM... null_resource.CallRequiredScriptsOnCC1 (remote-exec): Host: 172.31.22.107 null_resource.CallRequiredScriptsOnCC1 (remote-exec): Port: 5985 null_resource.CallRequiredScriptsOnCC1 (remote-exec): User: administrator null_resource.CallRequiredScriptsOnCC1 (remote-exec): Password: true null_resource.CallRequiredScriptsOnCC1 (remote-exec): HTTPS: false null_resource.CallRequiredScriptsOnCC1 (remote-exec): Insecure: false null_resource.CallRequiredScriptsOnCC1 (remote-exec): NTLM: false null_resource.CallRequiredScriptsOnCC1 (remote-exec): CACert: false null_resource.CallRequiredScriptsOnCC1 (remote-exec): Connected! #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>#< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>#< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>null_resource.UploadRequiredComponentsToCC1: Provisioning with 'file'... #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>null_resource.UploadRequiredComponentsToCC2: Provisioning with 'file'... #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>#< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>#< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>#< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>null_resource.UploadRequiredComponentsToCC2: Provisioning with 'file'... null_resource.UploadRequiredComponentsToCC1: Provisioning with 'file'... ... ** Output shortened **... #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>null_resource.UploadRequiredComponentsToCC1: Provisioning with 'file'... #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>#< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>#< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>null_resource.UploadRequiredComponentsToCC2: Provisioning with 'file'... #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>null_resource.UploadRequiredComponentsToCC1: Provisioning with 'file'... #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>#< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>#< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>null_resource.UploadRequiredComponentsToCC1: Creation complete after 7s [id=8529594446213212779] null_resource.CallRequiredScriptsOnCC1: Creating... null_resource.CallRequiredScriptsOnCC1: Provisioning with 'remote-exec'... null_resource.CallRequiredScriptsOnCC1 (remote-exec): Connecting to remote host via WinRM... null_resource.CallRequiredScriptsOnCC1 (remote-exec): Host: 172.31.22.107 null_resource.CallRequiredScriptsOnCC1 (remote-exec): Port: 5985 null_resource.CallRequiredScriptsOnCC1 (remote-exec): User: administrator null_resource.CallRequiredScriptsOnCC1 (remote-exec): Password: true null_resource.CallRequiredScriptsOnCC1 (remote-exec): HTTPS: false null_resource.CallRequiredScriptsOnCC1 (remote-exec): Insecure: false null_resource.CallRequiredScriptsOnCC1 (remote-exec): NTLM: false null_resource.CallRequiredScriptsOnCC1 (remote-exec): CACert: false #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>null_resource.UploadRequiredComponentsToCC2: Creation complete after 8s [id=5071991036813727940] null_resource.CallRequiredScriptsOnCC2: Creating... null_resource.CallRequiredScriptsOnCC2: Provisioning with 'remote-exec'... null_resource.CallRequiredScriptsOnCC2 (remote-exec): Connecting to remote host via WinRM... null_resource.CallRequiredScriptsOnCC2 (remote-exec): Host: 172.31.22.107 null_resource.CallRequiredScriptsOnCC2 (remote-exec): Port: 5985 null_resource.CallRequiredScriptsOnCC2 (remote-exec): User: administrator null_resource.CallRequiredScriptsOnCC2 (remote-exec): Password: true null_resource.CallRequiredScriptsOnCC2 (remote-exec): HTTPS: false null_resource.CallRequiredScriptsOnCC2 (remote-exec): Insecure: false null_resource.CallRequiredScriptsOnCC2 (remote-exec): NTLM: false null_resource.CallRequiredScriptsOnCC2 (remote-exec): CACert: false null_resource.CallRequiredScriptsOnCC1 (remote-exec): Connected! null_resource.CallRequiredScriptsOnCC2 (remote-exec): Connected! #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>#< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>#< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>#< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs> null_resource.CallRequiredScriptsOnCC1 (remote-exec): C:\Users\Administrator>powershell -File c:/temp/xdinst/DATA/InstallPreReqsOnCC.ps1 ... ** Output shortened **... Apply complete! Resources: 18 added, 0 changed, 0 destroyed. PS C:\TACG\_CCOnAWS\_CCOnAWS-Install> This configuration completes the creation and configuration of all initial resources: Installing the needed software on the CCs Creating a Resource Location in Citrix Cloud Configuring the 2 CCs as Cloud Connectors Registering the 2 CCs 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 2 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 Amazon EC2 and Citrix Cloud This module is split into the following configuration parts: Creating a Hypervisor Connection to Amazon EC2 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 make sure that background operations on Amazon EC2 or the VMs can be completed before the next configuration steps occur. We have seen different elapsed configuration times related to different loads on the Amazon EC2 systems! Before Terraform can create the Hypervisor Connection and the Hypervisor Pool, Terraform needs to 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 ran them in Module 2. After retrieving the IDs, Terraform configures a Hypervisor Connection to Amazon EC2 and a Hypervisor Resource Pool associated with the Hypervisor Connection. When these prerequisites are completed, the Machine Catalog is created. After the successful creation of the Hypervisor Connection, the Hypervisor Resource Pool, and the Machine Catalog the last step of the deployment process starts - the creation of 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 The deployment of Citrix Policies is a new feature that was built-in in version 0.5.2. We need to know the internal policy name as localized policy names and descriptions are not usable. Therefore we need to use a PowerShell script to determine all internal names - some pre-requisites are necessary for the script to work. You can need any machine but the Cloud Connectors! Install the Citrix Supportability Pack Install the Citrix Group Policy Management - scroll down to Group Policy .tg {border-collapse:collapse;border-spacing:0;width:100%} .tg .tg-5jvq{background-color:#0000b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px; text-align:left;vertical-align:top} Import-Module "C:\TACG\Supportability Pack\Tools\Scout\Current\Utilities\Citrix.GroupPolicy.commands.psm1" -force new-psdrive -name LocalFarmGpo -psprovider CitrixGroupPolicy -controller localhost \ Get-PSDrive cd LocalFarmGpo: Get-CtxGroupPolicyConfiguration Type: User ProfileLoadTimeMonitoring_Threshold ICALatencyMonitoring_Enable ICALatencyMonitoring_Period ICALatencyMonitoring_Threshold EnableLossless ProGraphics FRVideos_Part FRVideosPath_Part FRStartMenu_Part FRStartMenuPath_Part FRSearches_Part FRSearchesPath_Part FRSavedGames_Part FRSavedGamesPath_Part FRPictures_Part FRPicturesPath_Part FRMusic_Part FRMusicPath_Part FRLinks_Part FRLinksPath_Part FRFavorites_Part FRFavoritesPath_Part FRDownloads_Part FRDownloadsPath_Part FRDocuments_Part FRDocumentsPath_Part FRDesktop_Part FRDesktopPath_Part FRContacts_Part FRContactsPath_Part FRAdminAccess_Part FRIncDomainName_Part FRAppData_Part FRAppDataPath_Part StorefrontAccountsList AllowFidoRedirection AllowWIARedirection ClientClipboardWriteAllowedFormats ClipboardRedirection ClipboardSelectionUpdateMode DesktopLaunchForNonAdmins DragDrop LimitClipboardTransferC2H LimitClipboardTransferH2C LossTolerantModeAvailable NonPublishedProgramLaunching PrimarySelectionUpdateMode ReadonlyClipboard RestrictClientClipboardWrite RestrictSessionClipboardWrite SessionClipboardWriteAllowedFormats FramesPerSecond PreferredColorDepthForSimpleGraphics VisualQuality ExtraColorCompression ExtraColorCompressionThreshold LossyCompressionLevel LossyCompressionThreshold ProgressiveHeavyweightCompression MinimumAdaptiveDisplayJpegQuality MovingImageCompressionConfiguration ProgressiveCompressionLevel ProgressiveCompressionThreshold TargetedMinimumFramesPerSecond ClientUsbDeviceOptimizationRules UsbConnectExistingDevices UsbConnectNewDevices UsbDeviceRedirection UsbDeviceRedirectionRules USBDeviceRulesV2 UsbPlugAndPlayRedirection TwainCompressionLevel TwainRedirection LocalTimeEstimation RestoreServerTime SessionTimeZone EnableSessionWatermark WatermarkStyle WatermarkTransparency WatermarkCustomText WatermarkIncludeClientIPAddress WatermarkIncludeConnectTime WatermarkIncludeLogonUsername WatermarkIncludeVDAHostName WatermarkIncludeVDAIPAddress EnableRemotePCDisconnectTimer SessionConnectionTimer SessionConnectionTimerInterval SessionDisconnectTimer SessionDisconnectTimerInterval SessionIdleTimer SessionIdleTimerInterval LossTolerantThresholds EnableServerConnectionTimer EnableServerDisconnectionTimer EnableServerIdleTimer ServerConnectionTimerInterval ServerDisconnectionTimerInterval ServerIdleTimerInterval MinimumEncryptionLevel AutoCreationEventLogPreference ClientPrinterRedirection DefaultClientPrinter PrinterAssignments SessionPrinters WaitForPrintersToBeCreated UpsPrintStreamInputBandwidthLimit DPILimit EMFProcessingMode ImageCompressionLimit UniversalPrintingPreviewPreference UPDCompressionDefaults InboxDriverAutoInstallation UniversalDriverPriority UniversalPrintDriverUsage AutoCreatePDFPrinter ClientPrinterAutoCreation ClientPrinterNames DirectConnectionsToPrintServers GenericUniversalPrinterAutoCreation PrinterDriverMappings PrinterPropertiesRetention ClientComPortRedirection ClientComPortsAutoConnection ClientLptPortRedirection ClientLptPortsAutoConnection MaxSpeexQuality MSTeamsRedirection MultimediaOptimization UseGPUForMultimediaOptimization VideoLoadManagement VideoQuality WebBrowserRedirectionAcl WebBrowserRedirectionAuthenticationSites WebBrowserRedirectionBlacklist WebBrowserRedirectionIwaSupport WebBrowserRedirectionProxy WebBrowserRedirectionProxyAuth MultiStream AutoKeyboardPopUp ComboboxRemoting MobileDesktop TabletModeToggle ClientKeyboardLayoutSyncAndIME EnableUnicodeKeyboardLayoutMapping HideKeyboardLayoutSwitchPopupMessageBox AllowVisuallyLosslessCompression DisplayLosslessIndicator OptimizeFor3dWorkload ScreenSharing UseHardwareEncodingForVideoCodec UseVideoCodecForCompression EnableFramehawkDisplayChannel AllowFileDownload AllowFileTransfer AllowFileUpload AsynchronousWrites AutoConnectDrives ClientDriveLetterPreservation ClientDriveRedirection ClientFixedDrives ClientFloppyDrives ClientNetworkDrives ClientOpticalDrives ClientRemoveableDrives HostToClientRedirection ReadOnlyMappedDrive SpecialFolderRedirection AeroRedirection DesktopWallpaper GraphicsQuality MenuAnimation WindowContentsVisibleWhileDragging AllowLocationServices AllowBidirectionalContentRedirection BidirectionalRedirectionConfig ClientURLs VDAURLs AudioBandwidthLimit AudioBandwidthPercent ClipboardBandwidthLimit ClipboardBandwidthPercent ComPortBandwidthLimit ComPortBandwidthPercent FileRedirectionBandwidthLimit FileRedirectionBandwidthPercent HDXMultimediaBandwidthLimit HDXMultimediaBandwidthPercent LptBandwidthLimit LptBandwidthLimitPercent OverallBandwidthLimit PrinterBandwidthLimit PrinterBandwidthPercent TwainBandwidthLimit TwainBandwidthPercent USBBandwidthLimit USBBandwidthPercent AllowRtpAudio AudioPlugNPlay AudioQuality ClientAudioRedirection EnableAdaptiveAudio MicrophoneRedirection FlashAcceleration FlashBackwardsCompatibility FlashDefaultBehavior FlashEventLogging FlashIntelligentFallback FlashLatencyThreshold FlashServerSideContentFetchingWhitelist FlashUrlColorList FlashUrlCompatibilityList HDXFlashLoadManagement HDXFlashLoadManagementErrorSwf Type: Computer WemCloudConnectorList VirtualLoopbackPrograms VirtualLoopbackSupport EnableAutoUpdateOfControllers AppFailureExclusionList EnableProcessMonitoring EnableResourceMonitoring EnableWorkstationVDAFaultMonitoring SelectedFailureLevel CPUUsageMonitoring_Enable CPUUsageMonitoring_Period CPUUsageMonitoring_Threshold VdcPolicyEnable EnableClipboardMetadataCollection EnableVdaDiagnosticsCollection XenAppOptimizationDefinitionPathData XenAppOptimizationEnabled ExclusionList_Part IncludeListRegistry_Part LastKnownGoodRegistry DefaultExclusionList ExclusionDefaultReg01 ExclusionDefaultReg02 ExclusionDefaultReg03 PSAlwaysCache PSAlwaysCache_Part PSEnabled PSForFoldersEnabled PSForPendingAreaEnabled PSPendingLockTimeout PSUserGroups_Part StreamingExclusionList_Part ApplicationProfilesAutoMigration DeleteCachedProfilesOnLogoff LocalProfileConflictHandling_Part MigrateWindowsProfilesToUserStore_Part ProfileDeleteDelay_Part TemplateProfileIsMandatory TemplateProfileOverridesLocalProfile TemplateProfileOverridesRoamingProfile TemplateProfilePath DisableConcurrentAccessToOneDriveContainer DisableConcurrentAccessToProfileContainer EnableVHDAutoExtend EnableVHDDiskCompaction GroupsToAccessProfileContainer_Part PreventLoginWhenMountFailed_Part ProfileContainerExclusionListDir_Part ProfileContainerExclusionListFile_Part ProfileContainerInclusionListDir_Part ProfileContainerInclusionListFile_Part ProfileContainerLocalCache DebugFilePath_Part DebugMode LogLevel_ActiveDirectoryActions LogLevel_FileSystemActions LogLevel_FileSystemNotification LogLevel_Information LogLevel_Logoff LogLevel_Logon LogLevel_PolicyUserLogon LogLevel_RegistryActions LogLevel_RegistryDifference LogLevel_UserName LogLevel_Warnings MaxLogSize_Part LargeFileHandlingList_Part LogonExclusionCheck_Part AccelerateFolderMirroring MirrorFoldersList_Part ProfileContainer_Part SyncDirList_Part SyncFileList_Part ExclusionListSyncDir_Part ExclusionListSyncFiles_Part DefaultExclusionListSyncDir ExclusionDefaultDir01 ExclusionDefaultDir02 ExclusionDefaultDir03 ExclusionDefaultDir04 ExclusionDefaultDir05 ExclusionDefaultDir06 ExclusionDefaultDir07 ExclusionDefaultDir08 ExclusionDefaultDir09 ExclusionDefaultDir10 ExclusionDefaultDir11 ExclusionDefaultDir12 ExclusionDefaultDir13 ExclusionDefaultDir14 ExclusionDefaultDir15 ExclusionDefaultDir16 ExclusionDefaultDir17 ExclusionDefaultDir18 ExclusionDefaultDir19 ExclusionDefaultDir20 ExclusionDefaultDir21 ExclusionDefaultDir22 ExclusionDefaultDir23 ExclusionDefaultDir24 ExclusionDefaultDir25 ExclusionDefaultDir26 ExclusionDefaultDir27 ExclusionDefaultDir28 ExclusionDefaultDir29 ExclusionDefaultDir30 SharedStoreFileExclusionList_Part SharedStoreFileInclusionList_Part SharedStoreProfileContainerFileSizeLimit_Part CPEnable CPMigrationFromBaseProfileToCPStore CPPathData CPSchemaPathData CPUserGroups_Part DATPath_Part ExcludedGroups_Part MigrateUserStore_Part OfflineSupport ProcessAdmins ProcessedGroups_Part PSMidSessionWriteBack PSMidSessionWriteBackReg PSMidSessionWriteBackSessionLock ServiceActive AppAccessControl_Part CEIPEnabled CredBasedAccessEnabled DisableDynamicConfig EnableVolumeReattach FreeRatio4Compaction_Part FSLogixProfileContainerSupport LoadRetries_Part LogoffRatherThanTempProfile MultiSiteReplication_Part NDefrag4Compaction NLogoffs4Compaction_Part OneDriveContainer_Part OrderedGroups_Part OutlookEdbBackupEnabled OutlookSearchRoamingConcurrentSession OutlookSearchRoamingConcurrentSession_Part OutlookSearchRoamingEnabled ProcessCookieFiles SyncGpoStateEnabled UserGroupLevelConfigEnabled UserStoreSelection_Part UwpAppsRoaming VhdAutoExpansionIncrement_Part VhdAutoExpansionLimit_Part VhdAutoExpansionThreshold_Part VhdContainerCapacity_Part VhdStorePath_Part UplCustomizedUserLayerSizeInGb UplGroupsUsingCustomizedUserLayerSize UplRepositoryPath UplUserExclusions UplUserLayerSizeInGb ConcurrentLogonsTolerance CPUUsage CPUUsageExcludedProcessPriority DiskUsage MaximumNumberOfSessions MemoryUsage MemoryUsageBaseLoad ApplicationLaunchWaitTimeout HDXAdaptiveTransport HDXDirect HDXDirectMode HDXDirectPortRange IcaListenerPortNumber IcaListenerTimeout LogoffCheckerStartupDelay RemoteCredentialGuard RendezvousProtocol RendezvousProxy SecureHDX VdaUpgradeProxy VirtualChannelWhiteList VirtualChannelWhiteListLogging VirtualChannelWhiteListLogThrottling AcceptWebSocketsConnections WebSocketsPort WSTrustedOriginServerList SessionReliabilityConnections SessionReliabilityPort SessionReliabilityTimeout IdleTimerInterval LoadBalancedPrintServers PrintServersOutOfServiceThreshold UpcHttpConnectTimeout UpcHttpReceiveTimeout UpcHttpSendTimeout UpcSslCgpPort UpcSslCipherSuite UpcSslComplianceMode UpcSslEnable UpcSslFips UpcSslHttpsPort UpcSslProtocolVersion UpsCgpPort UpsEnable UpsHttpPort HTML5VideoRedirection MultimediaAcceleration MultimediaAccelerationDefaultBufferSize MultimediaAccelerationEnableCSF MultimediaAccelerationUseDefaultBufferSize MultimediaConferencing WebBrowserRedirection MultiPortPolicy MultiStreamAssignment MultiStreamPolicy RtpAudioPortRange UDPAudioOnServer AllowLocalAppAccess URLRedirectionBlackList URLRedirectionWhiteList IcaKeepAlives IcaKeepAliveTimeout DisplayDegradePreference DisplayDegradeUserNotification DisplayMemoryLimit DynamicPreview ImageCaching LegacyGraphicsMode MaximumColorDepth QueueingAndTossing FramehawkDisplayChannelPortRange PersistentCache EnhancedDesktopExperience IcaRoundTripCalculation IcaRoundTripCalculationInterval IcaRoundTripCalculationWhenIdle ACRTimeout AutoClientReconnect AutoClientReconnectAuthenticationRequired AutoClientReconnectLogging ReconnectionUiTransparencyLevel AppProtectionPostureCheck AdvanceWarningFrequency AdvanceWarningMessageTitle AdvanceWarningPeriod AgentTaskInterval FinalForceLogoffMessageBody FinalForceLogoffMessageTitle ForceLogoffGracePeriod ForceLogoffMessageTitle ImageProviderIntegrationEnabled RebootMessageBody Using these names allows the deployment of policies by using the Terraform provider. Caution: Before running Terraform no Terraform-related entities are available: The configuration can be started by following the normal Terraform workflow: terraform init, terraform plan and if no errors occur terraform apply .tg {border-collapse:collapse;border-spacing:0;width:100%} .tg .tg-5jvq{background-color:#0000b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px; text-align:left;vertical-align:top} PS C:\TACG\_CCOnAWS\_CCOnAWS-CCStuff> terraform init Initializing the backend... Initializing provider plugins... - Finding citrix/citrix versions matching ">= 0.5.2"... - Finding hashicorp/aws versions matching ">= 5.4.0"... - Finding mastercard/restapi versions matching "1.18.2"... - Finding latest version of hashicorp/local... - Installing hashicorp/aws v5.41.0... - Installed hashicorp/aws v5.41.0 (signed by HashiCorp) - Installing mastercard/restapi v1.18.2... - Installed mastercard/restapi v1.18.2 (self-signed, key ID DCB8C431D71C30AB) - Installing hashicorp/local v2.5.1... - Installed hashicorp/local v2.5.1 (signed by HashiCorp) - Installing citrix/citrix v0.5.2... - Installed citrix/citrix v0.5.2 (signed by a HashiCorp partner, key ID 25D62DD8407EA386) Partner and community providers are signed by their developers. If you'd like to know more about provider signing, you can read about it here: https://www.terraform.io/docs/cli/plugins/signing.html Terraform has created a lock file .terraform.lock.hcl to record the provider selections it made above. Include this file in your version control repository so that Terraform can guarantee to make the same selections by default when you run "terraform init" in the future. Terraform has been successfully initialized! You may now begin working with Terraform. Try running "terraform plan" to see any changes that are required for your infrastructure. All Terraform commands should now work. If you ever set or change modules or backend configuration for Terraform, rerun this command to reinitialize your working directory. If you forget, other commands will detect it and remind you to do so if necessary. PS C:\TACG\_CCOnAWS\_CCOnAWS-CCStuff> PS C:\TACG\_CCOnAWS\_CCOnAWS-CCStuff> terraform plan data.local_file.LoadZoneID: Reading... data.local_file.LoadZoneID: Read complete after 0s [id=a7592ebe91057eab80084fc014fa06ca52453732] data.aws_vpc.AWSAZ: Reading... data.aws_vpc.AWSVPC: Reading... data.aws_subnet.AWSSubnet: Reading... data.aws_subnet.AWSSubnet: Read complete after 0s [id=subnet-07e168f0c2a28edf3] data.aws_vpc.AWSVPC: Read complete after 0s [id=vpc-0f9ac384f3bf8cb3a] data.aws_vpc.AWSAZ: Read complete after 0s [id=vpc-0f9ac384f3bf8cb3a] Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # aws_ami_from_instance.CreateAMIFromWMI will be created + resource "aws_ami_from_instance" "CreateAMIFromWMI" { + architecture = (known after apply) + arn = (known after apply) + boot_mode = (known after apply) + ena_support = (known after apply) + hypervisor = (known after apply) + id = (known after apply) + image_location = (known after apply) + image_owner_alias = (known after apply) + image_type = (known after apply) + imds_support = (known after apply) + kernel_id = (known after apply) + manage_ebs_snapshots = (known after apply) + name = "TACG-AWS-TF-AMIFromWMI" + owner_id = (known after apply) + platform = (known after apply) + platform_details = (known after apply) + public = (known after apply) + ramdisk_id = (known after apply) + root_device_name = (known after apply) + root_snapshot_id = (known after apply) + source_instance_id = "i-024f77470f3f63c08" + sriov_net_support = (known after apply) + tags_all = (known after apply) + tpm_support = (known after apply) + usage_operation = (known after apply) + virtualization_type = (known after apply) + timeouts { + create = "45m" } } # citrix_aws_hypervisor.CreateHypervisorConnection will be created + resource "citrix_aws_hypervisor" "CreateHypervisorConnection" { + api_key = (sensitive value) + id = (known after apply) + name = "TACG-AWS-TF-HypConn" + region = (sensitive value) + secret_key = (sensitive value) + zone = "8d5dXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" } # citrix_aws_hypervisor_resource_pool.CreateHypervisorPool will be created + resource "citrix_aws_hypervisor_resource_pool" "CreateHypervisorPool" { + availability_zone = "eu-central-1a" + hypervisor = (known after apply) + id = (known after apply) + name = "TACG-AWS-TF-HypConnPool" + subnets = [ + "172.31.16.0/20", ] + vpc = "TACG-VPC" } # citrix_machine_catalog.CreateMCSCatalog will be created + resource "citrix_machine_catalog" "CreateMCSCatalog" { + allocation_type = "Random" + description = "Terraform-based Machine Catalog" + id = (known after apply) + is_power_managed = true + is_remote_pc = false + name = "MC-TACG-AWS-TF" + provisioning_scheme = { + aws_machine_config = { + image_ami = (known after apply) + master_image = "TACG-AWS-TF-AMIFromWMI" + service_offering = "T2 Large Instance" } + hypervisor = (known after apply) + hypervisor_resource_pool = (known after apply) + identity_type = "ActiveDirectory" + machine_account_creation_rules = { + naming_scheme = "TACG-AWS-WM-#" + naming_scheme_type = "Numeric" } + machine_domain_identity = { + domain = "aws.the-austrian-citrix-guy.at" + domain_ou = "CN=Computers,DC=aws,DC=the-austrian-citrix-guy,DC=at" + service_account = (sensitive value) + service_account_password = (sensitive value) } + network_mapping = { + network = "172.31.16.0/20" + network_device = "0" } + number_of_total_machines = 1 } + provisioning_type = "MCS" + session_support = "MultiSession" + zone = "8d5dXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" + minimum_functional_level = "L7_20" } # citrix_delivery_group.CreateDG will be created + resource "citrix_delivery_group" "CreateDG" { + associated_machine_catalogs = [ + { + machine_catalog = "f4e34a11-6e31-421f-8cb4-060bc4a13fef" + machine_count = 1 }, ] + autoscale_settings = { + autoscale_enabled = true + disconnect_off_peak_idle_session_after_seconds = 0 + disconnect_peak_idle_session_after_seconds = 300 + log_off_off_peak_disconnected_session_after_seconds = 0 + log_off_peak_disconnected_session_after_seconds = 300 + off_peak_buffer_size_percent = 0 + off_peak_disconnect_action = "Nothing" + off_peak_disconnect_timeout_minutes = 0 + off_peak_extended_disconnect_action = "Nothing" + off_peak_extended_disconnect_timeout_minutes = 0 + off_peak_log_off_action = "Nothing" + peak_buffer_size_percent = 0 + peak_disconnect_action = "Nothing" + peak_disconnect_timeout_minutes = 0 + peak_extended_disconnect_action = "Nothing" + peak_extended_disconnect_timeout_minutes = 0 + peak_log_off_action = "Nothing" + power_off_delay_minutes = 30 + power_time_schemes = [ + { + days_of_week = [ + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", ] + display_name = "TACG-AWS-TF-AS-Weekdays" + peak_time_ranges = [ + "09:00-17:00", ] + pool_size_schedules = [ + { + pool_size = 1 + time_range = "09:00-17:00" }, ] + pool_using_percentage = false }, ] } + desktops = [ + { + description = "Terraform-based Delivery Group running on AWS EC2" + enable_session_roaming = true + enabled = true + published_name = "DG-TF-TACG-AWS" + restricted_access_users = { + allow_list = [ + "TACG-AWS\\vdaallowed", ] } }, ] + id = (known after apply) + name = "DG-TF-TACG-AWS" + reboot_schedules = [ + { + days_in_week = [ + "Sunday", ] + frequency = "Weekly" + frequency_factor = 1 + ignore_maintenance_mode = true + name = "TACG-AWS-Reboot Schedule" + natural_reboot_schedule = false + reboot_duration_minutes = 0 + reboot_schedule_enabled = true + start_date = "2024-01-01" + start_time = "02:00" }, ] + restricted_access_users = { + allow_list = [ + "TACG-AWS\\vdaallowed", ] } + total_machines = (known after apply) } # time_sleep.Wait_60_Seconds_2 will be created + resource "time_sleep" "Wait_60_Seconds_2" { + create_duration = "60s" + id = (known after apply) } # time_sleep.wait_60_seconds_1 will be created + resource "time_sleep" "wait_60_seconds_1" { + create_duration = "60s" + id = (known after apply) } Plan: 8 to add, 0 to change, 0 to destroy. ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now. PS C:\TACG\_CCOnAWS\_CCOnAWS-CCStuff> PS C:\TACG\_CCOnAWS\_CCOnAWS-CCStuff> terraform apply data.local_file.LoadZoneID: Reading... data.local_file.LoadZoneID: Read complete after 0s [id=a7592ebe91057eab80084fc014fa06ca52453732] data.aws_subnet.AWSSubnet: Reading... data.aws_vpc.AWSVPC: Reading... data.aws_vpc.AWSAZ: Reading... data.aws_subnet.AWSSubnet: Read complete after 0s [id=subnet-07e168f0c2a28edf3] data.aws_vpc.AWSAZ: Read complete after 0s [id=vpc-0f9ac384f3bf8cb3a] data.aws_vpc.AWSVPC: Read complete after 0s [id=vpc-0f9ac384f3bf8cb3a] Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # aws_ami_from_instance.CreateAMIFromWMI will be created + resource "aws_ami_from_instance" "CreateAMIFromWMI" { + architecture = (known after apply) + arn = (known after apply) + boot_mode = (known after apply) + ena_support = (known after apply) + hypervisor = (known after apply) + id = (known after apply) + image_location = (known after apply) + image_owner_alias = (known after apply) + image_type = (known after apply) + imds_support = (known after apply) + kernel_id = (known after apply) + manage_ebs_snapshots = (known after apply) + name = "TACG-AWS-TF-AMIFromWMI" + owner_id = (known after apply) + platform = (known after apply) + platform_details = (known after apply) + public = (known after apply) + ramdisk_id = (known after apply) + root_device_name = (known after apply) + root_snapshot_id = (known after apply) + source_instance_id = "i-024f77470f3f63c08" + sriov_net_support = (known after apply) + tags_all = (known after apply) + tpm_support = (known after apply) + usage_operation = (known after apply) + virtualization_type = (known after apply) + timeouts { + create = "45m" } } # citrix_aws_hypervisor.CreateHypervisorConnection will be created + resource "citrix_aws_hypervisor" "CreateHypervisorConnection" { + api_key = (sensitive value) + id = (known after apply) + name = "TACG-AWS-TF-HypConn" + region = (sensitive value) + secret_key = (sensitive value) + zone = "8d5d77ba-4803-4b71-9a6b-6e28071304c1" } # citrix_aws_hypervisor_resource_pool.CreateHypervisorPool will be created + resource "citrix_aws_hypervisor_resource_pool" "CreateHypervisorPool" { + availability_zone = "eu-central-1a" + hypervisor = (known after apply) + id = (known after apply) + name = "TACG-AWS-TF-HypConnPool" + subnets = [ + "172.31.16.0/20", ] + vpc = "TACG-VPC" } # citrix_machine_catalog.CreateMCSCatalog will be created + resource "citrix_machine_catalog" "CreateMCSCatalog" { + allocation_type = "Random" + description = "Terraform-based Machine Catalog" + id = (known after apply) + is_power_managed = true + is_remote_pc = false + name = "MC-TACG-AWS-TF" + provisioning_scheme = { + aws_machine_config = { + image_ami = (known after apply) + master_image = "TACG-AWS-TF-AMIFromWMI" + service_offering = "T2 Large Instance" } + hypervisor = (known after apply) + hypervisor_resource_pool = (known after apply) + identity_type = "ActiveDirectory" + machine_account_creation_rules = { + naming_scheme = "TACG-AWS-WM-#" + naming_scheme_type = "Numeric" } + machine_domain_identity = { + domain = "aws.the-austrian-citrix-guy.at" + domain_ou = "CN=Computers,DC=aws,DC=the-austrian-citrix-guy,DC=at" + service_account = (sensitive value) + service_account_password = (sensitive value) } + network_mapping = { + network = "172.31.16.0/20" + network_device = "0" } + number_of_total_machines = 1 } + provisioning_type = "MCS" + session_support = "MultiSession" + zone = "8d5dXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" } # citrix_delivery_group.CreateDG will be created + resource "citrix_delivery_group" "CreateDG" { + associated_machine_catalogs = [ + { + machine_catalog = "f4e3XXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" + machine_count = 1 }, ] + autoscale_settings = { + autoscale_enabled = true + disconnect_off_peak_idle_session_after_seconds = 0 + disconnect_peak_idle_session_after_seconds = 300 + log_off_off_peak_disconnected_session_after_seconds = 0 + log_off_peak_disconnected_session_after_seconds = 300 + off_peak_buffer_size_percent = 0 + off_peak_disconnect_action = "Nothing" + off_peak_disconnect_timeout_minutes = 0 + off_peak_extended_disconnect_action = "Nothing" + off_peak_extended_disconnect_timeout_minutes = 0 + off_peak_log_off_action = "Nothing" + peak_buffer_size_percent = 0 + peak_disconnect_action = "Nothing" + peak_disconnect_timeout_minutes = 0 + peak_extended_disconnect_action = "Nothing" + peak_extended_disconnect_timeout_minutes = 0 + peak_log_off_action = "Nothing" + power_off_delay_minutes = 30 + power_time_schemes = [ + { + days_of_week = [ + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", ] + display_name = "TACG-AWS-TF-AS-Weekdays" + peak_time_ranges = [ + "09:00-17:00", ] + pool_size_schedules = [ + { + pool_size = 1 + time_range = "09:00-17:00" }, ] + pool_using_percentage = false }, ] } + desktops = [ + { + description = "Terraform-based Delivery Group running on AWS EC2" + enable_session_roaming = true + enabled = true + published_name = "DG-TF-TACG-AWS" + restricted_access_users = { + allow_list = [ + "TACG-AWS\\vdaallowed", ] } }, ] + id = (known after apply) + name = "DG-TF-TACG-AWS" + reboot_schedules = [ + { + days_in_week = [ + "Sunday", ] + frequency = "Weekly" + frequency_factor = 1 + ignore_maintenance_mode = true + name = "TACG-AWS-Reboot Schedule" + natural_reboot_schedule = false + reboot_duration_minutes = 0 + reboot_schedule_enabled = true + start_date = "2024-01-01" + start_time = "02:00" }, ] + restricted_access_users = { + allow_list = [ + "TACG-AWS\\vdaallowed", ] } + total_machines = (known after apply) } # time_sleep.Wait_60_Seconds_2 will be created + resource "time_sleep" "Wait_60_Seconds_2" { + create_duration = "60s" + id = (known after apply) } # time_sleep.wait_60_seconds_1 will be created + resource "time_sleep" "wait_60_seconds_1" { + create_duration = "60s" + id = (known after apply) } Plan: 8 to add, 0 to change, 0 to destroy. Do you want to perform these actions? Terraform will perform the actions described above. Only 'yes' will be accepted to approve. Enter a value: yes aws_ami_from_instance.CreateAMIFromWMI: Creating... citrix_aws_hypervisor.CreateHypervisorConnection: Creating... aws_ami_from_instance.CreateAMIFromWMI: Still creating... [10s elapsed] citrix_aws_hypervisor.CreateHypervisorConnection: Still creating... [10s elapsed] citrix_aws_hypervisor.CreateHypervisorConnection: Creation complete after 10s [id=706c408a-6eed-42b1-8102-93888db8a0eb] citrix_aws_hypervisor_resource_pool.CreateHypervisorPool: Creating... aws_ami_from_instance.CreateAMIFromWMI: Still creating... [20s elapsed] citrix_aws_hypervisor_resource_pool.CreateHypervisorPool: Still creating... [10s elapsed] aws_ami_from_instance.CreateAMIFromWMI: Still creating... [30s elapsed] citrix_aws_hypervisor_resource_pool.CreateHypervisorPool: Still creating... [20s elapsed] citrix_aws_hypervisor_resource_pool.CreateHypervisorPool: Creation complete after 22s [id=b460dfc2-f760-4b52-bc94-fad9c85a0b8f] aws_ami_from_instance.CreateAMIFromWMI: Still creating... [40s elapsed] aws_ami_from_instance.CreateAMIFromWMI: Still creating... [50s elapsed] aws_ami_from_instance.CreateAMIFromWMI: Still creating... [1m0s elapsed] aws_ami_from_instance.CreateAMIFromWMI: Still creating... [1m10s elapsed] aws_ami_from_instance.CreateAMIFromWMI: Still creating... [1m20s elapsed] aws_ami_from_instance.CreateAMIFromWMI: Still creating... [1m30s elapsed] aws_ami_from_instance.CreateAMIFromWMI: Still creating... [1m40s elapsed] aws_ami_from_instance.CreateAMIFromWMI: Still creating... [1m50s elapsed] ... ** Output shortened **... aws_ami_from_instance.CreateAMIFromWMI: Still creating... [6m10s elapsed] aws_ami_from_instance.CreateAMIFromWMI: Still creating... [6m20s elapsed] aws_ami_from_instance.CreateAMIFromWMI: Still creating... [6m30s elapsed] aws_ami_from_instance.CreateAMIFromWMI: Still creating... [6m40s elapsed] aws_ami_from_instance.CreateAMIFromWMI: Still creating... [6m50s elapsed] aws_ami_from_instance.CreateAMIFromWMI: Still creating... [7m0s elapsed] aws_ami_from_instance.CreateAMIFromWMI: Creation complete after 7m7s [id=ami-0e01b8c8d09a5fbe5] time_sleep.wait_30_seconds: Creating... time_sleep.wait_30_seconds: Still creating... [10s elapsed] time_sleep.wait_30_seconds: Still creating... [20s elapsed] time_sleep.wait_30_seconds: Creation complete after 30s [id=2024-03-15T17:40:18Z] citrix_machine_catalog.CreateMCSCatalog: Creating... citrix_machine_catalog.CreateMCSCatalog: Still creating... [10s elapsed] citrix_machine_catalog.CreateMCSCatalog: Still creating... [20s elapsed] citrix_machine_catalog.CreateMCSCatalog: Still creating... [30s elapsed] citrix_machine_catalog.CreateMCSCatalog: Still creating... [40s elapsed] citrix_machine_catalog.CreateMCSCatalog: Still creating... [50s elapsed] ... ** Output shortened **... citrix_machine_catalog.CreateMCSCatalog: Still creating... [16m1s elapsed] citrix_machine_catalog.CreateMCSCatalog: Still creating... [16m11s elapsed] citrix_machine_catalog.CreateMCSCatalog: Still creating... [16m21s elapsed] citrix_machine_catalog.CreateMCSCatalog: Still creating... [16m31s elapsed] citrix_machine_catalog.CreateMCSCatalog: Still creating... [16m41s elapsed] citrix_machine_catalog.CreateMCSCatalog: Still creating... [16m51s elapsed] citrix_machine_catalog.CreateMCSCatalog: Still creating... [17m1s elapsed] citrix_machine_catalog.CreateMCSCatalog: Creation complete after 17m4s [id=f4e34a11-6e31-421f-8cb4-060bc4a13fef] time_sleep.wait_60_seconds_1: Creating... time_sleep.wait_60_seconds_1: Still creating... [10s elapsed] time_sleep.wait_60_seconds_1: Still creating... [20s elapsed] time_sleep.wait_60_seconds_1: Still creating... [30s elapsed] time_sleep.wait_60_seconds_1: Still creating... [40s elapsed] time_sleep.wait_60_seconds_1: Still creating... [50s elapsed] time_sleep.wait_60_seconds_1: Creation complete after 1m0s [id=2024-03-20T08:06:45Z] time_sleep.Wait_60_Seconds_2: Creating... time_sleep.Wait_60_Seconds_2: Still creating... [10s elapsed] time_sleep.Wait_60_Seconds_2: Still creating... [20s elapsed] time_sleep.Wait_60_Seconds_2: Still creating... [30s elapsed] time_sleep.Wait_60_Seconds_2: Still creating... [40s elapsed] time_sleep.Wait_60_Seconds_2: Still creating... [50s elapsed] time_sleep.Wait_60_Seconds_2: Creation complete after 1m0s [id=2024-03-20T08:07:45Z] citrix_delivery_group.CreateDG: Creating... citrix_delivery_group.CreateDG: Still creating... [10s elapsed] citrix_delivery_group.CreateDG: Creation complete after 12s [id=7e2c73bf-f8b1-4e37-8cd8-efa1338304dc] Apply complete! Resources: 8 added, 0 changed, 0 destroyed. PS C:\TACG\_CCOnAWS\_CCOnAWS-CCStuff> This configuration completes the full deployment of a Citrix Cloud Resource Location in Microsoft Amazon EC2. The environment created by Terraform is now ready for usage. All entities are in place: The Resource Location: The Hypervisor Connection and the Hypervisor Pool: The Machine Catalog: The Worker VM in the Machine Catalog: The Delivery Group: The AutoScale settings of the Delivery Group: The Desktop in the Library: The Desktop in the Library: Connection to the Worker VM´s Desktop: Appendix Examples of the Terraform scripts Module 1: CConAWS-Creation These are the Terraform configuration files for Module 1 (excerpts): _CCOnAWS-Creation-Provider.tf # Terraform deployment of Citrix DaaS on Amazon AWS EC2 ## Definition of all required Terraform providers terraform { required_version = ">= 1.4.0" required_providers { aws = { source = "hashicorp/aws" version = ">= 5.4.0" } restapi = { source = "Mastercard/restapi" version = "1.18.2" } citrix = { source = "citrix/citrix" version = ">=0.5.3" } } } # Configure the AWS Provider provider "aws" { region = "${var.AWSEC2_Region}" access_key = "${var.AWSEC2_AccessKey}" secret_key = "${var.AWSEC2_AccessKeySecret}" } # Configure the Citrix Provider provider "citrix" { customer_id = "${var.CC_CustomerID}" client_id = "${var.CC_APIKey-ClientID}" client_secret = "${var.CC_APIKey-ClientSecret}" } # Configure the REST-API provider provider "restapi" { alias = "restapi_rl" uri = "${var.CC_RestAPIURI}" create_method = "POST" write_returns_object = true debug = true headers = { "Content-Type" = "application/json", "Citrix-CustomerId" = "${var.CC_CustomerID}", "Accept" = "application/json", "Authorization" = "${var.CC_APIKey-Bearer}" } } _CCOnAWS-Creation-Create.tf # Terraform deployment of Citrix DaaS on Amazon AWS EC2 ## Creation of all required VMs - two Cloud Connectors and one Worker Master image locals { } ### Create needed IAM roles #### IAM EC2 Policy with Assume Role data "aws_iam_policy_document" "ec2_assume_role" { statement { actions = ["sts:AssumeRole"] principals { type = "Service" identifiers = ["ec2.amazonaws.com"] } } } #### Create EC2 IAM Role resource "aws_iam_role" "ec2_iam_role" { name = "ec2-iam-role" path = "/" assume_role_policy = data.aws_iam_policy_document.ec2_assume_role.json } #### Create EC2 IAM Instance Profile resource "aws_iam_instance_profile" "ec2_profile" { name = "ec2-profile" role = aws_iam_role.ec2_iam_role.name } #### Attach Policies to Instance Role resource "aws_iam_policy_attachment" "ec2_attach1" { name = "ec2-iam-attachment" roles = [aws_iam_role.ec2_iam_role.id] policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" } resource "aws_iam_policy_attachment" "ec2_attach2" { name = "ec2-iam-attachment" roles = [aws_iam_role.ec2_iam_role.id] policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM" } #### Create Secret Manager IAM Policy resource "aws_iam_policy" "secret_manager_ec2_policy" { name = "secret-manager-ec2-policy" description = "Secret Manager EC2 policy" policy = jsonencode({ Version = "2012-10-17" Statement = [ { Action = [ "secretsmanager:*" ] Effect = "Allow" Resource = "*" }, ] }) } #### Attach Secret Manager Policies to Instance Role resource "aws_iam_policy_attachment" "api_secret_manager_ec2_attach" { name = "secret-manager-ec2-attachment" roles = [aws_iam_role.ec2_iam_role.id] policy_arn = aws_iam_policy.secret_manager_ec2_policy.arn } data "template_file" "Add-EC2InstanceToDomainScriptCC1" { template = file("${path.module}/Add-EC2InstanceToDomainCC1.ps1") vars = { ad_secret_id = "AD/SA/DomainJoin" ad_domain = "aws.the-austrian.citrix.guy.at" } } data "template_file" "Add-EC2InstanceToDomainScriptCC2" { template = file("${path.module}/Add-EC2InstanceToDomainCC2.ps1") vars = { ad_secret_id = "AD/SA/DomainJoin" ad_domain = "aws.the-austrian.citrix.guy.at" } } data "template_file" "Add-EC2InstanceToDomainScriptWMI" { template = file("${path.module}/Add-EC2InstanceToDomainWMI.ps1") vars = { ad_secret_id = "AD/SA/DomainJoin" ad_domain = "aws.the-austrian.citrix.guy.at" } } data "template_file" "Add-EC2InstanceToDomainScriptAdminVM" { template = file("${path.module}/Add-EC2InstanceToDomainAdminVM.ps1") vars = { ad_secret_id = "AD/SA/DomainJoin" ad_domain = "aws.the-austrian.citrix.guy.at" } } #### Create DHCP settings resource "aws_vpc_dhcp_options" "vpc-dhcp-options" { depends_on = [ data.template_file.Add-EC2InstanceToDomainScriptCC1 ] domain_name_servers = [ var.AWSEC2_DC-IP ] } resource "aws_vpc_dhcp_options_association" "dns_resolver" { vpc_id = var.AWSEC2_VPC-ID dhcp_options_id = aws_vpc_dhcp_options.vpc-dhcp-options.id } ### Create CC1-VM resource "aws_instance" "CC1" { depends_on = [ data.template_file.Add-EC2InstanceToDomainScriptCC1 ] ami = var.AWSEC2_AMI subnet_id = var.AWSEC2_Subnet-ID private_ip = var.AWSEC2_PrivIP-CC1 instance_type = var.AWSEC2_Instance-Type key_name = var.AWSEC2_AMI-KeyPairName vpc_security_group_ids = [ var.AWSEC2_SecurityGroup ] tags = { Name = var.AWSEC2_InstanceName-CC1 } user_data = data.template_file.Add-EC2InstanceToDomainScriptCC1.rendered iam_instance_profile = aws_iam_instance_profile.ec2_profile.id } ### Create CC2-VM resource "aws_instance" "CC2" { depends_on = [ data.template_file.Add-EC2InstanceToDomainScriptCC2 ] ami = var.AWSEC2_AMI subnet_id = var.AWSEC2_Subnet-ID private_ip = var.AWSEC2_PrivIP-CC2 instance_type = var.AWSEC2_Instance-Type key_name = var.AWSEC2_AMI-KeyPairName vpc_security_group_ids = [ var.AWSEC2_SecurityGroup ] tags = { Name = var.AWSEC2_InstanceName-CC2 } user_data = data.template_file.Add-EC2InstanceToDomainScriptCC2.rendered iam_instance_profile = aws_iam_instance_profile.ec2_profile.id } ### Create Admin-VM resource "aws_instance" "AdminVM" { depends_on = [ data.template_file.Add-EC2InstanceToDomainScriptAdminVM ] ami = var.AWSEC2_AMI subnet_id = var.AWSEC2_Subnet-ID private_ip = var.AWSEC2_PrivIP-AdminVM instance_type = var.AWSEC2_Instance-Type-Worker key_name = var.AWSEC2_AMI-KeyPairName vpc_security_group_ids = [ var.AWSEC2_SecurityGroup ] tags = { Name = var.AWSEC2_InstanceName-AdminVM } user_data = data.template_file.Add-EC2InstanceToDomainScriptAdminVM.rendered iam_instance_profile = aws_iam_instance_profile.ec2_profile.id } ### Create WMI-VM resource "aws_instance" "WMI" { depends_on = [ data.template_file.Add-EC2InstanceToDomainScriptWMI ] ami = var.AWSEC2_AMI subnet_id = var.AWSEC2_Subnet-ID private_ip = var.AWSEC2_PrivIP-WMI instance_type = var.AWSEC2_Instance-Type key_name = var.AWSEC2_AMI-KeyPairName vpc_security_group_ids = [ var.AWSEC2_SecurityGroup ] tags = { Name = var.AWSEC2_InstanceName-WMI } user_data = data.template_file.Add-EC2InstanceToDomainScriptWMI.rendered iam_instance_profile = aws_iam_instance_profile.ec2_profile.id } _CCOnAWS-Creation-GetBearerToken.tf ### Create PowerShell file for retrieving the Bearer Token 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" 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 } Module 2: CConAWS-Install These are the Terraform configuration files for Module 2 (excerpts): _CCOnAWS-Install-CreatePreReqs.tf # Terraform deployment of Citrix DaaS on Amazon AWS EC2 ## Creating a dedicated Resource Location on Citrix Cloud resource "random_uuid" "IDforCCRL" { } ### 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" } ### Create PowerShell command to be run on the CC machines locals { randomuuid = random_uuid.IDforCCRL.result } ### Create a dedicated Resource Location in Citrix Cloud resource "restapi_object" "CreateRL" { depends_on = [ local_file.log ] provider = restapi.restapi_rl path="/resourcelocations" data = jsonencode( { "id" = "${local.randomuuid}", "name" = "${var.CC_RestRLName}", "internalOnly" = false, "timeZone" = "GMT Standard Time", "readOnly" = false } ) } ### Create PowerShell files with configuration and next steps and save it into Transfer directory #### 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" } #### Wait 5 mins after RL creation to settle Zone creation resource "time_sleep" "wait_300_seconds" { create_duration = "300s" } ### Create PowerShell file for determining the SiteID resource "local_file" "GetSiteIDScript" { depends_on = [restapi_object.CreateRL, time_sleep.wait_300_seconds] content = <<-EOT $requestUri = "https://api-eu.cloud.com/cvad/manage/me" $headers = @{ "Accept"="application/json"; "Authorization" = "${var.CC_APIKey-Bearer}"; "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 EOT filename = "${path.module}/GetSiteID.ps1" } ### Running the SiteID-Script to generate the SiteID resource "terraform_data" "SiteID" { depends_on = [ local_file.GetSiteIDScript ] provisioner "local-exec" { command = "GetSiteID.ps1" interpreter = ["PowerShell", "-File"] } } ### Retrieving the SiteID data "local_file" "input_site" { depends_on = [ terraform_data.SiteID ] filename = "${var.CC_Install_LogPath}/DATA/GetSiteID.txt" } ### Create PowerShell file for determining the ZoneID resource "local_file" "GetZoneIDScript" { depends_on = [ data.local_file.input_site ] content = <<-EOT $requestUri = "https://api-eu.cloud.com/cvad/manage/Zones" $headers = @{ "Accept"="application/json"; "Authorization" = "${var.CC_APIKey-Bearer}"; "Citrix-CustomerId" = "${var.CC_CustomerID}"; "Citrix-InstanceId" = "${data.local_file.input_site.content}" } $response = Invoke-RestMethod -Uri $requestUri -Method GET -Headers $headers | Convertto-Json $responsedejson = $response | ConvertFrom-Json $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 EOT filename = "${path.module}/GetZoneID.ps1" } ### Running the ZoneID-Script to generate the ZoneID resource "terraform_data" "ZoneID" { depends_on = [ local_file.GetZoneIDScript ] provisioner "local-exec" { command = "GetZoneID.ps1" interpreter = ["PowerShell", "-File"] } } #### Create PowerShell file for installing the Citrix Cloud Connector - we need to determine the correct RL-ID resource "local_file" "InstallPreReqsOnCC-ps1" { 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 Add-Content ${var.CC_Install_LogPath}/log.txt "`nInstalling Cloud Connector." Start-Process -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 -Timeout 1800 } EOT filename = "${path.module}/DATA/InstallPreReqsOnCC.ps1" } #### Create PowerShell file for installing the Citrix Remote PoSH SDK on AVM - we need to determine the correct RL-ID resource "local_file" "InstallPreReqsOnAVM1-ps1" { 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 = "${path.module}/DATA/InstallPreReqsOnAVM1.ps1" } #### Create PowerShell file for installing the Citrix Remote PoSH SDK on AVM - we need to determine the correct RL-ID resource "local_file" "InstallPreReqsOnAVM2-ps1" { 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}" # Correct the Resource Location ID in cwc.json file $requestUri = "https://api-eu.cloud.com/resourcelocations" $headers = @{ "Accept"="application/json"; "Authorization" = "${var.CC_APIKey-Bearer}"; "Citrix-CustomerId" = "${var.CC_CustomerID}"} $response = Invoke-RestMethod -Uri $requestUri -Method GET -Headers $headers | Convertto-Json $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 Add-Content ${var.CC_Install_LogPath}/DATA/GetRLID.txt $RLID Add-Content ${var.CC_Install_LogPath}/log.txt "`ncwc.json corrected." Add-Content ${var.CC_Install_LogPath}/log.txt "`nScript completed." } EOT filename = "${path.module}/DATA/InstallPreReqsOnAVM2.ps1" } ### Upload required components to AVM #### Set the Provisioner-Connection resource "null_resource" "UploadRequiredComponentsToAVM" { depends_on = [ local_file.InstallPreReqsOnAVM1-ps1, local_file.GetSiteIDScript ] connection { type = var.Provisioner_Type user = var.Provisioner_Admin-Username password = var.Provisioner_Admin-Password host = var.Provisioner_AVM-IP timeout = var.Provisioner_Timeout } ###### Upload PreReqs script to AVM provisioner "file" { source = "${path.module}/DATA/InstallPreReqsOnAVM1.ps1" destination = "${var.CC_Install_LogPath}/DATA/InstallPreReqsOnAVM1.ps1" } provisioner "file" { source = "${path.module}/DATA/InstallPreReqsOnAVM2.ps1" destination = "${var.CC_Install_LogPath}/DATA/InstallPreReqsOnAVM2.ps1" } } ### Call the required scripts on AVM #### Set the Provisioner-Connection resource "null_resource" "CallRequiredScriptsOnAVM1" { depends_on = [ null_resource.UploadRequiredComponentsToAVM ] connection { type = var.Provisioner_Type user = var.Provisioner_Admin-Username password = var.Provisioner_Admin-Password host = var.Provisioner_AVM-IP timeout = var.Provisioner_Timeout } ###### Execute the PreReqs script on AVM provisioner "remote-exec" { inline = [ "powershell -File ${var.CC_Install_LogPath}/DATA/InstallPreReqsOnAVM1.ps1" ] } } resource "null_resource" "CallRequiredScriptsOnAVM2" { depends_on = [ null_resource.CallRequiredScriptsOnAVM1 ] connection { type = var.Provisioner_Type user = var.Provisioner_Admin-Username password = var.Provisioner_Admin-Password host = var.Provisioner_AVM-IP timeout = var.Provisioner_Timeout } ###### Execute the PreReqs script on AVM provisioner "remote-exec" { inline = [ "powershell -File ${var.CC_Install_LogPath}/DATA/InstallPreReqsOnAVM2.ps1" ] } } ############################################################################################################################## ### Upload required components to CC1 #### Set the Provisioner-Connection resource "null_resource" "UploadRequiredComponentsToCC1" { depends_on = [ local_file.InstallPreReqsOnCC-ps1, null_resource.CallRequiredScriptsOnAVM2] connection { type = var.Provisioner_Type user = var.Provisioner_Admin-Username password = var.Provisioner_Admin-Password host = var.Provisioner_DDC1-IP 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 = "${path.module}/DATA/InstallPreReqsOnCC.ps1" destination = "${var.CC_Install_LogPath}/DATA/InstallPreReqsOnCC.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 = var.Provisioner_AVM-IP timeout = var.Provisioner_Timeout } provisioner "remote-exec" { inline = [ "powershell -File ${var.CC_Install_LogPath}/DATA/InstallPreReqsOnCC.ps1" ] } } ### Upload required components to CC2 #### Set the Provisioner-Connection resource "null_resource" "UploadRequiredComponentsToCC2" { depends_on = [ local_file.InstallPreReqsOnCC-ps1,null_resource.CallRequiredScriptsOnAVM2 ] connection { type = var.Provisioner_Type user = var.Provisioner_Admin-Username password = var.Provisioner_Admin-Password host = var.Provisioner_DDC2-IP 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 = "${path.module}/DATA/InstallPreReqsOnCC.ps1" destination = "${var.CC_Install_LogPath}/DATA/InstallPreReqsOnCC.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 = var.Provisioner_AVM-IP timeout = var.Provisioner_Timeout } provisioner "remote-exec" { inline = [ "powershell -File ${var.CC_Install_LogPath}/DATA/InstallPreReqsOnCC.ps1" ] } } Module 3: CConAWS-CCStuff These are the Terraform configuration files for Module 3 (excerpts): _CCOnAWS-CCStuff-CreateCCEntities.tf # Terraform deployment of Citrix DaaS on Amazon AWS EC2 ## 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" } #### Creating the Hypervisor Connection resource "citrix_aws_hypervisor" "CreateHypervisorConnection" { depends_on = [ data.local_file.LoadZoneID ] name = "${var.CC_AWSEC2-HypConn-Name}" zone = data.local_file.LoadZoneID.content api_key = "${var.AWSEC2_AccessKey}" secret_key = "${var.AWSEC2_AccessKeySecret}" region = "${var.AWSEC2_Region}" } ### Creating a Hypervisor Resource Pool #### Retrieving the VPC name based on the VPC ID data "aws_vpc" "AWSVPC" { id = "${var.AWSEC2_VPC-ID}" } #### Retrieving the Availability Zone data "aws_vpc" "AWSAZ" { id = "${var.AWSEC2_VPC-ID}" } #### Retrieving the Subnet Mask based on the Subnet ID data "aws_subnet" "AWSSubnet" { id = "${var.AWSEC2_Subnet-ID}" } #### Create the Hypervisor Resource Pool resource "citrix_aws_hypervisor_resource_pool" "CreateHypervisorPool" { depends_on = [ citrix_aws_hypervisor.CreateHypervisorConnection ] name = "${var.CC_AWSEC2-HypConnPool-Name}" hypervisor = citrix_aws_hypervisor.CreateHypervisorConnection.id subnets = [ "${data.aws_subnet.AWSSubnet.cidr_block}", ] vpc = "${var.AWSEC2_VPC-Name}" availability_zone = data.aws_subnet.AWSSubnet.availability_zone } #### Create AMI from WMI instance resource "aws_ami_from_instance" "CreateAMIFromWMI" { name = "TACG-AWS-TF-AMIFromWMI" source_instance_id = "${var.AWSEC2_AMI-ID}" timeouts { create = "45m" } } #### Sleep 60s to let AWS Background processes settle resource "time_sleep" "wait_60_seconds" { depends_on = [ citrix_aws_hypervisor_resource_pool.CreateHypervisorPool, aws_ami_from_instance.CreateAMIFromWMI ] create_duration = "60s" } #### Create the Machine Catalog resource "citrix_machine_catalog" "CreateMCSCatalog" { depends_on = [ time_sleep.wait_60_seconds ] name = "${var.CC_AWSEC2-MC-Name}" description = "${var.CC_AWSEC2-MC-Description}" allocation_type = "${var.CC_AWSEC2-MC-AllocationType}" session_support = "${var.CC_AWSEC2-MC-SessionType}" is_power_managed = true is_remote_pc = false provisioning_type = "MCS" zone = data.local_file.LoadZoneID.content provisioning_scheme = { hypervisor = citrix_aws_hypervisor.CreateHypervisorConnection.id hypervisor_resource_pool = citrix_aws_hypervisor_resource_pool.CreateHypervisorPool.id identity_type = "${var.CC_AWSEC2-MC-IDPType}" machine_domain_identity = { domain = "${var.CC_AWSEC2-MC-Domain}" #domain_ou = "${var.CC_AWSEC2-MC-DomainOU}" service_account = "${var.Provisioner_DomainAdmin-Username-UPN}" service_account_password = "${var.Provisioner_DomainAdmin-Password}" } aws_machine_config = { image_ami = aws_ami_from_instance.CreateAMIFromWMI.id master_image = aws_ami_from_instance.CreateAMIFromWMI.name service_offering = "${var.CC_AWSEC2-MC-Service_Offering}" } number_of_total_machines = "${var.CC_AWSEC2-MC-Machine_Count}" network_mapping = { network_device = "0" network = "${data.aws_subnet.AWSSubnet.cidr_block}" } machine_account_creation_rules = { naming_scheme = "${var.CC_AWSEC2-MC-Naming_Scheme_Name}" naming_scheme_type = "${var.CC_AWSEC2-MC-Naming_Scheme_Type}" } } } #### Sleep 60s to let CC 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_AWSEC2-Policy-IsNotDaaS ? 1 : 0 depends_on = [ time_sleep.wait_60_seconds_1 ] name = "${var.CC_AWSEC2-Policy-Name}" description = "${var.CC_AWSEC2-Policy-Description}" type = "DeliveryGroupPolicies" scopes = [ "All" ] policies = [ { name = "TACG-AWS-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-AWS-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 CC 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_2] name = "${var.CC_AWSEC2-DG-Name}" associated_machine_catalogs = [ { #machine_catalog = citrix_machine_catalog.CreateMCSCatalog.id machine_catalog ="f4e34a11-6e31-421f-8cb4-060bc4a13fef" machine_count = "${var.CC_AWSEC2-MC-Machine_Count}" } ] desktops = [ { published_name = "${var.CC_AWSEC2-DG-PublishedDesktopName}" description = "${var.CC_AWSEC2-DG-Description}" restricted_access_users = { allow_list = [ "TACG-AWS\\vdaallowed" ] } enabled = true enable_session_roaming = var.CC_AWSEC2-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_AWSEC2-DG-AS-Name}" display_name = "${var.CC_AWSEC2-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-AWS\\vdaallowed" ] } reboot_schedules = [ { name = "TACG-AWS-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 } .table_component { overflow: auto; width: 100%; } .table_component table { border: 1px solid #0000ff; border-radius: 3px; height: 100%; width: 90%; table-layout: fixed; border-collapse: collapse; border-spacing: 1px; text-align: left; } .table_component caption { caption-side: top; text-align: left; } .table_component th { border: 1px solid #0000ff; background-color: #0000c8; color: #ffffff; padding: 10px; } .table_component td { border: 1px solid #0000ff; background-color: #0000ff; color: #ffffff; padding: 10px; font-family: Consolas,Courier New; font-size:12px; } .table_component { overflow: auto; width: 100%; } .table_component table { border: 1px solid #323232; border-radius: 3px; height: 100%; width: 90%; table-layout: fixed; border-collapse: collapse; border-spacing: 1px; text-align: left; } .table_component caption { caption-side: top; text-align: left; } .table_component th { border: 1px solid #323232; background-color: #323232; color: #ffffff; padding: 10px; } .table_component td { border: 1px solid #323232; background-color: #323232; color: #ffffff; padding: 10px; font-family: Consolas,Courier New; font-size:12px; } .table_component { overflow: auto; width: 100%; } .table_component table { border: 1px solid #323232; border-radius: 3px; height: 100%; width: 90%; table-layout: fixed; border-collapse: collapse; border-spacing: 1px; text-align: left; } .table_component caption { caption-side: top; text-align: left; } .table_component th { border: 1px solid #323232; background-color: #323232; color: #ffffff; padding: 10px; } .table_component td { border: 1px solid #323232; background-color: #323232; color: #ffffff; padding: 10px; font-family: Consolas,Courier New; font-size:12px; } .table_component { overflow: auto; width: 100%; } .table_component table { border: 1px solid #323232; border-radius: 3px; height: 100%; width: 90%; table-layout: fixed; border-collapse: collapse; border-spacing: 1px; text-align: left; } .table_component caption { caption-side: top; text-align: left; } .table_component th { border: 1px solid #323232; background-color: #323232; color: #ffffff; padding: 10px; } .table_component td { border: 1px solid #323232; background-color: #323232; color: #ffffff; padding: 10px; font-family: Consolas,Courier New; font-size:12px; } .table_component { overflow: auto; width: 100%; } .table_component table { border: 1px solid #323232; border-radius: 3px; height: 100%; width: 90%; table-layout: fixed; border-collapse: collapse; border-spacing: 1px; text-align: left; } .table_component caption { caption-side: top; text-align: left; } .table_component th { border: 1px solid #323232; background-color: #323232; color: #ffffff; padding: 10px; } .table_component td { border: 1px solid #323232; background-color: #323232; color: #ffffff; padding: 10px; font-family: Consolas,Courier New; font-size:12px; } .tg {border-collapse:collapse;border-spacing:0;} .tg td{border-color:black;border-style:solid;border-width:1px; overflow:hidden;padding:10px 5px;word-break:normal;} .tg .tg-u3wl{background-color:#1f1f1f;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px; text-align:left;vertical-align:top}
×
×
  • Create New...