<?xml version="1.0"?>
<rss version="2.0"><channel><title>Citrix Tech Zone Document History</title><link>https://community.stage.citrix.com/rss/1-citrix-tech-zone-document-history.xml/</link><description>The following describes important changed to the Citrix Tech Zone documentaiotn. To receive notification about these updates, subscribe to the RSS feed.</description><language>en</language><item><title>The Citrix Automation Handbook 2601 - Part 6</title><link>https://community.stage.citrix.com/tech-zone/automation/automation-handbook-2601-part6/</link><description>The Citrix Automation Handbook - Part 6In the first part of the Citrix Automation Handbook, we discuss the Citrix Automation story and the basics of Infrastructure-as-Code, of Continuous Integration/Continuous Deployment (CI/CD) strategies, and our main IaC tools: Packer, Terraform, and Ansible. In the second part of the Citrix Automation Handbook, we focus on: Applying automation and infrastructure-as-code in the Citrix universe. We discuss all relevant components and provide code examples from real environments Best practises for designing an environment for automation and IaC use, and discuss deployment tips In the third part of the Citrix Automation Handbook, we discuss common and special use cases, as well as complete deployment examples from the field. In the fourth part of the Citrix Automation Handbook, we cover creating a Citrix Virtual Apps and Desktops 2507 LTSR deployment on XenServer 8.4 In the fifth part of the Citrix Automation Handbook, we cover creating a Citrix DaaS deployment on Azure In this part, we cover some final topics.  Part 6 Let&#xB4;s look at some final thoughts about using Automation and Infrastructure-as-Code in the Citrix Universe ConclusionDeploying Citrix infrastructure without Infrastructure-as-Code and automation is like navigating a storm without a compass. By embracing IaC, your organization gains predictability, speed, and governance&#x2014;turning your infrastructure from a liability into a strategic asset. It&#x2019;s not just about technology; it&#x2019;s about enabling the business to deliver secure, high-performance apps and desktops at scale, with confidence. Instead of manual clicks and scripts scattered across teams, every component in your Citrix estate is defined, validated, and governed through code. This code is version-controlled, peer-reviewed, and automatically tested before deployment. With a single command, the entire Citrix stack spins up consistently&#x2014;whether in the cloud, on-premises, or hybrid. But the evolution doesn&#x2019;t stop at automation. For the last decade, automation was the bridge&#x2014;the gap separating modern operations from legacy systems. Today, policy-driven infrastructure and AI-assisted intelligence are extending that bridge even further. Once your Citrix environment is expressed as code and governed by declarative policy, you unlock the next stage of operational maturity: infrastructure defined by intent, not manual effort Automated policy enforcement for security, cost, and performance baselines AI-driven recommendations, remediation, and continuous optimization Self-healing, self-scaling, and context-aware decision making across the Citrix control plane In other words: automation gets you consistency; policy + AI gets you intelligence. An organization that never crossed the automation bridge is already behind. But those who embrace IaC, policy-as-code, and AI-driven operations position themselves for compound acceleration, enabling their Citrix infrastructure to operate with predictability, resilience, and adaptability that legacy environments cannot match. This handbook is intended to: Provide an overview of the possibilities Citrix currently offers in Infrastructure-as-Code and Automation Show examples from real-world projects Provide insights and samples touching the three main IaC frameworks Explain CI/CD strategies for deploying and updating Citrix infrastructure Illustrate how policy-as-code and AI-driven intelligence become the natural next evolution once IaC is in place  Important The Citrix Automation Handbook is a living document. We will add, edit, remove, and alter parts if necessary.  AcknowledgementsThank you to all my colleagues who helped me create this handbook. They provided invaluable insights, experience, knowledge, and ideas: Avijit Gahtori Zhuolun Lin Sourav Samanta Sameer Sharma  Special thanks for providing invaluable information and granting me access to their repositories to: Alan Goldman Konstantinos Kaltsas Rody Kossen Jason Samuel  LinksAutomation Landing Zone Citrix GitHub repository for Terraform Citrix GitHub repository for Ansible Citrix GitHub repository for Packer Citrix NetScaler GitHub repository  That completes Part 6 of the Citrix Automation Handbook. In the first part of the Citrix Automation Handbook, we discuss the Citrix Automation story and the basics of Infrastructure-as-Code, of Continuous Integration/Continuous Deployment (CI/CD) strategies, and our main IaC tools: Packer, Terraform, and Ansible. In the second part of the Citrix Automation Handbook, we focus on: Applying automation and infrastructure-as-code in the Citrix universe. We discuss all relevant components and provide code examples from real environments Best practises for designing an environment for automation and IaC use, and discuss deployment tips In the third part of the Citrix Automation Handbook, we discuss common and special use cases, as well as complete deployment examples from the field. In the fourth part of the Citrix Automation Handbook, we cover creating a Citrix Virtual Apps and Desktops 2507 LTSR deployment on XenServer 8.4 In the fifth part of the Citrix Automation Handbook, we cover creating a Citrix DaaS deployment on Azure In this part, we covered some final topics.  DisclaimerMost important All code snippets mentioned in this handbook were thoroughly tested and run in a sandbox environment. All shown Packer, Terraform, Ansible Playbooks, and PowerShell scripts were tailored exclusively for the environment used for this handbook. You need to adjust all settings, values, snippets, etc., in the most suitable way tailored to your requirements, regulations, and existing environments. All settings, scripts, and code examples listed in this document are intended only to illustrate the possibilities and serve as a basis for your own deployment. We tried to show different ways, even if they were not always in line with best-practice guidelines, such as unencrypted WinRM communication for demonstration purposes. Using all the provided code snippets is at your own risk &#x2013; please read the disclaimer below for further information.   EXCEPT WHERE EXPRESSLY PROVIDED OTHERWISE BY CLOUD SOFTWARE GROUP, THE CONTENT IN THIS DOCUMENT IS PROVIDED &#x201C;AS IS&#x201D; AND CLOUD SOFTWARE GROUP HEREBY DISCLAIMS ALL EXPRESS OR IMPLIED REPRESENTATIONS, WARRANTIES, GUARANTIES, AND CONDITIONS, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES OR CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD TO BE LEGALLY INVALID. CLOUD SOFTWARE GROUP MAKES NO REPRESENTATIONS, WARRANTIES, GUARANTIES, OR CONDITIONS AS TO THE QUALITY, SUITABILITY, TRUTH, ACCURACY, OR COMPLETENESS OF ANY OF THE CONTENT CONTAINED ON THE WEBSITE. The above disclaimer applies to any damages, liability, or injuries caused by any failure of performance, error, omission, interruption, deletion, defect, delay in operation or transmission, computer virus, communication line failure, theft or destruction of or unauthorized access to, alteration of, or use, whether for breach of contract, tort, negligence or any other cause of action.</description><pubDate>Wed, 21 Jan 2026 09:14:10 +0000</pubDate></item><item><title>The Citrix Automation Handbook 2601 - Part 5</title><link>https://community.stage.citrix.com/tech-zone/automation/automation-handbook-2601-part5/</link><description><![CDATA[The Citrix Automation Handbook - Part 5In the first part of the Citrix Automation Handbook, we discuss the Citrix Automation story and the basics of Infrastructure-as-Code, of Continuous Integration/Continuous Deployment (CI/CD) strategies, and our main IaC tools: Packer, Terraform, and Ansible. In the second part of the Citrix Automation Handbook, we focus on: Applying automation and infrastructure-as-code in the Citrix universe. We discuss all relevant components and provide code examples from real environments Best practises for designing an environment for automation and IaC use, and discuss deployment tips In the third part of the Citrix Automation Handbook, we discuss common and special use cases, as well as complete deployment examples from the field. In the fourth part of the Citrix Automation Handbook, we cover creating a Citrix Virtual Apps and Desktops 2507 LTSR deployment on XenServer 8.4 In this part, we focus on: Creating a Citrix DaaS deployment on Azure. In the sixth part of the Citrix Automation Handbook, we cover some final topics.  Part 5 Creating a Citrix DaaS Deployment on AzureIn this example, we cover the usage of three distinct Infrastructure-as-Code (IaC) frameworks to deploy a Citrix DaaS-based deployment on Azure. Components and Frameworks usedLet´s have a short overview of all the components and frameworks used. Hashicorp TerraformTerraform is one of the most widely adopted Infrastructure-as-Code (IaC) platforms for provisioning and managing cloud and on-prem infrastructure at scale. Its declarative configuration model, strong ecosystem support, and provider-agnostic architecture make it a foundational tool for organizations seeking consistency, compliance, and operational maturity in their infrastructure lifecycle. Terraform relies on declarative code to define infrastructure in a predictable, reproducible manner. By representing compute, networking, storage, IAM, and platform services as version-controlled code artifacts, Terraform: Hashicorp PackerHashiCorp Packer is a foundational tool for organizations seeking to standardize, automate, and scale the creation of master images across multi-cloud and hybrid environments. As organizations increasingly adopt Infrastructure-as-Code (IaC) approaches, Packer provides a consistent, version-controlled, and repeatable way to build golden images aligned with modern DevOps and platform engineering practices. RedHat AnsibleMany organizations that have matured their Infrastructure-as-Code (IaC) practices increasingly require tooling that not only provisions infrastructure but also configures systems, applications, and operating environments in a controlled, repeatable manner. Ansible is a leading configuration management and automation platform that aligns naturally with IaC principles and complements provisioning tools such as Terraform, or cloud-native IaC engines. Its agentless architecture, declarative playbook model, and extensive ecosystem make it a strategic choice for enterprises seeking predictable, scalable, and compliant post-provision configuration. Ansible provides a declarative, idempotent configuration model that ensures systems converge to a desired state regardless of their current configuration. This makes it ideally suited for environments provisioned using IaC tools. Note You can find more information about the three frameworks and their importance in the first part of the Citrix Automation Handbook.  Better togetherTerraform and Ansible serve complementary roles in modern Infrastructure-as-Code (IaC) architectures. Terraform excels at provisioning infrastructure resources—compute, networks, storage, identity, and cloud services—while Ansible specializes in configuring and managing the software, applications, and operating system state on those resources once they are created. When combined, they form a robust, scalable, and fully automated delivery pipeline that allows organizations to provision secure, consistent environments and configure them in a modular, policy-driven manner. Terraform is primarily designed for infrastructure lifecycle management, while Ansible is engineered for post-provisioning configuration. This clean separation ensures: Terraform builds and destroys infrastructure predictably Ansible configures servers, middleware, application stacks, and OS settings Changes to infrastructure and configuration are maintained in distinct codebases, improving clarity and reducing risk IT can version, audit, and test both layers independently This division of responsibilities aligns with architectural patterns used in platform engineering and regulated environments, where infrastructure and system configuration often have separate compliance requirements. All needed Automation components were installed on an Ubuntu-based VM cluster.  If all IaC frameworks reside on the same machine/cluster, communication between them is less error-prone.  Communication Flow between Packer, Terraform, Ansible, and the Target MachinesAll communication between Terraform, Packer, and Ansible on the IaC-VM and the target VMs on the XenServer cluster uses SSH and WinRM. WinRM (Windows Remote Management) is a Microsoft protocol that enables remote management of Windows systems by allowing access to remote computers to perform management tasks. It is Microsoft's implementation of the WS-Management (Web Services Management Protocol) standard. It often uses SOAP (Simple Object Access Protocol) for communication over HTTP or HTTPS, using ports 5985 (HTTP) and 5986 (HTTPS) by default. WinRM provides the basis for remote management with PowerShell and is used for tasks such as running remote scripts, automating, configuring, and performing system inventory. Therefore, WinRM and SSH must be configured correctly. Important We emphasize ensuring the communication flow works as intended from the outset of the pre-domain-joined stage; otherwise, Terraform and Ansible will fail to configure the VMs. During the pre-domain-join stage, you can configure these settings using the local Group Policy Editor on the VM or in the autounattend.xml file as we do in our Packer scripts. After a successful domain join, you can set all relevant settings using GPOs. To provide maximum flexibility and a fallback mechanism, you can use both listeners - HTTP and HTTPS - but we recommend using the secure transport whenever and wherever possible. Note You can find more information about the communication flow, its configuration, and its importance in the first part of the Citrix Automation Handbook.  Creation of the Citrix DaaS Environment using IaCAs already stated, the workflow is divided into blocks that must be started in a specific, predetermined order: Create all prerequisites on Azure  Using Packer to create the Master Images  Creating the needed Cloud Connector VMs  Putting the 2 Cloud Connector VMs Into the Active Directory Domain  Installing and Configuring the Cloud Connector Software  Creating the Hypervisor Connection and Hypervisor Resource Pool  Creating a Machine Catalog on Azure  Creating a Delivery Group  Creating Policy Sets, Scopes, and Roles  Removing Citrix DaaS entities using Terraform  Note All Ansible playbooks are directly called from Terraform. Terraform stops the workflow if an error occurs by checking Ansible's return code.  Important All shown Terraform codes, Ansible Playbooks, and PowerShell scripts are tailored exclusively for the specific environment used for this guide. You need to adjust all settings, values, snippets, etc., in the most suitable way tailored to your requirements, regulations, and existing environments. All settings, scripts, and code examples listed in this document are intended only to illustrate the possibilities and serve as a basis for your own deployment. Using all the provided code snippets is at your own risk – please read the disclaimer at the end of the document for further information.  Tip This IaC-based deployment is built on single, stand-alone modules. This is because our GitHub workflows enable the reuse of each module for Day 2-like administrative steps. Each module might need variables from earlier modules to function properly - for example, the Resource Group name, the Citrix Zone ID, etc., are values that are needed in most modules. You need to ensure the values are available across all modules.  Part 1: Create all needed prerequisitesDetermining available base images on AzureThe first step is to determine which base image you want to use – Azure offers many options. As we create master images for MCS, we focus on Windows images. Packer needs to know which base image it should use: # Source Image
  os_type         = "Windows"
  image_publisher = var.azure_imgpublisher
  image_offer     = var.azure_imgoffer
  image_sku       = var.azure_imgsku
  image_version   = var.azure_imgversion
 Let´s find out which images we can use. The easiest way is to use PowerShell: Get the Image Publisher: PS C:\TMM&gt; $LocName="austriaeast"
PS C:\TMM&gt; $Publisher=Get-AzVMImagePublisher -Location $LocName | Where-Object { $_.PublisherName -eq "MicrosoftWindowsDesktop" }
PS C:\TMM&gt; $Publisher

PublisherName           Location    Id
-------------           --------    --
MicrosoftWindowsDesktop austriaeast /Subscriptions/&lt;sensitive&gt;/Providers/Microsoft.Compute/Locations/austriaeast/Publishers/MicrosoftWindowsDesktop

PS C:\TMM&gt;Get the Image Offer: PS C:\TMM&gt; $Offer = Get-AzVMImageOffer -Location $LocName -PublisherName $Publisher.PublisherName | Where-Object { $_.Offer -eq "windows-11" }
PS C:\TMM&gt; $Offer

Offer      PublisherName           Location   Id
-----      -------------           --------   --
windows-11 MicrosoftWindowsDesktop austriaeast /Subscriptions/&lt;sensitive&gt;/Providers/Microsoft.Compute/Locations/austriaeast/Publishers/MicrosoftWindowsDesktop/ArtifactTypes/VMImage…

PS C:\TMM&gt;Get the Image SKU – here all SKUs are shown for informational purposes: PS C:\TMM&gt; $Skus = Get-AZVMImageSku -Location $LocName -PublisherName $Publisher.PublisherName -Offer $Offer.Offer | Select Skus
PS C:\TMM&gt; $SKus

Skus
----
win11-21h2-avd
win11-21h2-ent
win11-21h2-pro-zh-cn
win11-22h2-avd
win11-22h2-ent
win11-22h2-entn
win11-23h2-avd
win11-23h2-ent
win11-23h2-entn
win11-23h2-pro
win11-23h2-pro-zh-cn
win11-23h2-pron
win11-24h2-avd
win11-24h2-ent
win11-24h2-entn
win11-24h2-pro
win11-24h2-pro-zh-cn
win11-24h2-pron
win11-25h2-avd
win11-25h2-ent
win11-25h2-entn
win11-25h2-pro
win11-25h2-pro-zh-cn
win11-25h2-pron

PS C:\TMM&gt;Get the Image: PS C:\TMM&gt; Get-AzVMImage -Location $LocName -PublisherName $Publisher.PublisherName -Offer $Offer.Offer -Sku "win11-25h2-pro" | Select Version

Version
-------
26200.6584.250915
26200.6899.251011
26200.7171.251109
26200.7462.251207

PS C:\TMM&gt;We now have all the parameters we need to tell Packer which base image should be used: ...
azure_imgpublisher   = "MicrosoftWindowsDesktop"
azure_imgoffer       = "windows-11"
azure_imgsku         = "win11-25h2-pro"
azure_imgversion     = "26200.7462.251207"
...
 Creating a Service Principal Name (SPN) with a Client Secret for Azure AuthenticationA Service Principal is an application within Azure Active Directory whose authentication tokens can be used as the client_id, client_secret, and tenant_id fields needed by the Azure Terraform provider. The subscription_id field can be found in your Azure Account details. Terraform uses the SPN to connect to Azure and create everything needed. If you do not have an SPN configured, you can use e.g. Azure CLI on PowerShell to create one: PS C:\TMM\TestWinRM&gt; az login
Select the account you want to log in with. For more information on login with Azure CLI, see https://go.microsoft.com/fwlink/?linkid=2271136

Retrieving tenants and subscriptions for the selection...

[Tenant and subscription selection]

No     Subscription name    Subscription ID                       Tenant
-----  -------------------  ------------------------------------  ----------------
[1] *  WWCO-XxXxXxX         dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13  Citrix (TM-WWCO)

The default is marked with an *; the default tenant is 'Citrix (TM-WWCO)' and subscription is 'WWCO-XxXxXxX' (dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13).

Select a subscription and tenant (Type a number or Enter for no changes): 1

Tenant: Citrix (TM-WWCO)
Subscription: WWCO-XxXxXxX (dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13)

PS C:\TMM&gt; az ad sp create-for-rbac
The output includes credentials that you must protect. Be sure that you do not include these credentials in your code or check the credentials into your source control. For more information, see https://aka.ms/azadsp-cli
{
  "appId": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
  "displayName": "azure-cli-2025-12-03-18-12-57",
  "password": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
  "tenant": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
}
PS C:\TMM&gt;Please record the corresponding values for future use.  (Optional) Create a dedicated Resource Group in your Azure SubscriptionYou can decide if you want Terraform to create a new Resource Group on Azure to deploy all further components by setting the corresponding variable: ...
"CreateRG": true,
"RG-Name": "TMM-TEAM-RG-AEAST"
...The new Resource Group will be created if you set this variable to true. # Create a Citrix Cloud Resource Location and DaaS Deployment on Microsoft Azure
## Create Pre-requisites
### Create a Resource Group on Azure
#### https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/resource_group
resource "azurerm_resource_group" "TACG-AZ-RG" {
  count    = var.CreateRG ? 1 : 0
  location = var.RG-DeployLocation
  name     = var.RG-Name
  tags     = {
               Environment        = var.TAG-Environment,
               Environment-Entity = var.TAG-Environment-Entity,
               Environment-Usage  = var.TAG-Environment-Usage,
  }
} Determining existing Azure Entities - Virtual Network, Subnet, and Network Security GroupWe assume that these entities are already configured on Azure. Terraform relies on them and retrieves Important configuration details during the deployment.  You can use PowerShell to retrieve all the needed entity values. Write down the required entity information for later use in the corresponding .auto.tfvars.json files so the Terraform provider can use this Information. Get the Virtual Network to use: PS C:\_TMM&gt; Get-AzVirtualNetwork -ResourceGroupName 'TMM-TEAM-RG-AEAST' | FT Name, Location

Name          Location
----          --------
AEAST-vnet austriaeast

PS C:\_TMM&gt;Get the Subnet(s) to use: 
PS C:\_TMM&gt; $vn = Get-AzVirtualNetwork -Name 'AEAST-vnet'
PS C:\_TMM&gt; Get-AzVirtualNetworkSubnetConfig -VirtualNetwork $vn | FT Name, AddressPrefix

Name          AddressPrefix
----          -------------
AEAST-1-sn {10.53.16.0/24}
AEAST-2-sn {10.53.17.0/25}
GatewaySubnet {10.53.17.128/26}

PS C:\_TMM&gt;Get the Network Security Group to use: 
PS C:\_TMM&gt; Get-AzNetworkSecurityGroup -ResourceGroupName 'TMM-TEAM-RG-AEAST' | FT Name, Location

Name                Location
----                --------
AZ-AEAST-GK-nsg    austriaeast
AZ-AEAST-test-nsg  austriaeast

PS C:\_TMM&gt; You can, of course, use a Terraform code snippet to determine these entities directly in Terraform for further usage: 
variable "azure_resource_group_name" {
  description = "Name of the Azure RG"
  type        = string
}

# Resolve the RG
data "azurerm_resource_group" "rg" {
  name = var.resource_group_name
}

data "azurerm_resources" "vnets" {
  resource_group_name = data.azurerm_resource_group.rg.name
  type                = "Microsoft.Network/virtualNetworks"
}

locals {
  vnet_index = {
    for r in data.azurerm_resources.vnets.resources :
    r.name =&gt; r
  }
}

data "azurerm_virtual_network" "vnet" {
  for_each            = local.vnet_index
  name                = each.value.name
  resource_group_name = data.azurerm_resource_group.rg.name
}

locals {
  vnet_subnet_ids = flatten([
    for v in data.azurerm_virtual_network.vnet :
    v.subnets
  ])

  subnets_map = {
    for sid in local.vnet_subnet_ids :
    sid =&gt; {
      id   = sid
      name = element(split("/", sid), length(split("/", sid)) - 1)
      vnet = element(split("/", sid), index(split("/", sid), "virtualNetworks") + 1)
    }
  }
}

data "azurerm_subnet" "subnet" {
  for_each             = local.subnets_map
  name                 = each.value.name
  virtual_network_name = each.value.vnet
  resource_group_name  = data.azurerm_resource_group.rg.name
}

data "azurerm_resources" "nsgs_in_rg" {
  resource_group_name = data.azurerm_resource_group.rg.name
  type                = "Microsoft.Network/networkSecurityGroups"
}

locals {
  nsgs_in_rg_index = {
    for r in data.azurerm_resources.nsgs_in_rg.resources :
    r.name =&gt; r
  }
}

data "azurerm_network_security_group" "nsg_in_rg" {
  for_each            = local.nsgs_in_rg_index
  name                = each.value.name
  resource_group_name = data.azurerm_resource_group.rg.name
}

locals {
  associated_nsg_ids = distinct(
    compact([
      for s in values(data.azurerm_subnet.subnet) : s.network_security_group_id
    ])
  )

  associated_nsg_map = {
    for id in local.associated_nsg_ids :
    id =&gt; {
      id = id
      # Parse NSG name and RG from the ARM ID
      name = element(split("/", id), length(split("/", id)) - 1)
      rg   = element(split("/", id), index(split("/", id), "resourceGroups") + 1)
    }
  }
}

data "azurerm_network_security_group" "associated" {
  for_each            = local.associated_nsg_map
  name                = each.value.name
  resource_group_name = each.value.rg
} (Optional) Check the available VM sizes and quotas in your Azure SubscriptionCaution It is best practice to check whether your subscription has enough VMs of the chosen size available, as not all Machine Sizes are available in all Azure locations. Be sure that your subscription has no quota limitation on the chosen VM type and that you have enough resources on Azure to create all the Virtual Machines planned to put into the Machine Catalog by checking quotas. Otherwise, the creation of the Machine Catalog will fail if there are not enough compute resources available! Use PowerShell to list the available Machine Sizes for Windows Server-based VMs. In this example, we plan to deploy "D2-based" VMs - change the parameters according to your needs: PS C:\_TMM&gt; $location="austriaeast"
PS C:\_TMM&gt; Get-AzVMSize -Location $location | Where Name -like 'Standard_D2*' | Select Name,NumberOfCores,MemoryInMB,OSDiskSizeInMB,ResourceDiskSizeInMB | Format-Table

Name                 NumberOfCores MemoryInMB OSDiskSizeInMB ResourceDiskSizeInMB
----                 ------------- ---------- -------------- --------------------
Standard_D2a_v4                  2       8192        1047552                51200
Standard_D2as_v4                 2       8192        1047552                16384
Standard_D2as_v5                 2       8192        1047552                    0
Standard_D2ads_v5                2       8192        1047552                76800
Standard_D2_v2                   2       7168        1047552               102400
Standard_D2_v2_Promo             2       7168        1047552               102400
Standard_D2_v3                   2       8192        1047552                51200
Standard_D2s_v3                  2       8192        1047552                16384
Standard_D2ds_v4                 2       8192        1047552                76800
Standard_D2ds_v5                 2       8192        1047552                76800
Standard_D2d_v4                  2       8192        1047552                76800
Standard_D2d_v5                  2       8192        1047552                76800
Standard_D2s_v4                  2       8192        1047552                    0
Standard_D2s_v5                  2       8192        1047552                    0
Standard_D2_v4                   2       8192        1047552                    0
Standard_D2_v5                   2       8192        1047552                    0
Standard_D2ls_v5                 2       4096        1047552                    0
Standard_D2lds_v5                2       4096        1047552                76800
Standard_D2                      2       7168        1047552               102400
Standard_D2plds_v5               2       4096        1047552                76800
Standard_D2pls_v5                2       4096        1047552                    0
Standard_D2pds_v5                2       8192        1047552                76800
Standard_D2ps_v5                 2       8192        1047552                    0
Standard_D2pls_v6                2       4096        1047552                    0
Standard_D2plds_v6               2       4096        1047552                    0
Standard_D2ps_v6                 2       8192        1047552                    0
Standard_D2pds_v6                2       8192        1047552                    0
Standard_D2ls_v6                 2       4096        1047552                    0
Standard_D2lds_v6                2       4096        1047552                    0
Standard_D2s_v6                  2       8192        1047552                    0
Standard_D2ds_v6                 2       8192        1047552                    0
Standard_D2as_v6                 2       8192        1047552                    0
Standard_D2ads_v6                2       8192        1047552                    0
Standard_D2als_v6                2       4096        1047552                    0
Standard_D2alds_v6               2       4096        1047552                    0

PS C:\_TMM&gt;Use PowerShell to list the available Machine Sizes for Windows Server-based VMs. In this example, we check the vCPU quotas for a "D2S_V5"-based VM - change the parameters according to your needs: PS C:\_TMM&gt; $Location = 'austriaeast'
PS C:\_TMM&gt; $VMSize = 'Standard_D2s_v5'
PS C:\_TMM&gt; $SKU = Get-AzComputeResourceSku -Location $Location | where ResourceType -eq "virtualMachines" | select Name,Family
PS C:\_TMM&gt; $VMFamily = ($SKU | where Name -eq $VMSize | select -Property Family).Family
PS C:\_TMM&gt; Get-AzVMUsage -Location $Location | Where-Object { $_.Name.Value -eq $VMFamily }

Name                       Current Value Limit  Unit
----                       ------------- -----  ----
Standard DSv5 Family vCPUs            18   350 Count

PS C:\_TMM&gt;This example shows that we can create enough D2S_v5 VMs before exceeding the limit. The Azure Console or PowerShell can increase the vCPU or any other quota. More information about increasing vCPU quotas can be found here: https://learn.microsoft.com/en-us/azure/quotas/per-vm-quota-requests.  Creating a Shared Image Gallery on AzureThe next step is to create a Shared Image Gallery on Azure, where Packer will later upload the images. Based on that image, an image definition and an image version will be created in Azure for further use. # Create a Citrix Cloud Resource Location and DaaS Deployment
## Create Pre-requisites
### Create a Shared Image Gallery
#### https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/shared_image_gallery

data "azurerm_resource_group" "TMM-RG" {
  name                = var.RG-Name
}

resource "azurerm_shared_image_gallery" "TACG-AZ-V2-SIG" {
  name                = var.SIG-Name
  description         = var.SIG-Description
  resource_group_name = data.azurerm_resource_group.TMM-RG.name
  location            = data.azurerm_resource_group.TMM-RG.location
  tags    			  = {
               					Environment        = var.TAG-Environment,
               					Environment-Entity = var.TAG-Environment-Entity,
               					Environment-Usage  = var.TAG-Environment-Usage,
  }
} Creating a Service Principal in Citrix Cloud for AuthenticationA Service Principal in Citrix Cloud acts as an API client to Citrix Cloud APIs: Service principals have their own roles and permissions. These roles and permissions are distinct from the creator’s roles and permissions. Service principals are scoped to a single Citrix Cloud customer. To access more than one customer, you must create a distinct service principal within each customer. Service principals’ secrets expire on an expiration date of your choice. Service principals currently only support custom access. Full access will be available soon once all resource provider services support service principals.  Select the Identity and Access Management option from the menu to create a Service Principal. 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, Service principals:    Click Create service principal    Enter a Name and click Next:    Choose the correct Access settings and click Next:    Choose the Expiration and click Next:    Review the settings and click Complete:    After the Service Principal 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 customer ID field can also be found in your Citrix Cloud details. Put the values in the corresponding .auto.tvars.json file: ...
 "CC_APIKey-ClientID":"f4xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx5",
 "CC_APIKey-ClientSecret":"VxxxxxxxA==",
 "CC_CustomerID": "uxxxxxxxxj"
...Now that the prerequisites are complete, we can jump into creation.  Part 2: Using Packer to create the Master ImagesConfiguring Packer to create the Master ImagePacker uses the HashiCorp Configuration Language (HCL) to define build templates for creating machine images. HCL is a human-readable, declarative language designed by HashiCorp for defining infrastructure and automation workflows. It is used across HashiCorp tools like Terraform, Packer, and Vault because it balances readability for humans with machine-parsability. Key Features of HCL: Declarative syntax: You describe what you want, not how to do it Blocks and attributes: Resources are defined in blocks with key-value pairs Supports variables, interpolation, and conditionals Let´s look at a valid example of an HCL to create a Windows 11-based master image on Azure. In this example, we use Chocolatey to deploy software packages, and upload the final image to an Azure Image Gallery – from there, you can, for example, create an image definition and image version in Citrix DaaS to deploy a Machine catalog. Note You can find more information on using Packer, Chocolatey, and Ansible together to deploy software packages, Windows components, and Windows settings in the third part of the Citrix Automation Handbook. win11-azure.packer.hcl: # Defining all used variables
variable "azure_clientid" {
  type        = string
  description = "azure Service Principal App ID"
  sensitive   = true
}

variable "azure_clientsecret" {
  type        = string
  description = "azure Service Principal Secret"
  sensitive   = true
}

variable "azure_subscriptionid" {
  type        = string
  description = "azure Subscription ID"
  sensitive   = true
}

variable "azure_tenantid" {
  type        = string
  description = "azure Tenant ID"
  sensitive   = true
}

...

variable "vda_location" {
  type = string
}

variable "script_installvda" {
  type    = string
  default = "install-vda.ps1"
}

variable "script_optimizer" {
  type    = string
  default = "run-optimizer.ps1"
}

variable "script_cleanup" {
  type    = string
  default = "run-cleanup.ps1"
}

variable "location_setup" {
  type    = string
  default = "c:\\setup"
}

# Defining the used Packer Plugin(s)
packer {
  required_plugins {
    azure = {
      source  = "github.com/hashicorp/azure"
      version = "&gt;= 2.5.0"
    }
  }
}

# Defining the source(s) block(s)
source "azure-arm" "W11MIWithSWPackagesWithVDA" {
  # Tagging the Azure VM
  azure_tags = {
    environment        = var.azure_tag_environment,
    environment-entity = var.azure_tag_environment_entity,
    usage              = var.azure_tag_usage
  }

  # WinRM Communicator
  communicator   = "winrm"
  winrm_use_ssl  = true
  winrm_insecure = false
  winrm_timeout  = "5m"
  winrm_username = "packer"

  # Service Principal Authentication
  client_id       = var.azure_clientid
  client_secret   = var.azure_clientsecret
  subscription_id = var.azure_subscriptionid
  tenant_id       = var.azure_tenantid

  # Source Image
  os_type         = "Windows"
  image_publisher = var.azure_imgpublisher
  image_offer     = var.azure_imgoffer
  image_sku       = var.azure_imgsku
  image_version   = var.azure_imgversion

  # Destination Image - we want to upload the artifact directly into the Azure Image Gallery, so no creation of a stand-alone image is needed
  # managed_image_resource_group_name = var.azure_RG
  # managed_image_name                = var.azure_ManagedImgName

  # Store created Image in Shared Image Gallery 
  shared_image_gallery_destination {
    resource_group       = var.azure_rg
    gallery_name         = var.azure_sig_name
    image_name           = var.azure_sig_imgname
    image_version        = var.azure_sig_imgversion
    replication_regions  = ["austriaeast"]
    storage_account_type = "Standard_LRS"
  }

  # Packer Computing Resources
  build_resource_group_name = var.azure_temprg
  vm_size                   = var.azure_vmsize

}

# Defining the building blocks
build {
  source "azure-arm.W11MIWithSWPackagesWithVDA" {}

  # Install Chocolatey: https://chocolatey.org/install#individual
  provisioner "powershell" {
    inline = ["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'))"]
  }

  # Install Chocolatey packages
  provisioner "file" {
    source      = "./scripts/SWPackagesToInstall.config"
    destination = "${var.location_setup}/scripts/SWPackagesToInstall.config"
  }

  provisioner "powershell" {
    inline = ["choco install --ignore-hash --ignore-checksums --confirm ${var.location_setup}/scripts/SWPackagesToInstall.config"]
    # See https://docs.chocolatey.org/en-us/choco/commands/install#exit-codes
    valid_exit_codes = [0, 3, 1641, 3010]
  }

  provisioner "windows-restart" {}

  provisioner "powershell" {
    inline = ["New-Item -Path ${var.location_setup} -Force -ItemType \"directory\" | Out-Null"]
  }

  provisioner "file" {
    destination = "${var.location_setup}/${var.script_installvda}"
    source      = "./scripts/${var.script_installvda}"
  }

  provisioner "file" {
    destination = "${var.location_setup}/${var.script_optimizer}"
    source      = "./scripts/${var.script_optimizer}"
  }

  provisioner "powershell" {
    inline = ["${var.location_setup}/${var.script_installvda} -VdaSetupDownloadUri ${var.vda_location}"]
  }

  provisioner "windows-restart" {
    restart_check_command = "powershell -command \"&amp; {Write-Host 'Script: computer restarted.'}\""
    restart_timeout       = "30m"
  }

  provisioner "powershell" {
    inline = ["${var.location_setup}/${var.script_installvda} -VdaSetupDownloadUri ${var.vda_location}"]
  }

  provisioner "powershell" {
    inline = ["$assemblyPath = \"C:\\Program Files (x86)\\Citrix\\ICA Client\\wfica32.exe\"", "$ngen64 = \"C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\ngen.exe\"", "$ngen32 = \"C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319\\ngen.exe\"", "Write-Host \"Running 64-bit Ngen...\"", "&amp; $ngen64 executequeueditems", "Write-Host \"Running 32-bit Ngen...\"", "&amp; $ngen32 executequeueditems"]
  }

  provisioner "powershell" {
    inline = ["${var.location_setup}/${var.script_optimizer} -OptimizerDownloadUri ${var.optimizer_location} -Template ${var.optimizer_template}"]
  }

  # Generalize image using Sysprep
  # See https://www.packer.io/docs/builders/azure/arm#windows
  # See https://docs.microsoft.com/en-us/azure/virtual-machines/windows/build-image-with-packer#define-packer-template
  provisioner "powershell" {
    inline = [
      "while ((Get-Service RdAgent).Status -ne 'Running') { Start-Sleep -s 5 }",
      "while ((Get-Service WindowsazureGuestAgent).Status -ne 'Running') { Start-Sleep -s 5 }",
      "&amp; $env:SystemRoot\\System32\\Sysprep\\Sysprep.exe /oobe /generalize /quiet /quit /mode:vm",
      "while ($true) { $imageState = Get-ItemProperty HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State | Select ImageState; if($imageState.ImageState -ne 'IMAGE_STATE_GENERALIZE_RESEAL_TO_OOBE') { Write-Output $imageState.ImageState; Start-Sleep -s 10  } else { break } }"
    ]
  }
}The variables are defined in a key-value format - example: win11-azure.auto.pkrvars.hcl: azure_clientid       = &lt;sensitive&gt;
azure_clientsecret   = &lt;sensitive&gt;
azure_subscriptionid = &lt;sensitive&gt;
azure_tenantid       = &lt;sensitive&gt;
azure_rg             = "&lt;sensitive&gt;"
azure_temprg         = "Temp-&lt;sensitive&gt;"
azure_imgpublisher   = "MicrosoftWindowsDesktop"
azure_imgoffer       = "windows-11"
azure_imgsku         = "win11-25h2-pro"
azure_imgversion     = "26200.7462.251207"
...
vda_location         = "https://&lt;sensitive&gt;.blob.core.windows.net/ctxsw/VDAWorkstationSetup_2507.exe"
script_installvda    = "install-vda.ps1"
script_optimizer     = "run-optimizer.ps1"
script_cleanup       = "run-cleanup.ps1"
location_setup       = "c:\\ttemp"
optimizer_location   = "https://&lt;sensitive&gt;.blob.core.windows.net/ctxsw/CitrixOptimizerTool.zip"
optimizer_template   = "Citrix_Windows_11_2009.xml" Most Important The Image definition on the Azure Image Gallery  MUST exist before running Packer, otherwise the deployment will fail.  If you want to start the creation process manually, you can use the well-known Packer command: azadmin@az-weur-devops-automation:/etc/terraform-tmm/PACKER/_AZURE$ packer build -on-error=cleanup -force -var-file="win11-azure.auto.pkrvars.hcl" win11-azure.pkr.hclPacker writes out all relevant information during the building process: ==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Running builder ...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Creating Azure Resource Manager (ARM) client ...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: ARM Client successfully created
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Getting source image id for the deployment ...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  -&gt; SourceImageName: '/subscriptions/&lt;sensitive&gt;/providers/Microsoft.Compute/locations/austriaeast/publishers/MicrosoftWindowsDesktop/ArtifactTypes/vmimage/offers/windows-11/skus/win11-25h2-pro/versions/26200.7462.251207'

...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Getting the VM's IP address ...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  -&gt; ResourceGroupName   : '&lt;sensitive&gt;'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  -&gt; PublicIPAddressName : 'pkripgf6zyyvkul'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  -&gt; NicName             : 'pkrnigf6zyyvkul'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  -&gt; Network Connection  : 'PublicEndpoint'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  -&gt; IP Address          : '4.210.182.179'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Waiting for WinRM to become available...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: WinRM connected.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Connected to WinRM!
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Provisioning with Powershell...
...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Forcing web requests to allow TLS v1.2 (Required for requests to Chocolatey.org)
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Getting latest version of the Chocolatey package for download.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Not using proxy.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Getting Chocolatey from https://community.chocolatey.org/api/v2/package/chocolatey/2.6.0.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Downloading https://community.chocolatey.org/api/v2/package/chocolatey/2.6.0 to C:\Users\packer\AppData\Local\Temp\chocolatey\chocoInstall\chocolatey.zip
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Not using proxy.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Extracting C:\Users\packer\AppData\Local\Temp\chocolatey\chocoInstall\chocolatey.zip to C:\Users\packer\AppData\Local\Temp\chocolatey\chocoInstall
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Installing Chocolatey on the local machine
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Creating ChocolateyInstall as an environment variable (targeting 'Machine')
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:   Setting ChocolateyInstall to 'C:\ProgramData\chocolatey'
...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: c:\ttemp/scripts/SWPackagesToInstall.config
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: By installing, you accept licenses for the packages.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Installing the following packages:
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: adobereader
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: googlechrome
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: git.install
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: sysinternals
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: winscp.install
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: teamviewer
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: dropbox
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Downloading package from source 'https://community.chocolatey.org/api/v2/'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Progress: Downloading chocolatey-compatibility.extension 1.0.0... 100%

...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Downloading package from source 'https://community.chocolatey.org/api/v2/'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Progress: Downloading GoogleChrome 143.0.7499.41... 100%
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: GoogleChrome v143.0.7499.41[Approved]
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: GoogleChrome package files install completed. Performing other installation steps.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Downloading googlechrome 64 bit
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:   from 'https://dl.google.com/dl/chrome/install/googlechromestandaloneenterprise64.msi'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Download of googlechromestandaloneenterprise64.msi (-1 B) completed.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: WARNING: Ignoring checksums due to feature checksumFiles turned off or option --ignore-checksums set.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Installing googlechrome...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: googlechrome has been installed.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:   GoogleChrome may be able to be automatically uninstalled.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  The install of GoogleChrome was successful.

...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Chocolatey installed 13/13 packages.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  See the log for details (C:\ProgramData\chocolatey\logs\chocolatey.log).
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Installed:
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  - chocolatey-compatibility.extension v1.0.0
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  - chocolatey-core.extension v1.4.0
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  - chocolatey-dotnetfx.extension v1.0.1
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  - chocolatey-visualstudio.extension v1.13.0
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  - chocolatey-windowsupdate.extension v1.0.5
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  - adobereader v2025-1-20577
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  - filezilla v3.69.5
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  - GoogleChrome v143.0.7499.41
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  - git.install v2.52.0
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  - sysinternals v2025.11.17
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  - winscp.install v6.5.5
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  - teamviewer v15.72.6
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  - dropbox v238.4.6075
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Restarting Machine
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Waiting for machine to restart...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: A system shutdown is in progress.(1115)
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: pkrvmgf6zyyvkul restarted.
...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Uploading ./scripts/install-vda.ps1 =&gt; c:\ttemp/install-vda.ps1
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Uploading ./scripts/run-optimizer.ps1 =&gt; c:\ttemp/run-optimizer.ps1
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Provisioning with Powershell...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Provisioning with powershell script: C:\Users\packer\AppData\Local\Temp\powershell-provisioner3550365223
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 01:55:50 - Initiating Ctrix VDA installation.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 01:55:50 - Adding firewall allow rules for ports 80, 443, 1494, 2598, 8008
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 01:55:53 - Finished adding firewall allow rules for ports 80, 443, 1494, 2598, 8008
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 01:55:53 - Downloading VDASetup file from blob storage
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 01:56:05 - Executing: C:\Users\packer\AppData\Local\Temp\VDASetup.exe, Components: VDA
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 01:56:05 - Parameters: /quiet /noreboot /masterimage /enable_real_time_transport /enable_hdx_ports /components vda /includeadditional "Citrix MCS IODriver","Citrix VDA Upgrade Agent"
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 01:56:40 - XenDesktopVDA installation started
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 01:56:40 - Waiting few seconds to make sure the process extracts and starts
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 01:58:40 - Checking if XenDesktopVdaSetup process is running
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 01:58:40 - Installation process still running

...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 02:00:40 - Installation process completed
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 02:00:40 - GctRegistration already enabled
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 02:00:40 - BrokerAgent found and it is running.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 02:00:40 - Installation of XenDesktopVDA completed with success
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Restarting Machine
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Waiting for machine to restart...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: A system shutdown is in progress.(1115)
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: computer restarted.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: pkrvmgf6zyyvkul restarted.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Machine successfully restarted, moving on

...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Provisioning with Powershell...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Provisioning with powershell script: C:\Users\packer\AppData\Local\Temp\powershell-provisioner2019159959
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 02:20:55 - Downloading Citrix Optimizer from blob storage
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 02:20:55 - Executing: C:\Users\packer\AppData\Local\Temp\CitrixOptimizer\CtxOptimizerEngine.ps1, Template: Citrix_Windows_11_2009.xml
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: ------------------------------
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: | Citrix Optimization Engine |
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: | Version 2.9                |
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: ------------------------------
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Running in execute mode
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Creating Logs folder 2025-12-12_14-20-55
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Starting session log
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Checking permissions
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Processing definition file C:\Users\packer\AppData\Local\Temp\CitrixOptimizer\Templates\Citrix_Windows_11_2009.xml

...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 02:24:38 - Optimization script completed
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Provisioning with Powershell...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: IMAGE_STATE_COMPLETE

...

==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Querying the machine's properties ...

...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Powering off machine ...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Publishing to Shared Image Gallery ...

...

==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  -&gt; Shared Gallery Image Version ID : '/subscriptions/&lt;sensitive&gt;/resourceGroups/TMM-&lt;sensitive&gt;/providers/Microsoft.Compute/galleries/&lt;sensitive&gt;/images/&lt;sensitive&gt;/versions/1.0.0'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Deleting Virtual Machine deployment and its attached resources...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Deleted -&gt; Microsoft.Compute/virtualMachines : 'pkrvmgf6zyyvkul'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Deleted -&gt; Microsoft.Network/networkInterfaces : 'pkrnigf6zyyvkul'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Deleted -&gt; Microsoft.Network/virtualNetworks : 'pkrvngf6zyyvkul'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Deleted -&gt; Microsoft.Network/publicIPAddresses : 'pkripgf6zyyvkul'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Deleted -&gt; Microsoft.Network/networkSecurityGroups : 'pkrsggf6zyyvkul'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Deleted -&gt; Microsoft.Compute/disks : '/subscriptions/&lt;sensitive&gt;/resourceGroups/TMM-sensitive/providers/Microsoft.Compute/disks/pkrosgf6zyyvkul'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Removing the created Deployment object: 'pkrdpgf6zyyvkul'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Deleting KeyVault created during build
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Deleted -&gt; Microsoft.KeyVault/vaults : 'pkrkvgf6zyyvkul'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Removing the created Deployment object: 'kvpkrdpgf6zyyvkul'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: The resource group was not created by Packer, not deleting ...
Build 'azure-arm.W11MIWithSWPackagesWithVDA' finished after 54 minutes 36 seconds.

==&gt; Wait completed after 54 minutes 36 seconds

==&gt; Builds finished. The artifacts of successful builds are:
--&gt; azure-arm.W11MIWithSWPackagesWithVDA: Azure.ResourceManagement.VMImage:

OSType: Windows
ManagedImageSharedImageGalleryId: /subscriptions/&lt;sensitive&gt;/resourceGroups/TMM-&lt;sensitive&gt;/providers/Microsoft.Compute/galleries/&lt;sensitive&gt;/images/&lt;sensitive&gt;/versions/1.0.0
SharedImageGalleryResourceGroup: TMM-&lt;sensitive&gt;
SharedImageGalleryName: &lt;sensitive&gt;
SharedImageGalleryImageName: &lt;sensitive&gt;
SharedImageGalleryImageVersion: 1.0.0
SharedImageGalleryReplicatedRegions: austriaeastThe creation was completed successfully. Important Please make sure you know the return code for each software package's successful installation, as it varies by package. Usually, ‘0’ indicates a successful installation, but, for example, the return code for a successful VDA deployment is ‘3’ if a reboot is required. You need to enter the correct return codes for all packages in the corresponding configuration step: valid_exit_codes = [0, 3, 1641, 3010] If Packer encounters another code, it will raise an error, and the deployment will fail.  Note If some errors occur during the creation process, you might enable Packer to write verbose logs by using: azadmin@az-weur-devops-automation:/etc/terraform-tmm/PACKER/_AZURE$ export PACKER_LOG=1 azadmin@az-weur-devops-automation:/etc/terraform-tmm/PACKER/_AZURE$ export PACKER_LOG_PATH="./packer.log" or start the building process with verbose logging: azadmin@az-weur-devops-automation:/etc/terraform-tmm/PACKER/_AZURE$ packer build -on-error=cleanup -debug -force -var-file="win11-azure.auto.pkrvars.hcl" win11-azure.pkr.hcl Depending on the underlying load on Azure, the deployment process lasts some time - in our example, it took around 55 minutes. The respective Azure Gallery reflects the newly created image and image version:    Part 3: Creating the Cloud Connector VMsTerraform creates 2 Cloud Connector VMs. Therefore, it creates various Azure entities, including a Network Interface with its IP configuration, associations with a Network Security Group, and VMs. # Create a Citrix Cloud Resource Location and DaaS Deployment
## Create Pre-requisites
### We are using existing entitites like the Resource Group, NSGs, Firewall Rules, etc
#### Get existing entity data
data "azurerm_virtual_network" "TACG-TMM-VNet" {
  name                = var.TACG-TMM-VNet-Name
  resource_group_name = var.TACG-TMM-ResourceGroup-Name
}

data "azurerm_subnet" "TACG-TMM-Subnet" {
  name                 = var.TACG-TMM-Subnet-Name
  virtual_network_name = data.azurerm_virtual_network.TACG-TMM-VNet.name
  resource_group_name  = var.TACG-TMM-ResourceGroup-Name
}

data "azurerm_network_security_group" "TACG-TMM-NSG" {
  name                 = var.TACG-TMM-NSG-Name
  resource_group_name  = var.TACG-TMM-ResourceGroup-Name
}

#### Create a new inbound rule in NSG to allow WinRM Communication
resource "azurerm_network_security_rule" "EnableWinRMInNSG" {
  depends_on = [ data.azurerm_network_security_group.TACG-TMM-NSG ]
  count 					  = var.TACG-TMM-NSGRule-CreateRule ? 1 : 0
  name                        = var.TACG-TMM-NSGRule-Name
  priority                    = var.TACG-TMM-NSGRule-Priority
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = var.TACG-TMM-NSGRule-InboundPorts
  source_address_prefix       = "VirtualNetwork"
  destination_address_prefix  = "VirtualNetwork"
  resource_group_name         = var.TACG-TMM-ResourceGroup-Name
  network_security_group_name = data.azurerm_network_security_group.TACG-TMM-NSG.name
}

#### Create a dedicated Network Interface and IP configuration for the CC1-VM
#### https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/network_interface
##### Set the values in the corresponding .auto.tfvars.json file
resource "azurerm_network_interface" "TACG-TMM-TF-CC1-NIC" {
  depends_on = [ data.azurerm_subnet.TACG-TMM-Subnet ]
  name                           = "${var.TACG-TMM-CC1-NIC-Name}"
  location                       = "${var.TACG-TMM-ResourceGroup-Location}"
  resource_group_name            = "${var.TACG-TMM-ResourceGroup-Name}"
  accelerated_networking_enabled = true
  dns_servers                    = data.azurerm_virtual_network.TACG-TMM-VNet.dns_servers
  tags                           = {
                                       Environment        = var.TAG-Environment,
                                       Environment-Entity = var.TAG-Environment-Entity,
                                       Environment-Usage  = var.TAG-Environment-Usage,
  }
  ip_configuration {
    name                          = var.TACG-TMM-CC1-IPC-Name
    subnet_id                     = data.azurerm_subnet.TACG-TMM-Subnet.id
    private_ip_address_allocation = var.TACG-TMM-CC1-IPC-Private_IP_Address_Allocation
    private_ip_address            = var.TACG-TMM-CC1-IPC-Private_IP_Address
  }
}

#### Create a dedicated Network Interface and IP configuration for the CC2-VM
#### https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/network_interface
##### Set the values in the corresponding .auto.tfvars.json file
resource "azurerm_network_interface" "TACG-TMM-TF-CC2-NIC" {
  depends_on = [ data.azurerm_subnet.TACG-TMM-Subnet ]
  name                           = var.TACG-TMM-CC2-NIC-Name
  location                       = var.TACG-TMM-ResourceGroup-Location
  resource_group_name            = var.TACG-TMM-ResourceGroup-Name
  accelerated_networking_enabled = true
  dns_servers                    = data.azurerm_virtual_network.TACG-TMM-VNet.dns_servers
  tags                           = {
                                       Environment        = var.TAG-Environment,
                                       Environment-Entity = var.TAG-Environment-Entity,
                                       Environment-Usage  = var.TAG-Environment-Usage,
  }
  ip_configuration {
    name                          = var.TACG-TMM-CC2-IPC-Name
    subnet_id                     = data.azurerm_subnet.TACG-TMM-Subnet.id
    private_ip_address_allocation = var.TACG-TMM-CC2-IPC-Private_IP_Address_Allocation
    private_ip_address            = var.TACG-TMM-CC2-IPC-Private_IP_Address
  }
}

#### Associate the CC1-Network Interface with an existing NSG
#### https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/network_security_group
##### Set the values in the corresponding .auto.tfvars.json file
resource "azurerm_network_interface_security_group_association" "TACG-TMM-TF-CC1-NICNSG" {
  depends_on = [ azurerm_network_interface.TACG-TMM-TF-CC1-NIC, data.azurerm_network_security_group.TACG-TMM-NSG ]
  network_interface_id      = azurerm_network_interface.TACG-TMM-TF-CC1-NIC.id
  network_security_group_id = data.azurerm_network_security_group.TACG-TMM-NSG.id
}

#### Associate the CC2-Network Interface with an existing NSG
#### https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/network_security_group
##### Set the values in the corresponding .auto.tfvars.json file
resource "azurerm_network_interface_security_group_association" "TACG-TMM-TF-CC2-NICNSG" {
  depends_on = [ azurerm_network_interface.TACG-TMM-TF-CC2-NIC, data.azurerm_network_security_group.TACG-TMM-NSG ]
  network_interface_id      = azurerm_network_interface.TACG-TMM-TF-CC2-NIC.id
  network_security_group_id = data.azurerm_network_security_group.TACG-TMM-NSG.id
}

resource "azurerm_windows_virtual_machine" "TACG-TMM-CC1" {
  depends_on = [ azurerm_network_interface_security_group_association.TACG-TMM-TF-CC1-NICNSG ]
  name                     = var.TACG-TMM-CC1-VM-Name
  location                 = var.TACG-TMM-ResourceGroup-Location
  resource_group_name      = var.TACG-TMM-ResourceGroup-Name
  network_interface_ids    = [azurerm_network_interface.TACG-TMM-TF-CC1-NIC.id]
  size                     = var.TACG-TMM-CCX-VM-Size
  admin_username           = var.TACG-TMM-CCX-VM-Loc-UN
  admin_password           = var.TACG-TMM-CCX-VM-Loc-PW
  provision_vm_agent       = true
  timezone                 = "W. Europe Standard Time"
  patch_mode           	   = "AutomaticByOS"
  enable_automatic_updates = true

  # Reboot setting is set to Never, because of Cloud COntrollers should not go down simultaneously due to a pending reboot!
  reboot_setting        = "Never"
  hotpatching_enabled   = false
  secure_boot_enabled   = false

  os_disk {
    name                 = "${var.TACG-TMM-CC1-VM-OSDrive-Name}"
    caching              = "${var.TACG-TMM-CCX-VM-OSDrive-Cache}"
    storage_account_type = "${var.TACG-TMM-CCX-VM-OSDrive-StorageType}"
  }

  tags                   = {
                                       Environment        = var.TAG-Environment,
                                       Environment-Entity = var.TAG-Environment-Entity,
                                       Environment-Usage  = var.TAG-Environment-Usage,
  }

#### Create the CC2-VM #### https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/virtual_machine
##### Set the values in the corresponding .auto.tfvars.json file
resource "azurerm_windows_virtual_machine" "TACG-TMM-CC2" {
  depends_on = [ azurerm_network_interface_security_group_association.TACG-TMM-TF-CC2-NICNSG ]
  name                     = var.TACG-TMM-CC2-VM-Name
  location                 = var.TACG-TMM-ResourceGroup-Location
  resource_group_name      = var.TACG-TMM-ResourceGroup-Name
  network_interface_ids    = [azurerm_network_interface.TACG-TMM-TF-CC2-NIC.id]
  size                     = var.TACG-TMM-CCX-VM-Size
  admin_username           = var.TACG-TMM-CCX-VM-Loc-UN
  admin_password           = var.TACG-TMM-CCX-VM-Loc-PW
  provision_vm_agent       = true
  timezone                 = "W. Europe Standard Time"
  patch_mode               = "AutomaticByOS"
  enable_automatic_updates = true

  # Reboot setting is set to Never, because of Cloud Controllers should not go down simultaneously due to a pending reboot!
  reboot_setting        = "Never"
  hotpatching_enabled   = false
  secure_boot_enabled   = false

  os_disk {
    name                 = var.TACG-TMM-CC2-VM-OSDrive-Name
    caching              = var.TACG-TMM-CCX-VM-OSDrive-Cache
    storage_account_type = var.TACG-TMM-CCX-VM-OSDrive-StorageType
  }

  tags                   = {
                                       Environment        = var.TAG-Environment,
                                       Environment-Entity = var.TAG-Environment-Entity,
                                       Environment-Usage  = var.TAG-Environment-Usage,
  }
}Running the snippet creates the VMs as intended: PS C:\TMM\CreateVMs&gt; terraform 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

azurerm_network_interface.TACG-TMM-TF-CC1-NIC: Creating...
azurerm_network_interface.TACG-TMM-TF-CC2-NIC: Creating...
azurerm_network_interface.TACG-TMM-TF-CC1-NIC: Still creating... [10s elapsed]
azurerm_network_interface.TACG-TMM-TF-CC2-NIC: Still creating... [10s elapsed]
azurerm_network_interface.TACG-TMM-TF-CC2-NIC: Creation complete after 12s [id=/subscriptions/.../Microsoft.Network/networkInterfaces/TFT-az-aeast-CC2-NIC]
azurerm_network_interface_security_group_association.TACG-TMM-TF-CC2-NICNSG: Creating...
azurerm_network_interface.TACG-TMM-TF-CC1-NIC: Still creating... [20s elapsed]
azurerm_network_interface_security_group_association.TACG-TMM-TF-CC2-NICNSG: Still creating... [10s elapsed]
azurerm_network_interface.TACG-TMM-TF-CC1-NIC: Creation complete after 23s [id=/subscriptions/.../Microsoft.Network/networkInterfaces/TFT-az-aeast-CC1-NIC]
azurerm_network_interface_security_group_association.TACG-TMM-TF-CC1-NICNSG: Creating...
azurerm_network_interface_security_group_association.TACG-TMM-TF-CC2-NICNSG: Creation complete after 11s [id=/subscriptions/.../Microsoft.Network/networkInterfaces/TFT-az-aeast-CC2-NIC|/subscriptions/...Microsoft.Network/networkSecurityGroups/AZ-aeast-GK-nsg]
azurerm_windows_virtual_machine.TACG-TMM-CC2: Creating...
azurerm_network_interface_security_group_association.TACG-TMM-TF-CC1-NICNSG: Still creating... [10s elapsed]
azurerm_windows_virtual_machine.TACG-TMM-CC2: Still creating... [10s elapsed]
azurerm_network_interface_security_group_association.TACG-TMM-TF-CC1-NICNSG: Creation complete after 11s [id=/subscriptions/.../Microsoft.Network/networkInterfaces/TFT-az-aeast-CC1-NIC|/subscriptions/.../Microsoft.Network/networkSecurityGroups/AZ-aeast-GK-nsg]
azurerm_windows_virtual_machine.TACG-TMM-CC1: Creating...
azurerm_windows_virtual_machine.TACG-TMM-CC2: Still creating... [20s elapsed]
azurerm_windows_virtual_machine.TACG-TMM-CC1: Still creating... [10s elapsed]
azurerm_windows_virtual_machine.TACG-TMM-CC2: Still creating... [30s elapsed]
azurerm_windows_virtual_machine.TACG-TMM-CC1: Still creating... [20s elapsed]
azurerm_windows_virtual_machine.TACG-TMM-CC2: Still creating... [40s elapsed]
azurerm_windows_virtual_machine.TACG-TMM-CC1: Still creating... [30s elapsed]
...
azurerm_windows_virtual_machine.TACG-TMM-CC1: Still creating... [1m40s elapsed]
azurerm_windows_virtual_machine.TACG-TMM-CC2: Still creating... [2m0s elapsed]
azurerm_windows_virtual_machine.TACG-TMM-CC1: Still creating... [1m50s elapsed]
azurerm_windows_virtual_machine.TACG-TMM-CC2: Creation complete after 2m5s [id=/subscriptions/.../Microsoft.Compute/virtualMachines/TFT-az-aeast-CC2]
azurerm_windows_virtual_machine.TACG-TMM-CC1: Still creating... [2m0s elapsed]
azurerm_windows_virtual_machine.TACG-TMM-CC1: Creation complete after 2m3s [id=/subscriptions/.../Microsoft.Compute/virtualMachines/TFT-az-aeast-CC1]

Apply complete! Resources: 6 added, 0 changed, 0 destroyed.

PS C:\TMM\CreateVMs&gt;Terraform successfully created the two Cloud Connector Virtual Machines. Now, the next step can be started.  Part 4: Putting the 2 Cloud Connector VMs Into the Active Directory DomainTerraform now puts the just-created 2 Cloud Connector VMs into the Active Directory Domain. Not all Terraform Providers have this ability, so we decided to show a Terraform Provider-agnostic way—we use an Ansible Playbook for this step. Schematic overview:  Terraform calls Ansible and the corresponding Ansible Playbook directly from its code snippet. Important Ensure flawless communication between Terraform and the Ansible Control Server. You can find more information about the communication flow, its configuration, and its importance in the first part of the Citrix Automation Handbook. The Ansible Playbook is written in YAML and contains all the necessary Information for joining the VMs into the Active Directory Domain - for example: ---
- name: join cc1 host to domain with automatic reboot
  hosts: cloudconnector1-ip

  tasks:
  - name: join cc1 host to domain with automatic reboot
    microsoft.ad.membership:
      dns_domain_name: wwco.net
      hostname: az-weur-tf-cc1
      domain_admin_user: XXXX@XXXX.XXX
      domain_admin_password: "XxXxXxXxXxXxXxXxX”
      domain_ou_path: "OU=Infra,OU=WestEUR,OU=Azure,OU=Cloud,OU=Compute,DC=wwco,DC=net"
      state: domain
      reboot: falseThe configuration can be started by following the standard Terraform workflow: # Create a Citrix Cloud Resource Location and DaaS Deployment
## Create Pre-requisites
### We are using existing entitites like the Resource Group, NSGs, Firewall Rules, etc
#### Call Ansible to Join VM to Domain
##### Copy Ansible Playbooks to Ansible Server
###### ***** IMPORTANT: You must set the permissions to the destination directory accordingly (sudo) chmod 777 otherwise the provisioning will fail *****

resource "null_resource" "CopyPlaybookForCC1ToAnsibleServer" {
connection {
    type        = var.TACG-TMM-Ansible_Connection-Type
	user        = var.TACG-TMM-Ansible_SecureAdmin-Username
    password    = var.TACG-TMM-Ansible_SecureAdmin-Password
    host        = var.TACG-TMM-Ansible_IP1
    port        = var.TACG-TMM-Ansible_Port
    https       = var.TACG-TMM-Ansible_HTTPS
    timeout     = var.TACG-TMM-Ansible_Timeout
 }

provisioner "file" {
  source      = var.TACG-TMM-Ansible-Playbook-CC1-Source
  destination = var.TACG-TMM-Ansible-Playbook-CC1-Destination
 }
}

resource "null_resource" "CopyPlaybookForCC2ToAnsibleServer" {
connection {
    type        = var.TACG-TMM-Ansible_Connection-Type
	user        = var.TACG-TMM-Ansible_SecureAdmin-Username
    password    = var.TACG-TMM-Ansible_SecureAdmin-Password
    host        = var.TACG-TMM-Ansible_IP2
    port        = var.TACG-TMM-Ansible_Port
    https       = var.TACG-TMM-Ansible_HTTPS
    timeout     = var.TACG-TMM-Ansible_Timeout
 }

provisioner "file" {
  source      = var.TACG-TMM-Ansible-Playbook-CC2-Source
  destination = var.TACG-TMM-Ansible-Playbook-CC2-Destination
 }
}

##### Connect to Ansible Interpreter and Add CC1 To Domain
resource "null_resource" "AddCC1ToDomain" {
  depends_on = [ null_resource.CopyPlaybookForCC1ToAnsibleServer ]

connection {
    type        = var.TACG-TMM-Ansible_Connection-Type
	user        = var.TACG-TMM-Ansible_SecureAdmin-Username
    password    = var.TACG-TMM-Ansible_SecureAdmin-Password
    host        = var.TACG-TMM-Ansible_IP1
    port        = var.TACG-TMM-Ansible_Port
    https       = var.TACG-TMM-Ansible_HTTPS
    timeout     = var.TACG-TMM-Ansible_Timeout
 }

 provisioner "remote-exec" {
     inline = var.TACG-TMM-Ansible-CMDForCC1
 }
}

##### Connect to Ansible Interpreter and Add CC2 To Domain
resource "null_resource" "AddCC2ToDomain" {
  depends_on = [ null_resource.CopyPlaybookForCC2ToAnsibleServer ]

connection {
    type        = var.TACG-TMM-Ansible_Connection-Type
	user        = var.TACG-TMM-Ansible_SecureAdmin-Username
    password    = var.TACG-TMM-Ansible_SecureAdmin-Password
    host        = var.TACG-TMM-Ansible_IP2
    port        = var.TACG-TMM-Ansible_Port
    https       = var.TACG-TMM-Ansible_HTTPS
    timeout     = var.TACG-TMM-Ansible_Timeout
 }

 provisioner "remote-exec" {
    inline = var.TACG-TMM-Ansible-CMDForCC2
 }
}Running the Terraform snippet puts the VMs into the domain: PS C:\TMM\UseAnsibleToJoinDomain&gt; terraform apply

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:

  # null_resource.AddCC1ToDomain will be created
  + resource "null_resource" "AddCC1ToDomain" {
      + id = (known after apply)
    }

  # null_resource.AddCC2ToDomain will be created
  + resource "null_resource" "AddCC2ToDomain" {
      + id = (known after apply)
    }

  # null_resource.CopyPlaybookForCC1ToAnsibleServer will be created
  + resource "null_resource" "CopyPlaybookForCC1ToAnsibleServer" {
      + id = (known after apply)
    }

  # null_resource.CopyPlaybookForCC2ToAnsibleServer will be created
  + resource "null_resource" "CopyPlaybookForCC2ToAnsibleServer" {
      + id = (known after apply)
    }

Plan: 4 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.CopyPlaybookForCC1ToAnsibleServer: Creating...
null_resource.CopyPlaybookForCC2ToAnsibleServer: Creating...
null_resource.CopyPlaybookForCC2ToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPlaybookForCC1ToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPlaybookForCC2ToAnsibleServer: Creation complete after 1s [id=1021300872990083837]
null_resource.AddCC2ToDomain: Creating...
null_resource.AddCC2ToDomain: Provisioning with 'remote-exec'...
null_resource.AddCC2ToDomain (remote-exec): Connecting to remote host via SSH...
null_resource.AddCC2ToDomain (remote-exec):   Host: 10.53.16.10
null_resource.AddCC2ToDomain (remote-exec):   Port: 5986
null_resource.AddCC2ToDomain (remote-exec):   User: svc_ansible
null_resource.AddCC2ToDomain (remote-exec):   Password: true
null_resource.AddCC2ToDomain (remote-exec):   HTTPS: true
null_resource.AddCC2ToDomain (remote-exec):   Insecure: false
null_resource.AddCC2ToDomain (remote-exec):   NTLM: false
null_resource.AddCC2ToDomain (remote-exec):   CACert: false
null_resource.AddCC2ToDomain (remote-exec):   SSH Agent: false
null_resource.AddCC2ToDomain (remote-exec):   Checking Host Key: false
null_resource.AddCC2ToDomain (remote-exec):   Target Platform: unix
null_resource.AddCC2ToDomain (remote-exec): Connected!
null_resource.CopyPlaybookForCC1ToAnsibleServer: Creation complete after 1s [id=4834864719047546946]
null_resource.AddCC1ToDomain: Creating...
null_resource.AddCC1ToDomain: Provisioning with 'remote-exec'...
null_resource.AddCC1ToDomain (remote-exec): Connecting to remote host via SSH...
null_resource.AddCC1ToDomain (remote-exec):   Host: 10.53.16.10
null_resource.AddCC1ToDomain (remote-exec):   Port: 5986
null_resource.AddCC1ToDomain (remote-exec):   User: svc_ansible
null_resource.AddCC1ToDomain (remote-exec):   Password: true
null_resource.AddCC1ToDomain (remote-exec):   HTTPS: true
null_resource.AddCC1ToDomain (remote-exec):   Insecure: false
null_resource.AddCC1ToDomain (remote-exec):   NTLM: false
null_resource.AddCC1ToDomain (remote-exec):   CACert: false
null_resource.AddCC1ToDomain (remote-exec):   SSH Agent: false
null_resource.AddCC2ToDomain (remote-exec):   Checking Host Key: false
null_resource.AddCC1ToDomain (remote-exec):   Target Platform: unix
null_resource.AddCC1ToDomain (remote-exec): Connected!

null_resource.AddCC2ToDomain (remote-exec): Using /etc/ansible/ansible.cfg as config file
null_resource.AddCC1ToDomain (remote-exec): Using /etc/ansible/ansible.cfg as config file

null_resource.AddCC2ToDomain (remote-exec): PLAY [join host to domain with automatic reboot] *******************************

null_resource.AddCC2ToDomain (remote-exec): TASK [Gathering Facts] *********************************************************

null_resource.AddCC1ToDomain (remote-exec): PLAY [join host to domain with automatic reboot] *******************************

null_resource.AddCC1ToDomain (remote-exec): TASK [Gathering Facts] *********************************************************
null_resource.AddCC2ToDomain (remote-exec): ok: [10.53.16.102]

null_resource.AddCC2ToDomain (remote-exec): TASK [join host to domain with automatic reboot] *******************************
null_resource.AddCC1ToDomain (remote-exec): ok: [10.53.16.101]

null_resource.AddCC1ToDomain (remote-exec): TASK [join host to domain with automatic reboot] *******************************
null_resource.AddCC2ToDomain: Still creating... [10s elapsed]
null_resource.AddCC1ToDomain: Still creating... [10s elapsed]
null_resource.AddCC2ToDomain (remote-exec): changed: [10.53.16.102] =&gt; {"changed": true, "reboot_required": true}

null_resource.AddCC2ToDomain (remote-exec): PLAY RECAP *********************************************************************
null_resource.AddCC2ToDomain (remote-exec): 10.53.16.102               : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

null_resource.AddCC2ToDomain: Creation complete after 15s [id=4528100002845834178]
null_resource.AddCC1ToDomain (remote-exec): changed: [10.53.16.101] =&gt; {"changed": true, "reboot_required": true}

null_resource.AddCC1ToDomain (remote-exec): PLAY RECAP *********************************************************************
null_resource.AddCC1ToDomain (remote-exec): 10.53.16.101               : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

null_resource.AddCC1ToDomain: Creation complete after 18s [id=2667672656305371565]

Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
PS C:\TMM\UseAnsibleToJoinDomain&gt;Terraform successfully put the two Cloud Connector Virtual Machines into the domain. Now, the next step can be started.  Part 5: Installing and Configuring the Cloud Connector SoftwareThis part installs and configures the Cloud Connector software and all required entities. It is split into the following configuration parts: Creating a new Resource Location and the adjacent Zone in Citrix Cloud Uploading the Cloud Connector software installer to the Cloud Connector VMs Dynamically creating the configuration file cwc.json based on the Resource Location- and Zone-Information, and uploading it to the Cloud Connector VMs Installing and configuring the Cloud Connector software using an Ansible Playbook Adding the Cloud Connectors to the newly created Resource Location Schematic overview:  The Ansible Playbook is written in YAML and contains all the necessary information for deploying the Cloud Connector software - for example: ---

- name: install citrix cloud connector
  hosts: cloudconnector1-ip

  tasks:
  - name: install citrix cloud connector
    ansible.windows.win_package:
      path: C:\temp\cwcconnector.exe
      product_id: CWCConnector.exe
      arguments:
        - /q
        - /ParametersFilePath:C:\temp\cwc.json
      state: present
    become: true
    become_method: runas
    become_user: SYSTEM Important In previous versions, the Terraform script would pause for some time after creating the Resource Location. This is due to time constraints on the back-end. The creation of the Zone related to the Resource Location takes some time—we have seen delays of up to 8 minutes before the Zone is created. The actual Terraform provider should handle that and wait until the creation is complete. The previous Wait commands are therefore commented out in the Terraform snippets. If you want them to be used, just remove the comments.  Example: Terraform snippet for configuring the Cloud Connector VMs # Create a Citrix Cloud Resource Location and DaaS Deployment on Microsoft Azure V2
## Create the Resource Location and install the Cloud Connectors create the Resource Location
### Set local variables and scripts
#### Create a Resource Location in Citrix Cloud
#### https://github.com/citrix/terraform-provider-citrix/tree/main/docs/resources
##### Set the values in the corresponding .auto.tfvars.json file
resource "citrix_cloud_resource_location" "CreateCCRLInCC" {
    name                 = "${var.TACG-TMM-CC-RL-Name}"
}

#### Create a Zone in the just created Resource Location in Citrix Cloud
resource "citrix_zone" "CreateZoneInCC" {
  depends_on = [ citrix_cloud_resource_location.CreateCCRLInCC ]
    resource_location_id = citrix_cloud_resource_location.CreateCCRLInCC.id
}

#### Wait 10mins until Background Processes have completed
/*
resource "time_sleep" "Wait10Minutes" {
  depends_on = [ citrix_zone.CreateZoneInCC ]
  create_duration         = "600s"
}
*/

resource "local_file" "WriteRLDataToFile" {
  # depends_on = [ time_sleep.Wait10Minutes ]
    content  = citrix_cloud_resource_location.CreateCCRLInCC.id
    filename = "${path.module}/assets/RLData.txt"
}

resource "local_file" "WriteZoneDataToFile" {
  # depends_on = [ time_sleep.Wait10Minutes ]
    content  = citrix_zone.CreateZoneInCC.id
    filename = "${path.module}/assets/ZoneData.txt"
}

#### Create JSON-file for Cloud Connector Installer
resource "local_file" "CWC-Configuration" {
 # depends_on = [ time_sleep.Wait10Minutes ]
  content  = jsonencode(
        {
        "customerName"          = var.cc-customerid}",
        "clientId"              = var.cc-apikey-clientId,
        "clientSecret"          = var.cc-apikey-clientSecret,
        "resourceLocationId"    = citrix_cloud_resource_location.CreateCCRLInCC.id
        "acceptTermsOfService"  = true
        }
      )
  filename = "${path.module}/assets/cwc.json"
}

#### Upload Cloud Connector Installer and Configuration file to CC1
##### Set the Provisioner-Connection
resource "null_resource" "UploadCCAndCWCToCC1" {
  depends_on = [ local_file.CWC-Configuration ]
  connection {
    type        = var.TACG-TMM-Ansible_Connection-Type
	user        = var.TACG-TMM-Ansible_SecureAdmin-Username
    password    = var.TACG-TMM-Ansible_SecureAdmin-Password
    host        = var.TACG-TMM-Ansible_IP1
    port        = var.TACG-TMM-Ansible_Port
    https       = var.TACG-TMM-Ansible_HTTPS
    timeout     = var.TACG-TMM-Ansible_Timeout
 }

###### Upload Cloud Connector Configuration to CC1
  provisioner "file" {
    source      = "${path.module}/assets/cwc.json"
    destination = "C:/temp/cwc.json"
  }

  ###### Upload Cloud Connector Installer to CC1
  provisioner "file" {
    source      = "${path.module}/assets/cwcconnector.exe"
    destination = "C:/temp/cwcconnector.exe"
  }
}

#### Upload Cloud Connector Installer and Configuration file to CC2
##### Set the Provisioner-Connection
resource "null_resource" "UploadCCAndCWCToCC2" {
  depends_on = [ local_file.CWC-Configuration ]
  connection {
    type        = var.TACG-TMM-Ansible_Connection-Type
	user        = var.TACG-TMM-Ansible_SecureAdmin-Username
    password    = var.TACG-TMM-Ansible_SecureAdmin-Password
    host        = var.TACG-TMM-Ansible_IP2
    port        = var.TACG-TMM-Ansible_Port
    https       = var.TACG-TMM-Ansible_HTTPS
    timeout     = var.TACG-TMM-Ansible_Timeout
 }

###### Upload Cloud Connector Configuration to CC2
  provisioner "file" {
    source      = "${path.module}/assets/cwc.json"
    destination = "C:/temp/cwc.json"
  }

  ###### Upload Cloud Connector Installer to CC2
  provisioner "file" {
    source      = "${path.module}/assets/cwcconnector.exe"
    destination = "C:/temp/cwcconnector.exe"
  }
}

###### ***** IMPORTANT: You must set the permissions to the destination directory accordingly (sudo) chmod 777 otherwise the provisioning will fail *****
resource "null_resource" "CopyPlaybookForCC1ToAnsibleServer" {
  depends_on = [ null_resource.UploadCCAndCWCToCC1 ]

connection {
    type        = var.TACG-TMM-Ansible_Connection-Type
	user        = var.TACG-TMM-Ansible_SecureAdmin-Username
    password    = var.TACG-TMM-Ansible_SecureAdmin-Password
    host        = var.TACG-TMM-Ansible_IP1
    port        = var.TACG-TMM-Ansible_Port
    https       = var.TACG-TMM-Ansible_HTTPS
    timeout     = var.TACG-TMM-Ansible_Timeout
 }

provisioner "file" {
  source      = var.TACG-TMM-Ansible-Playbook-CC1-Source
  destination = var.TACG-TMM-Ansible-Playbook-CC1-Destination
 }
}

resource "null_resource" "CopyPlaybookForCC2ToAnsibleServer" {
  depends_on = [ null_resource.UploadCCAndCWCToCC2 ]

connection {
    type        = var.TACG-TMM-Ansible_Connection-Type
	user        = var.TACG-TMM-Ansible_SecureAdmin-Username
    password    = var.TACG-TMM-Ansible_SecureAdmin-Password
    host        = var.TACG-TMM-Ansible_IP2
    port        = var.TACG-TMM-Ansible_Port
    https       = var.TACG-TMM-Ansible_HTTPS
    timeout     = var.TACG-TMM-Ansible_Timeout
 }

provisioner "file" {
  source      = var.TACG-TMM-Ansible-Playbook-CC2-Source
  destination = var.TACG-TMM-Ansible-Playbook-CC2-Destination
 }
}

##### Connect to Ansible Interpreter and Install CC on CC1
resource "null_resource" "InstallCCOnCC1" {
  depends_on = [ null_resource.CopyPlaybookForCC1ToAnsibleServer ]

connection {
    type        = var.TACG-TMM-Ansible_Connection-Type
	user        = var.TACG-TMM-Ansible_SecureAdmin-Username
    password    = var.TACG-TMM-Ansible_SecureAdmin-Password
    host        = var.TACG-TMM-Ansible_IP1
    port        = var.TACG-TMM-Ansible_Port
    https       = var.TACG-TMM-Ansible_HTTPS
    timeout     = var.TACG-TMM-Ansible_Timeout
 }

provisioner "remote-exec" {
     inline = var.TACG-TMM-Ansible-CMDForCC1
 }
}
 
##### Connect to Ansible Interpreter and and Install CC on CC2
resource "null_resource" "InstallCCOnCC2" {
  depends_on = [ null_resource.CopyPlaybookForCC2ToAnsibleServer ]

connection {
    type        = var.TACG-TMM-Ansible_Connection-Type
	user        = var.TACG-TMM-Ansible_SecureAdmin-Username
    password    = var.TACG-TMM-Ansible_SecureAdmin-Password
    host        = var.TACG-TMM-Ansible_IP2
    port        = var.TACG-TMM-Ansible_Port
    https       = var.TACG-TMM-Ansible_HTTPS
    timeout     = var.TACG-TMM-Ansible_Timeout
 }

 provisioner "remote-exec" {
     inline = var.TACG-TMM-Ansible-CMDForCC2
 }
}Running this snippet creates the necessary entities in Citrix Cloud and configures the Cloud Connector VMs. Due to the length of the output during the process, it will be partially omitted below: XxXxXxXxX@devops-automation:/etc/terraform-tmm/DeployCCs$ terraform apply

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_cloud_resource_location.CreateCCRLInCC will be created
  + resource "citrix_cloud_resource_location" "CreateCCRLInCC" {
      + id            = (known after apply)
      + internal_only = false
      + name          = "TF-Azure-WEUR"
      + time_zone     = "GMT Standard Time"
    }

  # citrix_zone.CreateZoneInCC will be created
  + resource "citrix_zone" "CreateZoneInCC" {
      + description          = (known after apply)
      + id                   = (known after apply)
      + name                 = (known after apply)
      + resource_location_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             = "./assets/cwc.json"
      + id                   = (known after apply)
    }

  # local_file.WriteRLDataToFile will be created
  + resource "local_file" "WriteRLDataToFile" {
      + content              = (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)
      + directory_permission = "0777"
      + file_permission      = "0777"
      + filename             = "./assets/RLData.txt"
      + id                   = (known after apply)
    }

  # local_file.WriteZoneDataToFile will be created
  + resource "local_file" "WriteZoneDataToFile" {
      + content              = (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)
      + directory_permission = "0777"
      + file_permission      = "0777"
      + filename             = "./assets/ZoneData.txt"
      + id                   = (known after apply)
    }

  # null_resource.CopyPlaybookForCC1ToAnsibleServer will be created
  + resource "null_resource" "CopyPlaybookForCC1ToAnsibleServer" {
      + id = (known after apply)
    }

  # null_resource.CopyPlaybookForCC2ToAnsibleServer will be created
  + resource "null_resource" "CopyPlaybookForCC2ToAnsibleServer" {
      + id = (known after apply)
    }

  # null_resource.InstallCCOnCC1 will be created
  + resource "null_resource" "InstallCCOnCC1" {
      + id = (known after apply)
    }

  # null_resource.InstallCCOnCC2 will be created
  + resource "null_resource" "InstallCCOnCC2" {
      + id = (known after apply)
    }

  # null_resource.UploadCCAndCWCToCC1 will be created
  + resource "null_resource" "UploadCCAndCWCToCC1" {
      + id = (known after apply)
    }

  # null_resource.UploadCCAndCWCToCC2 will be created
  + resource "null_resource" "UploadCCAndCWCToCC2" {
      + id = (known after apply)
    }

  # time_sleep.Wait10Minutes will be created
  + resource "time_sleep" "Wait10Minutes" {
      + create_duration = "600s"
      + id              = (known after apply)
    }

Plan: 12 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_cloud_resource_location.CreateCCRLInCC: Creating...
citrix_cloud_resource_location.CreateCCRLInCC: Creation complete after 3s [id=ba22XXXX-XXXX-XXXX-XXXX-XXXXXXXXa7c6]
citrix_zone.CreateZoneInCC: Creating...
citrix_zone.CreateZoneInCC: Still creating... [00m10s elapsed]
citrix_zone.CreateZoneInCC: Still creating... [00m20s elapsed]
...
citrix_zone.CreateZoneInCC: Still creating... [01m50s elapsed]
citrix_zone.CreateZoneInCC: Still creating... [02m00s elapsed]
citrix_zone.CreateZoneInCC: Creation complete after 2m3s [id=4b16XXXX-XXXX-XXXX-XXXX-XXXXXXXX2623]
time_sleep.Wait10Minutes: Creating...
time_sleep.Wait10Minutes: Still creating... [00m10s elapsed]
time_sleep.Wait10Minutes: Still creating... [00m20s elapsed]
...
time_sleep.Wait10Minutes: Still creating... [09m50s elapsed]
time_sleep.Wait10Minutes: Still creating... [10m00s elapsed]
time_sleep.Wait10Minutes: Creation complete after 10m0s [id=2025-10-14T12:07:31Z]
local_file.WriteZoneDataToFile: Creating...
local_file.WriteRLDataToFile: Creating...
local_file.WriteZoneDataToFile: Creation complete after 0s [id=843500af8ed08f26e10bfd8cecb070de73223227]
local_file.CWC-Configuration: Creating...
local_file.WriteRLDataToFile: Creation complete after 1s [id=f4b71055fe71f40a2c3c177871fd686bc6e276c2]
local_file.CWC-Configuration: Creation complete after 1s [id=3b27a001ebb3f6e16b9762acbde333fdf4521a6b]
null_resource.UploadCCAndCWCToCC1: Creating...
null_resource.UploadCCAndCWCToCC2: Creating...
null_resource.UploadCCAndCWCToCC1: Provisioning with 'file'...
null_resource.UploadCCAndCWCToCC2: Provisioning with 'file'...
#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadCCAndCWCToCC1: Provisioning with 'file'...
#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadCCAndCWCToCC2: Provisioning with 'file'...
null_resource.UploadCCAndCWCToCC2: Still creating... [00m10s elapsed]
null_resource.UploadCCAndCWCToCC1: Still creating... [00m10s elapsed]
...
null_resource.UploadCCAndCWCToCC1: Still creating... [20m20s elapsed]
null_resource.UploadCCAndCWCToCC2: Still creating... [20m20s elapsed]
#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadCCAndCWCToCC2: Creation complete after 20m25s [id=6362997336651914450]
null_resource.CopyPlaybookForCC2ToAnsibleServer: Creating...
null_resource.CopyPlaybookForCC2ToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPlaybookForCC2ToAnsibleServer: Creation complete after 1s [id=7075206156614440554]
null_resource.InstallCCOnCC2: Creating...
null_resource.InstallCCOnCC2: Provisioning with 'remote-exec'...
null_resource.InstallCCOnCC2 (remote-exec): Connecting to remote host via SSH...
null_resource.InstallCCOnCC2 (remote-exec):   Host: 10.53.16.10
...
null_resource.InstallCCOnCC2 (remote-exec): Connected!
...
null_resource.InstallCCOnCC2 (remote-exec): PLAY [install Citrix Cloud Connector™] ******************************************

null_resource.InstallCCOnCC2 (remote-exec): TASK [Gathering Facts] *********************************************************
null_resource.UploadCCAndCWCToCC1: Still creating... [20m30s elapsed]
null_resource.InstallCCOnCC2 (remote-exec): ok: [10.53.16.102]

null_resource.InstallCCOnCC2 (remote-exec): TASK [install citrix cloud connector] ******************************************
null_resource.InstallCCOnCC2: Still creating... [00m10s elapsed]
null_resource.UploadCCAndCWCToCC1: Still creating... [20m40s elapsed]
null_resource.InstallCCOnCC2: Still creating... [00m20s elapsed]
null_resource.UploadCCAndCWCToCC1: Still creating... [20m50s elapsed]
null_resource.InstallCCOnCC2: Still creating... [00m30s elapsed]
null_resource.UploadCCAndCWCToCC1: Still creating... [21m00s elapsed]
null_resource.InstallCCOnCC2: Still creating... [00m40s elapsed]
null_resource.UploadCCAndCWCToCC1: Still creating... [21m10s elapsed]
null_resource.InstallCCOnCC2: Still creating... [00m50s elapsed]
null_resource.UploadCCAndCWCToCC1: Still creating... [21m20s elapsed]
null_resource.InstallCCOnCC2: Still creating... [01m00s elapsed]
null_resource.InstallCCOnCC2 (remote-exec): changed: [10.53.16.102] =&gt; {"changed": true, "checksum": "28D0D0F2A545E18A4271850FA21B62A51DA6C62A", "rc": 0, "reboot_required": false}

null_resource.InstallCCOnCC2 (remote-exec): PLAY RECAP *********************************************************************
null_resource.InstallCCOnCC2 (remote-exec): 10.53.16.102               : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

null_resource.InstallCCOnCC2: Creation complete after 1m1s [id=8928953023351995099]
#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadCCAndCWCToCC1: Creation complete after 21m28s [id=4106024715508474007]
null_resource.CopyPlaybookForCC1ToAnsibleServer: Creating...
null_resource.CopyPlaybookForCC1ToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPlaybookForCC1ToAnsibleServer: Creation complete after 1s [id=1082050735244950664]
null_resource.InstallCCOnCC1: Creating...
null_resource.InstallCCOnCC1: Provisioning with 'remote-exec'...
null_resource.InstallCCOnCC1 (remote-exec): Connecting to remote host via SSH...
null_resource.InstallCCOnCC1 (remote-exec):   Host: 10.53.16.10
...
null_resource.InstallCCOnCC1 (remote-exec): Connected!
null_resource.InstallCCOnCC1 (remote-exec): PLAY [install citrix cloud connector] ******************************************
null_resource.InstallCCOnCC1 (remote-exec): TASK [Gathering Facts] *********************************************************
null_resource.InstallCCOnCC1 (remote-exec): ok: [10.53.16.101]
null_resource.InstallCCOnCC1 (remote-exec): TASK [install citrix cloud connector] ******************************************
null_resource.InstallCCOnCC1: Still creating... [00m10s elapsed]
null_resource.InstallCCOnCC1: Still creating... [00m20s elapsed]
null_resource.InstallCCOnCC1: Still creating... [00m30s elapsed]
null_resource.InstallCCOnCC1: Still creating... [00m40s elapsed]
null_resource.InstallCCOnCC1: Still creating... [00m50s elapsed]
null_resource.InstallCCOnCC1 (remote-exec): changed: [10.53.16.101] =&gt; {"changed": true, "checksum": "28D0D0F2A545E18A4271850FA21B62A51DA6C62A", "rc": 0, "reboot_required": false}

null_resource.InstallCCOnCC1 (remote-exec): PLAY RECAP *********************************************************************
null_resource.InstallCCOnCC1 (remote-exec): 10.53.16.101               : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

null_resource.InstallCCOnCC1: Creation complete after 57s [id=96612861702981347]

Apply complete! Resources: 12 added, 0 changed, 0 destroyed.
XxXxXxXxX@devops-automation:/etc/terraform-tmm/DeployCCs$Terraform has successfully created the Resource Location, the Zone, and deployed and configured the 2 Cloud Connectors:  Now, the next step can begin: creating the first Citrix DaaS entity, the Hypervisor Connection and Resource Pool.  Part 6: Creating the Hypervisor Connection and Hypervisor Resource PoolThe next step is to configure the Hypervisor Connection and the corresponding Hypervisor Resource Pool. Important There are many ways to create the Hypervisor Connection and the Hypervisor Resource Pool using Terraform, as Citrix Cloud supports multiple connections to different Hypervisors and Hyperscalers. Listing all possibilities, snippets, and runtime outputs is out of scope. Please consult the documentation at https://github.com/citrix/terraform-provider-citrix/tree/main/docs/resources for other examples. The example below is the most requested by customers: creating entities in Azure. Example: Terraform snippet to create a Hypervisor connection to Azure and to create the adjacent Resource Pool # Create a Citrix Cloud Resource Location and DaaS Deployment on Microsoft Azure
## Create all relevant Citrix Cloud Entities
### Set local variables and scripts
locals {}

### Get existing entity data
#### Retrieving the ZoneID
data "citrix_zone" "GetTFAzureZoneID" {
  name = var.CC-Azure-ZoneID
}

#### Creating the Hypervisor Connection
#### https://github.com/citrix/terraform-provider-citrix/tree/main/docs/resources
##### Set the values in the corresponding .auto.tfvars.json file

resource "citrix_azure_hypervisor" "CreateAzureHostingConnection" {
  depends_on = [ data.citrix_zone.GetTFAzureZoneID ]
    name                = var.CC-Azure-HypConn-Name
    zone                = data.citrix_zone.GetTFAzureZoneID.id
    active_directory_id = var.azurerm-tenantid
    subscription_id     = var.azurerm-subscriptionid
    application_secret  = var.azurerm-clientsecret
    application_id      = var.azurerm-clientid
    #scopes              = var.CC-GetScopeToBeUsed-Names
} 

#### Sleep 30s to let Background processes settle
resource "time_sleep" "wait_30_seconds" {
  depends_on = [ citrix_azure_hypervisor.CreateAzureHostingConnection ]
  create_duration = "30s"
}  

#### Creating the Hypervisor Resource Pool
#### https://github.com/citrix/terraform-provider-citrix/tree/main/docs/resources
##### Set the values in the corresponding .auto.tfvars.json file
resource "citrix_azure_hypervisor_resource_pool" "CreateAzureHostingConnectionPool" {
  depends_on = [ time_sleep.wait_30_seconds]
  name                           = var.CC-Azure-HypConn-Name
  hypervisor                     = citrix_azure_hypervisor.CreateAzureHostingConnection.id
  region                         = var.TACG-TMM-ResourceGroup-Location
  virtual_network_resource_group = var.TACG-TMM-ResourceGroup-Name
  virtual_network                = var.CC-Azure-VNet-Name
  subnets                        = var.CC-Azure-Subnets
  
}

#### Sleep 30s to let Background processes settle
resource "time_sleep" "wait_30_seconds1" {
  depends_on = [ citrix_azure_hypervisor_resource_pool.CreateAzureHostingConnectionPool ]
  create_duration = "30s"
}
Running the snippet creates the wanted entities. XxXxXxXxX@devops-automation:/etc/terraform-tmm/DeployCItrixCloudEntitites$ terraform apply
data.citrix_zone.GetTFAzureZoneID: Reading...
data.citrix_zone.GetTFAzureZoneID: Read complete after 0s [id=4b16XXXX-XXXX-XXXX-XXXX-XXXXXXXX2623]

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_azure_hypervisor.CreateAzureHostingConnection will be created
  + resource "citrix_azure_hypervisor" "CreateAzureHostingConnection" {
      + active_directory_id                        = (sensitive value)
      + application_id                             = (sensitive value)
      + application_secret                         = (sensitive value)
      + authentication_mode                        = "AppClientSecret"
      + enable_azure_ad_device_management          = false
      + id                                         = (known after apply)
      + name                                       = "TF-HC-TMM-Azure-WEUR-HypConn"
      + proxy_hypervisor_traffic_through_connector = false
      + scopes                                     = []
      + subscription_id                            = (sensitive value)
      + tenants                                    = (known after apply)
      + zone                                       = "4b16XXXX-XXXX-XXXX-XXXX-XXXXXXXX2623"
    }

  # citrix_azure_hypervisor_resource_pool.CreateAzureHostingConnectionPool will be created
  + resource "citrix_azure_hypervisor_resource_pool" "CreateAzureHostingConnectionPool" {
      + hypervisor                     = (known after apply)
      + id                             = (known after apply)
      + name                           = "TF-HC-TMM-Azure-WEUR-HypConn"
      + region                         = "westeurope"
      + subnets                        = [
          + "West-EUR-1-sn",
        ]
      + virtual_network                = "West-EUR-vnet"
      + virtual_network_resource_group = "TMM-TF-WEUR"
      + vm_tagging                     = true
    }

  # time_sleep.wait_30_seconds will be created
  + resource "time_sleep" "wait_30_seconds" {
      + create_duration = "30s"
      + id              = (known after apply)
    }

  # time_sleep.wait_30_seconds1 will be created
  + resource "time_sleep" "wait_30_seconds1" {
      + create_duration = "30s"
      + id              = (known after apply)
    }

Plan: 4 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + hypconnname = (known after apply)
  + hypname     = (known after apply)

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_azure_hypervisor.CreateAzureHostingConnection: Creating...
citrix_azure_hypervisor.CreateAzureHostingConnection: Still creating... [00m10s elapsed]
...
citrix_azure_hypervisor.CreateAzureHostingConnection: Still creating... [01m10s elapsed]
citrix_azure_hypervisor.CreateAzureHostingConnection: Creation complete after 1m12s [id=f183XXXX-XXXX-XXXX-XXXX-XXXXXXXX4de9]
time_sleep.wait_30_seconds: Creating...
time_sleep.wait_30_seconds: Still creating... [00m10s elapsed]
time_sleep.wait_30_seconds: Still creating... [00m20s elapsed]
time_sleep.wait_30_seconds: Still creating... [00m30s elapsed]
time_sleep.wait_30_seconds: Creation complete after 30s [id=2025-10-14T15:22:18Z]
citrix_azure_hypervisor_resource_pool.CreateAzureHostingConnectionPool: Creating...
citrix_azure_hypervisor_resource_pool.CreateAzureHostingConnectionPool: Still creating... [00m10s elapsed]
...
citrix_azure_hypervisor_resource_pool.CreateAzureHostingConnectionPool: Still creating... [01m30s elapsed]
citrix_azure_hypervisor_resource_pool.CreateAzureHostingConnectionPool: Creation complete after 1m36s [id=828bXXXX-XXXX-XXXX-XXXX-XXXXXXXX860d]
time_sleep.wait_30_seconds1: Creating...
time_sleep.wait_30_seconds1: Still creating... [00m10s elapsed]
time_sleep.wait_30_seconds1: Still creating... [00m20s elapsed]
time_sleep.wait_30_seconds1: Still creating... [00m30s elapsed]
time_sleep.wait_30_seconds1: Creation complete after 30s [id=2025-10-14T15:24:23Z]

Apply complete! Resources: 4 added, 0 changed, 0 destroyed.

XxXxXxXxX@devops-automation:/etc/terraform-tmm/DeployCItrixCloudEntitites$The Hypervisor Connection and the Hypervisor Resource Pool were successfully created. Tip We recommend running health checks during the first few runs to ensure the snippets and their adjacent entities are working as intended before proceeding to the next modules.   We can now proceed to the next step.  Part 7: Creating a Machine Catalog on AzureThe next step is to create a Machine Catalog in Azure based on the image version we created earlier. If you follow this approach, it is easy to update the Machine Catalog with a new version of the image - you only need to change the appropriate variable in the Terraform snippet. Important There are many ways to create the Machine Catalog using Terraform. Listing all possibilities, snippets, and runtime outputs is out of scope. Please consult the documentation at https://github.com/citrix/terraform-provider-citrix/blob/main/docs/resources/machine_catalog.md for other examples. The example below is the most requested by customers: creating entities in Azure. Example: Terraform snippet to create a Machine Catalog in Azure based on the Packer-generated Master Image stored in the Azure Shared Image Gallery # Create a Citrix Cloud Resource Location and DaaS Deployment on Microsoft Azure 
## Create all relevant Citrix Cloud Entities
### Set local variables and scripts
locals {}

### Get existing entity data
#### Retrieving the ZoneID
data "citrix_zone" "GetTFAzureZoneID" {
  name = var.CC-Azure-ZoneID
}

#### Retrieving the Shared Image Gallery
 data "azurerm_shared_image_gallery" "GetAzureSIG" {
  name                = var.CC-Azure-SIG-Name
  resource_group_name = var.TACG-TMM-ResourceGroup-Name
} 

#### Retrieving the Shared Image Definition
data "azurerm_shared_image" "GetAzureSIGDefinition" {
  name                = var.CC-Azure-SIGDefinition-Name
  gallery_name        = data.azurerm_shared_image_gallery.GetAzureSIG.name
  resource_group_name = var.TACG-TMM-ResourceGroup-Name
} 

#### Retrieving the Shared Image Definition Version
data "azurerm_shared_image_version" "GetAzureSIGDefinitionVersion" {
  name                = var.CC-Azure-SIGDefinitionVersion
  image_name          = data.azurerm_shared_image.GetAzureSIGDefinition.name
  gallery_name        = data.azurerm_shared_image_gallery.GetAzureSIG.name
  resource_group_name = var.TACG-TMM-ResourceGroup-Name
}

#### Retrieving the Hypervisor Connection
data "citrix_hypervisor" "CreateAzureHostingConnection" {
  name                = var.CC-Azure-HypConn-Name
} 

#### Retrieving the Hypervisor Resource Pool
data "citrix_hypervisor_resource_pool" "CreateAzureHostingConnectionPool" {
  name                = var.CC-Azure-HypConnPool-Name
  hypervisor_name     = data.citrix_hypervisor.CreateAzureHostingConnection.name
}

#### Sleep 30s to let Background processes settle
resource "time_sleep" "wait_30_seconds1" {
  create_duration = "30s"
}

#### Creating the Machine Catalog
resource "citrix_machine_catalog" "CreateAzureMCSCatalog" {
  depends_on            = [ time_sleep.wait_30_seconds1 ]
    name                        = var.CC-Azure-MC-Name
    description                 = var.CC-Azure-MC-Description
    allocation_type             = var.CC-Azure-MC-AllocationType
    session_support             = var.CC-Azure-MC-SessionType
    provisioning_type           = "MCS"
    zone               			= data.citrix_zone.GetTFAzureZoneID.id
    
    provisioning_scheme         =   {
        hypervisor               = data.citrix_hypervisor.CreateAzureHostingConnection.id
        hypervisor_resource_pool = data.citrix_hypervisor_resource_pool.CreateAzureHostingConnectionPool.id
        identity_type            = var.CC-Azure-MC-IDPType 
        machine_domain_identity  = {
            domain                   = var.CC-Azure-MC-Domain
            domain_ou                = var.CC-Azure-MC-DomainOU
            service_account          = var.CC-Azure-MC-DomainAdmin-Username-UPN
            service_account_password = var.CC-Azure-MC-DomainAdmin-Password
            }

        azure_machine_config = {
            storage_type             = "Standard_LRS"
            use_managed_disks        = true
            service_offering         = var.CC-Azure-MC-VMSize
            azure_master_image = {
                resource_group       = var.TACG-TMM-ResourceGroup-Name
                gallery_image        = {
                     gallery    = data.azurerm_shared_image_gallery.GetAzureSIG.name
                     definition = data.azurerm_shared_image.GetAzureSIGDefinition.name
                     version    = data.azurerm_shared_image_version.GetAzureSIGDefinitionVersion.name
                 }
            }
        }
       number_of_total_machines                =  var.CC-Azure-MC-VMNumbers
    
       machine_account_creation_rules          = {
            naming_scheme      = var.CC-Azure-MC-NamingScheme-Name
            naming_scheme_type = var.CC-Azure-MC-NamingScheme-Type
        }
    }
}Running the Terraform snippet creates the Machine Catalog: XxXxXxXxX@devops-automation:/etc/terraform-tmm/DeployMCS$ terraform apply
data.citrix_zone.GetTFAzureZoneID: Reading...
data.citrix_hypervisor.CreateAzureHostingConnection: Reading...
data.citrix_hypervisor.CreateAzureHostingConnection: Read complete after 0s [id=f183XXXX-XXXX-XXXX-XXXXXXXX4de9]
data.citrix_hypervisor_resource_pool.CreateAzureHostingConnectionPool: Reading...
data.citrix_hypervisor_resource_pool.CreateAzureHostingConnectionPool: Read complete after 0s [id=828bXXXX-XXXX-XXXX-XXXX-XXXXXXXX860d]
data.citrix_zone.GetTFAzureZoneID: Read complete after 0s [id=4b16XXXX-XXXX-XXXX-XXXX-XXXXXXXX860d2623]
data.azurerm_shared_image_gallery.GetAzureSIG: Reading...
...

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:
...

Plan: 2 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_30_seconds1: Creating...
time_sleep.wait_30_seconds1: Still creating... [00m10s elapsed]
time_sleep.wait_30_seconds1: Still creating... [00m20s elapsed]
time_sleep.wait_30_seconds1: Still creating... [00m30s elapsed]
time_sleep.wait_30_seconds1: Creation complete after 30s [id=2025-10-23T11:22:35Z]
citrix_machine_catalog.CreateAzureMCSCatalog: Creating...
citrix_machine_catalog.CreateAzureMCSCatalog: Still creating... [00m10s elapsed]
...
citrix_machine_catalog.CreateAzureMCSCatalog: Still creating... [10m40s elapsed]
citrix_machine_catalog.CreateAzureMCSCatalog: Creation complete after 10m42s [id=a283XXXX-XXXX-XXXX-XXXX-XXXXXXXX860d424e]

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

XxXxXxXxX@devops-automation:/etc/terraform-tmm/DeployMCs$The Machine Catalog based on an Azure Shared Image was successfully created. Important To change the base image of the Machine Catalog, update the variable that sets the image version – assuming that the image definition is the same: var.CC-Azure-SIGDefinitionVersion To change the number of machines in the Machine Catalog, update the variable that sets the number: number_of_total_machines = var.CC-Azure-MC-VMNumbers You can find more information about daily administrative tasks such as changes of the master image in the third part of the Citrix Automation Handbook.  Tip We recommend running health checks during the first few runs to ensure the snippets and their adjacent entities are working as intended before proceeding to the next modules.  We can now proceed to the next step.  Part 8: Creating a Delivery GroupThe next step is to create a Delivery Group based on the just-created Machine Catalog. Important There are many ways to create the Delivery Group using Terraform. Listing all possibilities, snippets, and runtime outputs is out of scope. Please consult the documentation at https://github.com/citrix/terraform-provider-citrix/blob/main/docs/resources/delivery_group.md for other examples. The example below is the most requested by customers: creating entities in Azure. Example: Terraform snippet to create a Delivery Group based on the Machine Catalog created earlier: # Create a Citrix Cloud Resource Location and DaaS Deployment on Microsoft Azure2
## Create all relevant Citrix Cloud Entities
### Set local variables and scripts
locals {}

### Get existing entity data
#### Retrieving the Machine Catalog
data "citrix_machine_catalog" "MCS" {
  name                = var.CC-Azure-MC-Name
}

#### Create the Delivery Group based on the Machine Catalog
resource "citrix_delivery_group" "TACG-TMM-DG" {
  depends_on = [citrix_machine_catalog.MCS]
  name                     = var.TACG-TMM-DG-Name
  description              = var.TACG-TMM-DG-Description
  minimum_functional_level = var.TACG-TMM-DG-FunctionalLevel

  associated_machine_catalogs = [
    {
      machine_catalog = data.citrix_machine_catalog.MCS.id
      machine_count   = var.TACG-TMM-MC-VM-NumberOfVMs
    }
  ]

  desktops = [
    {
      published_name = var.TACG-TMM-DG-Desktops-Name
      description    = var.TACG-TMM-DG-Desktops-Description

      restricted_access_users = {
        allow_list = var.TACG-TMM-DG-Desktops-AllowList
      }
      enabled = var.TACG-TMM-DG-Desktops-Enabled

      ###### Not needed due to Static assignment type-MC
      # enable_session_roaming = var.TACG-TMM-DG-Desktops-SessionRoaming
    }

  ]
  autoscale_settings = {
    autoscale_enabled                   = var.TACG-TMM-DG-AS-Enabled
    peak_disconnect_timeout_minutes     = var.TACG-TMM--DG-AS-PeakDisconnectTime
    off_peak_disconnect_timeout_minutes = var.TACG-TMM-DG-AS-OffPeakDisconnectTime
    peak_disconnect_action              = var.TACG-TMM-DG-AS-PeakDisconnectAction
    off_peak_disconnect_action          = var.TACG-TMM-DG-AS-OffPeakDisconnectAction
    off_peak_log_off_timeout_minutes    = var.TACG-TMM-DG-AS-PeakLogoffTime
    peak_log_off_timeout_minutes        = var.TACG-TMM-DG-AS-OffPeakLogoffTime
    peak_log_off_action                 = var.TACG-TMM-DG-AS-PeakDisconnectAction
    off_peak_log_off_action             = var.TACG-TMM-DG-AS-OffPeakDisconnectAction

    ###### Not needed due to Static assignment type-MC
    #log_off_off_peak_disconnected_session_after_seconds = var.TACG-TMM-DG-AS-PeakLogoffTime
    #log_off_peak_disconnected_session_after_seconds     = var.TACG-TMM-DG-AS-OffPeakLogoffTime
    #peak_log_off_action                                 = var.TACG-TMM-DG-AS-PeakDisconnectAction
    #off_peak_log_off_action                             = var.TACG-TMM-DG-AS-OffPeakDisconnectAction
    #log_off_reminder_enabled                            = var.TACG-TMM-DG-AS-Enabled
    #log_off_reminder_message                            = var.TACG-TMM-DG-RS-Notification-MessageBody
    #log_off_reminder_title                              = var.TACG-TMM-DG-AS-Notification-MessageTitle
    #log_off_warning_message                             = var.TACG-TMM-DG-RS-Notification-MessageBody
    #log_off_warning_title                               = var.TACG-TMM-DG-AS-Notification-MessageTitle
    timezone = var.TACG-TMM-DG-AS-TimeZone

    power_time_schemes = [
      {
        days_of_week          = var.TACG-TMM-DG-AS-Days
        display_name          = var.TACG-TMM-DG-AS-Name
        peak_time_ranges      = var.TACG-TMM-DG-AS-PeakTime
        pool_using_percentage = false
      },
    ]
  }
  restricted_access_users = {
    allow_list = var.TACG-TMM-DG-Desktops-AllowList
  }
  reboot_schedules = [
    {
      name                    = var.TACG-TMM-DG-RS-Name
      reboot_schedule_enabled = var.TACG-TMM-DG-RS-Enabled
      frequency               = var.TACG-TMM-DG-RS-Frequency
      frequency_factor        = 1
      days_in_week            = var.TACG-TMM-DG-RS-RebootDays
      start_time              = var.TACG-TMM-DG-RS-RebootStartTime
      start_date              = var.TACG-TMM-DG-RS-RebootStartDate
      reboot_duration_minutes = var.TACG-TMM-DG-RS-RebootDuration
      ignore_maintenance_mode = var.TACG-TMM-DG-RS-IgnoreMaintenanceMode
      natural_reboot_schedule = var.TACG-TMM-DG-RS-NormalRebootSchedule

      ###### Not needed due to SingleSession type-MC
      #reboot_notification_to_users = {
      #notification_duration_minutes       = var.TACG-TMM-DG-RS-Notification-Duration
      #notification_message                = var.TACG-TMM-DG-RS-Notification-MessageBody
      #notification_title                  = var.TACG-TMM-DG-RS-Notification-MessageTitle
      #notification_repeat_every_5_minutes = var.TACG-TMM-DG-RS-Notification-MessageRepeat
      #} 
    }
  ]
}Running this snippet creates the Delivery Group based on the Machine Catalog. XxXxXxXxX@devops-automation:/etc/terraform-tmm/DeployDG$ terraform apply
data.citrix_machine_catalog.MCS: Reading...
data.citrix_machine_catalog.MCS: Read complete after 1s [id=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX]
...

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" "TACG-TMM-DG" {
      + associated_machine_catalogs = [
          + {
              + machine_catalog = "a283XXXX-XXXX-XXXX-XXXX-XXXXXXXX424e"
              + machine_count   = 10
            },
        ]
      + autoscale_settings          = {
         ...

Plan: 1 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_delivery_group.TACG-TMM-DG: Creating...
citrix_delivery_group.TACG-TMM-DG: Still creating... [00m10s elapsed]
citrix_delivery_group.TACG-TMM-DG: Still creating... [00m20s elapsed]
citrix_delivery_group.TACG-TMM-DG: Still creating... [00m30s elapsed]
citrix_delivery_group.TACG-TMM-DG: Creation complete after 39s [id=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

XxXxXxXxX@devops-automation:/etc/terraform-tmm/DeployDG$The Delivery Group was successfully created. Important To change the number of machines in the Delivery Group, update the variable that sets the number: machine_count = var.TACG-TMM-MC-VM-NumberOfVMs You can find more information about daily administrative tasks such as changes of the master image in the third part of the Citrix Automation Handbook.  Tip We recommend running health checks during the first few runs to ensure the snippets and their adjacent entities are working as intended before proceeding to the next modules.  We can now proceed to the next step.  Part 9: Creating Policy Sets, Scopes, and RolesThe next step is to create a policy set, scopes, and roles to allow more granular configuration of your DaaS environment. Important Please consult the product documentation at https://docs.citrix.com/en-us/citrix-virtual-apps-desktops/policies/reference.html or the Terraform provider documentation at https://github.com/citrix/terraform-provider-citrix/blob/main/internal/daas/policies/policy_set_resource.md#available-policy-settings for more information about the various policy settings. For more information about policy filters, consult the Terraform provider documentation at https://github.com/citrix/terraform-provider-citrix/blob/main/internal/daas/policies/policy_set_resource.md#available-policy-filters.  Caution Please ensure your environment supports Policy Sets before creating them with Terraform.  Example: Terraform snippet to create scopes, roles, and a policy set: # Create a Citrix Cloud Resource Location and DaaS Deployment
# Terraform deployment of Scopes, Roles, and Policies
## Creating all Citrix Cloud-related entities
### Retrieve default Monitor Scope id for later use
data "citrix_admin_scope" "GetDefaultMonitorScope" {
    name = "All"
}

### Retrieve Delivery Group ID for later use
data "citrix_delivery_group" "TACG-TMM-DG" {
    name = var.CC-Azure-DG-Name
}

### Creating Monitor examples
#### Create an Monitor Scope example
resource "citrix_admin_scope" "CreateMonitorScopeExample" {
    name        = var.CreateMonitorScopeExample-Name
    description = var.CreateMonitorScopeExample-Description
}

#### Create an Monitor Role example
resource "citrix_admin_role" "CreateMonitorRoleExample" {
    name        = var.CreateMonitorRoleExample-Name
    description = var.CreateMonitorRoleExample-Description
    can_launch_manage = false
    can_launch_monitor = true
    permissions = [
        "Applications_Read",
        "Catalog_Read",
        "DesktopGroup_Read",
        "Director_Alerts_Read",
        "Director_ClientDetails_Read",
        "Director_ClientHelpDesk_Read",
        "Director_Dashboard_Read",
        "Director_HelpDesk_Read",
        "Director_MachineDetails_Read",
        "Director_Trends_Read",
        "Director_UserDetails_Read",
        "Hosts_Read",
        "Logging_Read",
        "Policies_Read",
        "Setting_Read",
        "Zone_Read"
    ]
}

#### Create a default Policy Set
resource "citrix_policy_set_v2" "TACG-TMM-DefaultPolicySet" {
  depends_on      = [data.citrix_delivery_group.TACG-TMM-DG]
  name            = var.TACG-TMM-PS-Name
  description     = var.TACG-TMM-PS-Description
  delivery_groups = [data.citrix_delivery_group.TACG-TMM-DG.id]
}

#### Create a Policy in the Policy Set
resource "citrix_policy" "TACG-TMM-DefaultPolicySet-Printing" {
  depends_on    = [citrix_policy_set_v2.TACG-TMM-DefaultPolicySet]
  policy_set_id = citrix_policy_set_v2.TACG-TMM-DefaultPolicySet.id
  name          = "Printing"
  description   = "Example of Printer-related policy in default Policy Set"
  enabled       = true
}

#### Create a Policy in the Policy Set
resource "citrix_policy" "TACG-TMM-DefaultPolicySet-HDX" {
  depends_on    = [citrix_policy_set_v2.TACG-TMM-DefaultPolicySet]
  policy_set_id = citrix_policy_set_v2.TACG-TMM-DefaultPolicySet.id
  name          = "HDX Graphics"
  description   = "Example of HDX Graphics-related policy in default Policy Set"
  enabled       = true
}

#### Create a Policy in the Policy Set
resource "citrix_policy" "TACG-TMM-DefaultPolicySet-ClientDrives" {
  depends_on    = [citrix_policy_set_v2.TACG-TMM-DefaultPolicySet]
  policy_set_id = citrix_policy_set_v2.TACG-TMM-DefaultPolicySet.id
  name          = "Client Drives"
  description   = "Example of Client Drive-related policy in default Policy Set"
  enabled       = true
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-DefaultPolicySet-Printing1" {
  policy_id   = citrix_policy.TACG-TMM-DefaultPolicySet-Printing.id
  name        = "ClientPrinterAutoCreation"
  value       = "DefaultPrinterOnly"
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-DefaultPolicySet-Printing2" {
  policy_id   = citrix_policy.TACG-TMM-DefaultPolicySet-Printing.id
  name        = "UniversalPrintDriverUsage"
  value       = "FallbackToSpecific"
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-DefaultPolicySet-HDX1" {
  policy_id   = citrix_policy.TACG-TMM-DefaultPolicySet-HDX.id
  name        = "AllowVisuallyLosslessCompression"
  enabled     = true
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-DefaultPolicySet-HDX2" {
  policy_id   = citrix_policy.TACG-TMM-DefaultPolicySet-HDX.id
  name        = "UseVideoCodecForCompression"
  value       = "UseVideoCodecIfPreferred"
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-DefaultPolicySet-HDX3" {
  policy_id   = citrix_policy.TACG-TMM-DefaultPolicySet-HDX.id
  name        = "UseHardwareEncodingForVideoCodec"
  enabled     = true
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-DefaultPolicySet-CD1" {
  policy_id   = citrix_policy.TACG-TMM-DefaultPolicySet-ClientDrives.id
  name        = "AutoConnectDrives"
  enabled     = true
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-DefaultPolicySet-CD2" {
  policy_id   = citrix_policy.TACG-TMM-DefaultPolicySet-ClientDrives.id
  name        = "ClientDriveRedirection"
  enabled     = true
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-DefaultPolicySet-CD3" {
  policy_id   = citrix_policy.TACG-TMM-DefaultPolicySet-ClientDrives.id
  name        = "ClientFixedDrives"
  enabled     = true
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-DefaultPolicySet-CD4" {
  policy_id   = citrix_policy.TACG-TMM-DefaultPolicySet-ClientDrives.id
  name        = "ClientFloppyDrives"
  enabled     = false
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-DefaultPolicySet-CD5" {
  policy_id   = citrix_policy.TACG-TMM-DefaultPolicySet-ClientDrives.id
  name        = "ClientOpticalDrives"
  enabled     = false
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-DefaultPolicySet-CD6" {
  policy_id   = citrix_policy.TACG-TMM-DefaultPolicySet-ClientDrives.id
  name        = "ClientNetworkDrives"
  enabled     = false
  use_default = false
}

#### Create the corresponding Policy Filters
resource "citrix_delivery_group_policy_filter" "TACG-TMM-DefaultPolicySet-DGFilter-1" {
  depends_on        = [citrix_policy.TACG-TMM-DefaultPolicySet-Printing]
  policy_id         = citrix_policy.TACG-TMM-DefaultPolicySet-Printing.id
  enabled           = true
  allowed           = true
  delivery_group_id = data.citrix_delivery_group.TACG-TMM-DG.id
}

#### Create the corresponding Policy Filters
resource "citrix_delivery_group_policy_filter" "TACG-TMM-DefaultPolicySet-DGFilter-2" {
  depends_on        = [citrix_policy.TACG-TMM-DefaultPolicySet-HDX]
  policy_id         = citrix_policy.TACG-TMM-DefaultPolicySet-HDX.id
  enabled           = true
  allowed           = true
  delivery_group_id = data.citrix_delivery_group.TACG-TMM-DG.id
}

#### Create the corresponding Policy Filters
resource "citrix_delivery_group_policy_filter" "TACG-TMM-DefaultPolicySet-DGFilter-3" {
  depends_on        = [citrix_policy.TACG-TMM-DefaultPolicySet-ClientDrives]
  policy_id         = citrix_policy.TACG-TMM-DefaultPolicySet-ClientDrives.id
  enabled           = true
  allowed           = true
  delivery_group_id = data.citrix_delivery_group.TACG-TMM-DG.id
}

#### Create the corresponding Policy Filters
resource "citrix_user_policy_filter" "TACG-TMM-DefaultPolicySet-SIDFilter-1" {
  depends_on = [citrix_policy.TACG-TMM-DefaultPolicySet-Printing]
  policy_id  = citrix_policy.TACG-TMM-DefaultPolicySet-Printing.id
  enabled    = true
  allowed    = true
  sid        = var.TACG-TMM-PS-UserSID
}

#### Create the corresponding Policy Filters
resource "citrix_user_policy_filter" "TACG-TMM-DefaultPolicySet-SIDFilter-2" {
  depends_on = [citrix_policy.TACG-TMM-DefaultPolicySet-HDX]
  policy_id  = citrix_policy.TACG-TMM-DefaultPolicySet-HDX.id
  enabled    = true
  allowed    = true
  sid        = var.TACG-TMM-PS-UserSID
}

#### Create the corresponding Policy Filters
resource "citrix_user_policy_filter" "TACG-TMM-DefaultPolicySet-SIDFilter-3" {
  depends_on = [citrix_policy.TACG-TMM-DefaultPolicySet-ClientDrives]
  policy_id  = citrix_policy.TACG-TMM-DefaultPolicySet-ClientDrives.id
  enabled    = true
  allowed    = true
  sid        = var.TACG-TMM-PS-UserSID
}

#### Create the corresponding Policy Filters
resource "citrix_client_ip_policy_filter" "TACG-TMM-DefaultPolicySet-ClientIPFilter-1" {
  depends_on = [citrix_policy.TACG-TMM-DefaultPolicySet-Printing]
  policy_id  = citrix_policy.TACG-TMM-DefaultPolicySet-Printing.id
  enabled    = true
  allowed    = true
  ip_address = var.TACG-TMM-PS-IPRange
}

#### Create the corresponding Policy Filters
resource "citrix_client_ip_policy_filter" "TACG-TMM-DefaultPolicySet-ClientIPFilter-2" {
  depends_on = [citrix_policy.TACG-TMM-DefaultPolicySet-HDX]
  policy_id  = citrix_policy.TACG-TMM-DefaultPolicySet-HDX.id
  enabled    = true
  allowed    = true
  ip_address = var.TACG-TMM-PS-IPRange
}

#### Create the corresponding Policy Filters
resource "citrix_client_ip_policy_filter" "TACG-TMM-DefaultPolicySet-ClientIPFilter-3" {
  depends_on = [citrix_policy.TACG-TMM-DefaultPolicySet-ClientDrives]
  policy_id  = citrix_policy.TACG-TMM-DefaultPolicySet-ClientDrives.id
  enabled    = true
  allowed    = true
  ip_address = var.TACG-TMM-PS-IPRange
}

resource "null_resource" "WriteProgress5" {
  depends_on = [citrix_policy_setting.TACG-TMM-DefaultPolicySet-CD1, citrix_policy_setting.TACG-TMM-DefaultPolicySet-CD2, citrix_policy_setting.TACG-TMM-DefaultPolicySet-CD3, citrix_policy_setting.TACG-TMM-DefaultPolicySet-CD4, citrix_policy_setting.TACG-TMM-DefaultPolicySet-CD5, citrix_policy_setting.TACG-TMM-DefaultPolicySet-CD6, citrix_policy_setting.TACG-TMM-DefaultPolicySet-HDX1, citrix_policy_setting.TACG-TMM-DefaultPolicySet-HDX2, citrix_policy_setting.TACG-TMM-DefaultPolicySet-HDX3, citrix_policy_setting.TACG-TMM-DefaultPolicySet-Printing1, citrix_policy_setting.TACG-TMM-DefaultPolicySet-Printing2]

  provisioner "local-exec" {
    command = "echo The default Policy Set and its policies and filters were successfully created..."
  }
}Running the Terraform snippet creates all entities and assigns the Policy Set to the Delivery Group. The output is omitted due to its length.  Part 10: Removing Citrix DaaS entities using TerraformWe have discussed mainly aspects of the CREATION of entities using Terraform. Finally, it is also most important to have an instrument to REMOVE entities created by Terraform. In Infrastructure‑as‑Code (IaC) environments, terraform destroy plays a critical role in ensuring clean deprovisioning, operational discipline, and compliance with governance.  By design, Terraform manages infrastructure declaratively—meaning everything it creates is recorded in its state. Using terraform destroy ensures that all Terraform‑managed resources are removed consistently, predictably, and traceably, eliminating the risk of leaving unmanaged or “orphaned” cloud assets behind, and supports security hygiene by ensuring no unused compute, networking, or identity components remain. It also reduces cost leakage. Terraform logs, state history, and pipeline execution metadata provide a verifiable chain of custody for resource lifecycle events. Using terraform destroy creates explicit evidence showing: What was removed, When it was removed, and Who initiated the removal This is essential for audit frameworks such as ISO 27001, SOC 2, and internal IT controls. Example: Removing a Delivery Group You can simply remove the Delivery Group by uncommenting the respective code in your snippet - place the uncomment markers /* &lt;------ place the marker here
#### Create the Delivery Group based on the Machine Catalog
resource "citrix_delivery_group" "TACG-TMM-DG" {
  depends_on = [citrix_machine_catalog.MCS]
  name                     = var.TACG-TMM-DG-Name
  description              = var.TACG-TMM-DG-Description
  minimum_functional_level = var.TACG-TMM-DG-FunctionalLevel

  associated_machine_catalogs = [
    {
      machine_catalog = data.citrix_machine_catalog.MCS.id
      machine_count   = var.TACG-TMM-MC-VM-NumberOfVMs
    }
  ]

  desktops = [
    {
      published_name = var.TACG-TMM-DG-Desktops-Name
      description    = var.TACG-TMM-DG-Desktops-Description

      restricted_access_users = {
        allow_list = var.TACG-TMM-DG-Desktops-AllowList
      }
      enabled = var.TACG-TMM-DG-Desktops-Enabled

      ###### Not needed due to Static assignment type-MC
      # enable_session_roaming = var.TACG-TMM-DG-Desktops-SessionRoaming
    }

  ]
  autoscale_settings = {
    autoscale_enabled                   = var.TACG-TMM-DG-AS-Enabled
    peak_disconnect_timeout_minutes     = var.TACG-TMM--DG-AS-PeakDisconnectTime
    off_peak_disconnect_timeout_minutes = var.TACG-TMM-DG-AS-OffPeakDisconnectTime
    peak_disconnect_action              = var.TACG-TMM-DG-AS-PeakDisconnectAction
    off_peak_disconnect_action          = var.TACG-TMM-DG-AS-OffPeakDisconnectAction
    off_peak_log_off_timeout_minutes    = var.TACG-TMM-DG-AS-PeakLogoffTime
    peak_log_off_timeout_minutes        = var.TACG-TMM-DG-AS-OffPeakLogoffTime
    peak_log_off_action                 = var.TACG-TMM-DG-AS-PeakDisconnectAction
    off_peak_log_off_action             = var.TACG-TMM-DG-AS-OffPeakDisconnectAction

    ###### Not needed due to Static assignment type-MC
    #log_off_off_peak_disconnected_session_after_seconds = var.TACG-TMM-DG-AS-PeakLogoffTime
    #log_off_peak_disconnected_session_after_seconds     = var.TACG-TMM-DG-AS-OffPeakLogoffTime
    #peak_log_off_action                                 = var.TACG-TMM-DG-AS-PeakDisconnectAction
    #off_peak_log_off_action                             = var.TACG-TMM-DG-AS-OffPeakDisconnectAction
    #log_off_reminder_enabled                            = var.TACG-TMM-DG-AS-Enabled
    #log_off_reminder_message                            = var.TACG-TMM-DG-RS-Notification-MessageBody
    #log_off_reminder_title                              = var.TACG-TMM-DG-AS-Notification-MessageTitle
    #log_off_warning_message                             = var.TACG-TMM-DG-RS-Notification-MessageBody
    #log_off_warning_title                               = var.TACG-TMM-DG-AS-Notification-MessageTitle
    timezone = var.TACG-TMM-DG-AS-TimeZone

    power_time_schemes = [
      {
        days_of_week          = var.TACG-TMM-DG-AS-Days
        display_name          = var.TACG-TMM-DG-AS-Name
        peak_time_ranges      = var.TACG-TMM-DG-AS-PeakTime
        pool_using_percentage = false
      },
    ]
  }
  restricted_access_users = {
    allow_list = var.TACG-TMM-DG-Desktops-AllowList
  }
  reboot_schedules = [
    {
      name                    = var.TACG-TMM-DG-RS-Name
      reboot_schedule_enabled = var.TACG-TMM-DG-RS-Enabled
      frequency               = var.TACG-TMM-DG-RS-Frequency
      frequency_factor        = 1
      days_in_week            = var.TACG-TMM-DG-RS-RebootDays
      start_time              = var.TACG-TMM-DG-RS-RebootStartTime
      start_date              = var.TACG-TMM-DG-RS-RebootStartDate
      reboot_duration_minutes = var.TACG-TMM-DG-RS-RebootDuration
      ignore_maintenance_mode = var.TACG-TMM-DG-RS-IgnoreMaintenanceMode
      natural_reboot_schedule = var.TACG-TMM-DG-RS-NormalRebootSchedule

      ###### Not needed due to SingleSession type-MC
      #reboot_notification_to_users = {
      #notification_duration_minutes       = var.TACG-TMM-DG-RS-Notification-Duration
      #notification_message                = var.TACG-TMM-DG-RS-Notification-MessageBody
      #notification_title                  = var.TACG-TMM-DG-RS-Notification-MessageTitle
      #notification_repeat_every_5_minutes = var.TACG-TMM-DG-RS-Notification-MessageRepeat
      #} 
    }
  ]
}
*/ &lt;------ place the marker hereRunning the snippet would remove the Delivery Group: XxXxXxXxX@devops-automation:/etc/terraform-tmm/DeployDG$ terraform apply
data.citrix_machine_catalog.MCS: Reading...
data.citrix_machine_catalog.MCS: Read complete after 1s [id=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX]
...

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  - destroy

Terraform will perform the following actions:

  # citrix_delivery_group.CreateDG will be destroyed
  - resource "citrix_delivery_group" "TACG-TMM-DG" {
      - associated_machine_catalogs = [
          - {
              - machine_catalog = "a283XXXX-XXXX-XXXX-XXXX-XXXXXXXX424e" -&gt; null
              - machine_count   = 10 -&gt; null
            },
        ]
      + autoscale_settings          = {
         ...

Plan: 0 to add, 0 to change, 1 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_delivery_group.TACG-TMM-DG: Destroying...
citrix_delivery_group.TACG-TMM-DG: Destruction complete after 39s [id=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX]

Apply complete! Resources: 0 added, 0 changed, 1 destroyed.

XxXxXxXxX@devops-automation:/etc/terraform-tmm/DeployDG$Terraform has decommissioned the Delivery Group and its adjacent entities. That completes our example of using Infrastructure-as-Code for creating a Citrix DaaS Deployment on Azure.  SummaryUsing Terraform can significantly streamline your workflow and improve efficiency: Automation: IaC automates repetitive tasks, such as provisioning and managing infrastructure. This reduces the need for manual intervention and minimizes the risk of human error. Consistency: Defining IaC ensures that your environments are consistent and reproducible. This is particularly useful for maintaining multiple environments, such as development, staging, and production. Scalability: IaC makes it easy to scale your infrastructure up or down based on demand. You can define your desired state, and Terraform will handle the necessary changes to achieve that state. Collaboration: IaC configurations can be stored in version control systems like Git, enabling team collaboration. Team members can review, comment on, and approve changes before they are applied. Visibility: IaC provides a clear and auditable record of changes to your infrastructure. This makes it easier to track changes, troubleshoot issues, and ensure compliance with organizational policies. Integration: IaCintegrates with various tools and services, such as CI/CD pipelines, monitoring systems, and configuration management tools. This allows you to create a seamless, automated workflow for managing your infrastructure. Cost Management: By automating resource provisioning and de-provisioning, Terraform helps you optimize resource usage and control costs. You can easily identify and eliminate unused or underutilized resources.  That completes Part 5 of the Citrix Automation Handbook. In the first part of the Citrix Automation Handbook, we discuss the Citrix Automation story and the basics of Infrastructure-as-Code, of Continuous Integration/Continuous Deployment (CI/CD) strategies, and our main IaC tools: Packer, Terraform, and Ansible. In the second part of the Citrix Automation Handbook, we focus on: Applying automation and infrastructure-as-code in the Citrix universe. We discuss all relevant components and provide code examples from real environments Best practises for designing an environment for automation and IaC use, and discuss deployment tips In the third part of the Citrix Automation Handbook, we discuss common and special use cases, as well as complete deployment examples from the field. In the fourth part of the Citrix Automation Handbook, we cover creating a Citrix Virtual Apps and Desktops 2507 LTSR deployment on XenServer 8.4 In this part, we focus on: Creating a Citrix DaaS deployment on Azure. In the sixth part of the Citrix Automation Handbook, we cover some final topics.  DisclaimerMost important All code snippets mentioned in this handbook were thoroughly tested and run in a sandbox environment. All shown Packer, Terraform, Ansible Playbooks, and PowerShell scripts were tailored exclusively for the environment used for this handbook. You need to adjust all settings, values, snippets, etc., in the most suitable way tailored to your requirements, regulations, and existing environments. All settings, scripts, and code examples listed in this document are intended only to illustrate the possibilities and serve as a basis for your own deployment. We tried to show different ways, even if they were not always in line with best-practice guidelines, such as unencrypted WinRM communication for demonstration purposes. Using all the provided code snippets is at your own risk – please read the disclaimer below for further information.   EXCEPT WHERE EXPRESSLY PROVIDED OTHERWISE BY CLOUD SOFTWARE GROUP, THE CONTENT IN THIS DOCUMENT IS PROVIDED “AS IS” AND CLOUD SOFTWARE GROUP HEREBY DISCLAIMS ALL EXPRESS OR IMPLIED REPRESENTATIONS, WARRANTIES, GUARANTIES, AND CONDITIONS, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES OR CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD TO BE LEGALLY INVALID. CLOUD SOFTWARE GROUP MAKES NO REPRESENTATIONS, WARRANTIES, GUARANTIES, OR CONDITIONS AS TO THE QUALITY, SUITABILITY, TRUTH, ACCURACY, OR COMPLETENESS OF ANY OF THE CONTENT CONTAINED ON THE WEBSITE. The above disclaimer applies to any damages, liability, or injuries caused by any failure of performance, error, omission, interruption, deletion, defect, delay in operation or transmission, computer virus, communication line failure, theft or destruction of or unauthorized access to, alteration of, or use, whether for breach of contract, tort, negligence or any other cause of action.]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2025_03/cc-sp-1.png.0c18e625d097901a997458b4eff1afd6.png" length="155589" type="image/png"/><pubDate>Wed, 21 Jan 2026 09:13:42 +0000</pubDate></item><item><title>The Citrix Automation Handbook 2601 - Part 4</title><link>https://community.stage.citrix.com/tech-zone/automation/automation-handbook-2601-part4/</link><description><![CDATA[The Citrix Automation Handbook - Part 4In the first part of the Citrix Automation Handbook, we discuss the Citrix Automation story and the basics of Infrastructure-as-Code, of Continuous Integration/Continuous Deployment (CI/CD) strategies, and our main IaC tools: Packer, Terraform, and Ansible. In the second part of the Citrix Automation Handbook, we focus on: Applying automation and infrastructure-as-code in the Citrix universe. We discuss all relevant components and provide code examples from real environments Best practises for designing an environment for automation and IaC use, and discuss deployment tips In the third part of the Citrix Automation Handbook, we discuss common and special use cases, as well as complete deployment examples from the field. In this part, we focus on: Creating a Citrix Virtual Apps and Desktops 2507 LTSR deployment on XenServer 8.4. In the fifth part of the Citrix Automation Handbook, we cover creating a Citrix DaaS deployment on Azure. In the sixth part of the Citrix Automation Handbook, we cover some final topics.  Part 4 Creating a Citrix Virtual Apps and Desktops 2507 LTSR Deployment on XenServer 8.4In this example, we cover the usage of three distinct Infrastructure-as-Code (IaC) frameworks to deploy a Citrix Virtual Desktops™ 2507 LTSR-based deployment on a XenServer® 8.4 cluster. The instructions are divided into blocks that must be started in a specific, predetermined order. However, it is not necessary to start all blocks in one go—the dependencies on previous blocks are recorded at runtime, so essential time intervals or even execution pauses between the individual blocks are not a problem. All these blocks can, of course, be implemented in a CI/CD deployment – we will cover that aspect in a distinct example later in this handbook. Components and Frameworks usedLet´s have a short overview of all the components and frameworks used. XenServer 8.4For customers who want to virtualize Citrix workloads, XenServer delivers an affordable, straightforward hypervisor optimized for Citrix Virtual Apps™ and Desktops™ deployments. XenServer 8.4 is the most optimized hypervisor for running Citrix workloads, with features only available with Citrix and XenServer – relevant for this deployment guide are: IntelliCache™ MCS Read Cache You can find more information about XenServer 8 in its respective product documentation. Hashicorp TerraformTerraform is one of the most widely adopted Infrastructure-as-Code (IaC) platforms for provisioning and managing cloud and on-prem infrastructure at scale. Its declarative configuration model, strong ecosystem support, and provider-agnostic architecture make it a foundational tool for organizations seeking consistency, compliance, and operational maturity in their infrastructure lifecycle. Terraform relies on declarative code to define infrastructure in a predictable, reproducible manner. By representing compute, networking, storage, IAM, and platform services as version-controlled code artifacts, Terraform: Hashicorp PackerHashiCorp Packer is a foundational tool for organizations seeking to standardize, automate, and scale the creation of master images across multi-cloud and hybrid environments. As organizations increasingly adopt Infrastructure-as-Code (IaC) approaches, Packer provides a consistent, version-controlled, and repeatable way to build golden images aligned with modern DevOps and platform engineering practices. RedHat AnsibleMany organizations that have matured their Infrastructure-as-Code (IaC) practices increasingly require tooling that not only provisions infrastructure but also configures systems, applications, and operating environments in a controlled, repeatable manner. Ansible is a leading configuration management and automation platform that aligns naturally with IaC principles and complements provisioning tools such as Terraform, or cloud-native IaC engines. Its agentless architecture, declarative playbook model, and extensive ecosystem make it a strategic choice for enterprises seeking predictable, scalable, and compliant post-provision configuration. Ansible provides a declarative, idempotent configuration model that ensures systems converge to a desired state regardless of their current configuration. This makes it ideally suited for environments provisioned using IaC tools, Note You can find more information about the three frameworks and their importance in the first part of the Citrix Automation Handbook. You can find more information about XenServer 8 and its importance in an IaC deployment in the third part of the Citrix Automation Handbook.  Better togetherTerraform and Ansible serve complementary roles in modern Infrastructure-as-Code (IaC) architectures. Terraform excels at provisioning infrastructure resources—compute, networks, storage, identity, and cloud services—while Ansible specializes in configuring and managing the software, applications, and operating system state on those resources once they are created. When combined, they form a robust, scalable, and fully automated delivery pipeline that allows organizations to provision secure, consistent environments and configure them in a modular, policy-driven manner. Terraform is primarily designed for infrastructure lifecycle management, while Ansible is engineered for post-provisioning configuration. This clean separation ensures: Terraform builds and destroys infrastructure predictably Ansible configures servers, middleware, application stacks, and OS settings Changes to infrastructure and configuration are maintained in distinct codebases, improving clarity and reducing risk IT can version, audit, and test both layers independently This division of responsibilities aligns with architectural patterns used in platform engineering and regulated environments, where infrastructure and system configuration often have separate compliance requirements.  All needed Automation components were installed on an Ubuntu-based VM cluster.  If all IaC frameworks reside on the same machine/cluster, communication between them is less error-prone. Caution Due to current limitations in Citrix StoreFront´s automation capabilities, you must run the automated deployment and configuration of Citrix StoreFront™ from a domain-joined Windows machine with Terraform installed.  Communication Flow between Packer, Terraform, Ansible, and the Target MachinesAll communication between Terraform, Packer, and Ansible on the IaC-VM and the target VMs on the XenServer cluster uses SSH and WinRM. WinRM (Windows Remote Management) is a Microsoft protocol that enables remote management of Windows systems by allowing access to remote computers to perform management tasks. It is Microsoft's implementation of the WS-Management (Web Services Management Protocol) standard. It often uses SOAP (Simple Object Access Protocol) for communication over HTTP or HTTPS, using ports 5985 (HTTP) and 5986 (HTTPS) by default. WinRM provides the basis for remote management with PowerShell and is used for tasks such as running remote scripts, automating, configuring, and performing system inventory. Therefore, WinRM and SSH must be configured correctly. Important We emphasize ensuring the communication flow works as intended from the outset of the pre-domain-joined stage; otherwise, Terraform and Ansible will fail to configure the VMs. During the pre-domain-join stage, you can configure these settings using the local Group Policy Editor on the VM or in the autounattend.xml file as we do in our Packer scripts. After a successful domain join, you can set all relevant settings using GPOs. To provide maximum flexibility and a fallback mechanism, you can use both listeners - HTTP and HTTPS - but we recommend using the secure transport whenever and wherever possible. Note You can find more information about the communication flow, its configuration, and its importance in the first part of the Citrix Automation Handbook.  Creation of the CVAD Environment using IaCAs already stated, the workflow is divided into blocks that must be started in a specific, predetermined order: Caution As already mentioned above, Module 9 - Using Terraform to create the StoreFront deployment and all its necessities, must be run on a Windows-based, domain-joined VM to be supported. This is due to limitations in StoreFront´s automation capabilities.  Using Packer to create the Master Images In this IaC block, we use Packer to create the Master Images for the VDI machines and the Windows Servers.  Using Terraform to create the needed VMs based on the Master Images Terraform will create these VMs: a.       Two Windows Server 2025-based VMs for the Delivery Controllers b.       One Windows Server 2025-based VM for the StoreFront™ server c.       One Windows Server 2025-based VM for the WebStudio™ server d.       One Windows 11-based VM as a VDI Worker  Using Terraform and Ansible to configure the VMs Terraform and Ansible take further configuration steps: a.       Change the IP addresses to known IPs b.       Change the Ansible configuration file to reflect the IP changes c.       Put the VMs into the AD domain  Using Terraform and Ansible to deploy the Delivery Controllers Terraform and Ansible take further configuration steps: a.       Mount the CVAD ISO installer b.       Copy the publicly-signed SSL certificate to the DDCs c.       Install all required server roles and features before installing the DDCs d.       Change some necessary registry settings  Using Terraform and Ansible to deploy the StoreFront server Terraform and Ansible take further configuration steps: a.       Mount the CVAD ISO installer b.       Copy the publicly-signed SSL certificate to the StoreFront server c.       Install all required server roles and features before installing the StoreFront d.       Change some necessary registry settings  Using Terraform and Ansible to deploy the WebStudio server Terraform and Ansible take further configuration steps: a.       Mount the CVAD ISO installer b.       Copy the publicly-signed SSL certificate to the WebStudio server c.       Install all required server roles and features before installing the WebStudio server d.       Change some necessary registry settings e.       Configure WebStudio server as proxy for the DDCs  Using Terraform and Ansible to change the SSL Bindings Terraform and Ansible take further configuration steps: a.       Change the SSL bindings on all servers and listeners to achieve complete HTTPS communication  Using Terraform and Ansible to create the CVAD site and all its necessities Terraform and Ansible take further configuration steps: a.       Create all databases b.       Create the site c.       Add the second DDC to the site d.       Configure additional site configurations e.       Configure a dedicated Administrators group f.        Configure the licensing  Using Terraform to create the StoreFront deployment and all its necessities Terraform takes further configuration steps: a.       Create the StoreFront deployment b.       Create a StoreFront Authentication service c.       Create a StoreFront Store service d.       Create a StoreFront WebReceiver service  Using Terraform to create all CVAD entities and all their necessities Terraform takes further configuration steps: a.       Create a StoreFront Server object b.       Create a Hypervisor connection c.       Create a Hypervisor connection resource pool d.       Create a Machine Catalog e.       Create a Delivery Group f.        Create a Policy Set and its policies  Using Terraform to remove CVAD entities  Note All Ansible playbooks are directly called from Terraform. Terraform stops the workflow if an error occurs by checking Ansible's return code.  Important All shown Terraform codes, Ansible Playbooks, and PowerShell scripts are tailored exclusively for the specific environment used for this guide. You need to adjust all settings, values, snippets, etc., in the most suitable way tailored to your requirements, regulations, and existing environments. All settings, scripts, and code examples listed in this document are intended only to illustrate the possibilities and serve as a basis for your own deployment. Using all the provided code snippets is at your own risk – please read the disclaimer at the end of the document for further information. Terraform verifies the successful execution of each Ansible playbook. If an error occurs, Terraform will halt the deployment and write out the failed Playbook Example snippet: ##### Create Databases Start
##### Connect to Ansible Interpreter and run CreateDatabase-Playbook on DDCs
resource "null_resource" "RunCreateDatabasePlaybook" {
  depends_on = [time_sleep.wait_10_seconds]

  provisioner "local-exec" {
    working_dir = "/etc/ansible"
    command     = var.TACG-TMM-XS-CL-Ansible-CreateDatabase-CMD
  }
}

##### Check Ansible Return Code
data "external" "CheckAnsibleReturnCodeCDB" {
  depends_on = [null_resource.RunCreateDatabasePlaybook]
  program    = ["bash", "/etc/ansible/assets/CheckAnsibleReturnCode.sh", "/etc/ansible/assets/ansible_exit_code.txt", "0"]
}

##### Terminate if Return Code is not 0
resource "null_resource" "IfExitCodeIsNotZeroThenHaltCDB" {
  depends_on = [data.external.CheckAnsibleReturnCodeCDB]
  provisioner "local-exec" {
    interpreter = ["/bin/bash", "-c"]
    command     = "[ \"${data.external.CheckAnsibleReturnCodeCDB.result.found}\" = \"1\" ] &amp;&amp; echo \"Create Databases - Everything OK\" || { echo \"Deploying the CVAD Databases failed. Please review the settings. Halting Terraform.\"; exit 1; }"
  }
}
In this example, Terraform runs the Ansible playbook to create the CVAD databases. If an error occurs, Terraform writes an error message indicating that deploying the CVAD databases failed. You would find more information in the LogFile directory on the corresponding server. Terraform then stops the further execution of the snippets.  Part 1: Using Packer to create the Master ImagesHashicorp Packer is our standard framework for creating, maintaining, and deploying Master Images using IaC. Caution XenServer currently lacks an official Packer plugin. We use an open-source Tech Preview plugin maintained by my Citrite colleagues Rody Kossen and Marshall Wu to showcase the feasibility of end-to-end IaC. You can find more about this TP on GitHub: https://github.com/xenserver/packer-plugin-xenserver/tree/feature-rodyk-revamp Use the plugin at your own risk – please read the disclaimer at the end of the document for further information. There is NO warranty that it will work as intended – we cannot provide any support. You can find a more thorough example of using a supported Packer deployment in our TechZone guide "Using Infrastructure-as-Code for deploying Citrix® Virtual Apps and Desktops™ 2507 LTSR on vSphere™ 8" or in the third part of the Citrix Automation Handbook. Example Packer HCL file for deploying a Windows Server 2025-VM on XenServer 8: packer {
  required_plugins {
   xenserver= {
      version = "&gt;= v0.1.0"
      source = "github.com/xenserver/xenserver"
    }
  }
}

source "xenserver-iso" "XSTEST" {

    iso_checksum_type           = "none"
	iso_name					= var.os_iso_path
    sr_iso_name                 = var.sr_iso_name
    sr_name                     = var.sr_name
    tools_iso_name              = var.tools_iso_name

    remote_host                 = var.remote_host
    remote_password             = var.remote_password
    remote_username             = var.remote_username

    vm_name                     = var.vm_name
    vm_description              = var.vm_description
    vm_memory                   = var.memory
    disk_size                   = var.disksize
	vcpus				        = var.cpu
	cores_per_socket			= var.cpu
	network_names				= ["${var.virtual_network_name}"]
	firmware                    = var.firmware
    secure_boot 				= var.secure_boot
    vTPM                        = var.vtpm
    vgpu_profile                = var.vgpu_profile

	clone_template				= var.clone_template

    cd_files                	= ["W2K25AutoUnattendInclWinRM.xml"]

	ssh_username            	= var.ssh_username
	ssh_password            	= var.ssh_password
	ssh_wait_timeout        	= "60000s"
	
    boot_command = [
        "&lt;spacebar&gt;&lt;wait2&gt;&lt;spacebar&gt;"
    ]
    boot_wait                   = "1s"
	
    #Export Options
	format 						= "xva"
    keep_vm 					= "on_success"

    communicator                = "winrm"
    winrm_insecure              = true
    winrm_password              = var.winrm_password
    winrm_timeout               = "5m"
    winrm_use_ssl               = false
    winrm_username              = var.winrm_username
}

build {
   sources = ["xenserver-iso.windows"]
  
   provisioner "powershell" {
    inline = ["Write-Host 'Installing updates and XenServer Tools...'", "Start-Process msiexec.exe -ArgumentList '/i D:\\xensetup\\managementagentx64.msi /qn' -Wait”]
  }
}
In this example, we use a modified autounattend.xml file to configure the Windows VM during creation: &lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;unattend xmlns="urn:schemas-microsoft-com:unattend"&gt;
  &lt;settings pass="windowsPE"&gt;
    &lt;component name="Microsoft-Windows-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"&gt;
      &lt;DiskConfiguration&gt;
        &lt;Disk wcm:action="add"&gt;
          &lt;DiskID&gt;0&lt;/DiskID&gt;
          &lt;WillWipeDisk&gt;true&lt;/WillWipeDisk&gt;
          &lt;CreatePartitions&gt;
            &lt;CreatePartition wcm:action="add"&gt;
              &lt;Order&gt;1&lt;/Order&gt;
              &lt;Type&gt;Primary&lt;/Type&gt;
              &lt;Size&gt;100&lt;/Size&gt;
            &lt;/CreatePartition&gt;
            &lt;CreatePartition wcm:action="add"&gt;
              &lt;Order&gt;2&lt;/Order&gt;
              &lt;Type&gt;Primary&lt;/Type&gt;
              &lt;Extend&gt;true&lt;/Extend&gt;
            &lt;/CreatePartition&gt;
          &lt;/CreatePartitions&gt;
          &lt;ModifyPartitions&gt;
            &lt;ModifyPartition wcm:action="add"&gt;
              &lt;Order&gt;1&lt;/Order&gt;
              &lt;PartitionID&gt;1&lt;/PartitionID&gt;
              &lt;Active&gt;true&lt;/Active&gt;
              &lt;Label&gt;System&lt;/Label&gt;
              &lt;Format&gt;NTFS&lt;/Format&gt;
            &lt;/ModifyPartition&gt;
            &lt;ModifyPartition wcm:action="add"&gt;
              &lt;Order&gt;2&lt;/Order&gt;
              &lt;PartitionID&gt;2&lt;/PartitionID&gt;
              &lt;Label&gt;Windows&lt;/Label&gt;
              &lt;Format&gt;NTFS&lt;/Format&gt;
              &lt;Letter&gt;C&lt;/Letter&gt;
            &lt;/ModifyPartition&gt;
          &lt;/ModifyPartitions&gt;
        &lt;/Disk&gt;
        &lt;WillShowUI&gt;OnError&lt;/WillShowUI&gt;
      &lt;/DiskConfiguration&gt;

      &lt;ImageInstall&gt;
        &lt;OSImage&gt;
          &lt;InstallFrom&gt;
            &lt;MetaData wcm:action="add"&gt;
              &lt;Key&gt;/IMAGE/INDEX&lt;/Key&gt;
              &lt;Value&gt;1&lt;/Value&gt;
            &lt;/MetaData&gt;
          &lt;/InstallFrom&gt;
          &lt;InstallTo&gt;
            &lt;DiskID&gt;0&lt;/DiskID&gt;
            &lt;PartitionID&gt;2&lt;/PartitionID&gt;
          &lt;/InstallTo&gt;
        &lt;/OSImage&gt;
      &lt;/ImageInstall&gt;

      &lt;UserData&gt;
        &lt;ProductKey&gt;
          &lt;Key&gt;&lt;/Key&gt;
          &lt;WillShowUI&gt;Never&lt;/WillShowUI&gt;
        &lt;/ProductKey&gt;
        &lt;AcceptEula&gt;true&lt;/AcceptEula&gt;
        &lt;FullName&gt;Packer Builder&lt;/FullName&gt;
        &lt;Organization&gt;ExampleCorp&lt;/Organization&gt;
      &lt;/UserData&gt;
    &lt;/component&gt;
  &lt;/settings&gt;

  &lt;settings pass="specialize"&gt;
    &lt;component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"&gt;
      &lt;ComputerName&gt;WIN2025BASE&lt;/ComputerName&gt;
      &lt;TimeZone&gt;UTC&lt;/TimeZone&gt;
    &lt;/component&gt;

    &lt;component name="Microsoft-Windows-Deployment" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"&gt;
      &lt;RunSynchronous&gt;
        &lt;RunSynchronousCommand&gt;
          &lt;Order&gt;1&lt;/Order&gt;
          &lt;Path&gt;cmd /c winrm quickconfig -quiet&lt;/Path&gt;
          &lt;Description&gt;Enable WinRM&lt;/Description&gt;
        &lt;/RunSynchronousCommand&gt;
        &lt;RunSynchronousCommand&gt;
          &lt;Order&gt;2&lt;/Order&gt;
          &lt;Path&gt;cmd /c winrm set winrm/config/service/auth @{Basic="true"}&lt;/Path&gt;
          &lt;Description&gt;Enable Basic Auth&lt;/Description&gt;
        &lt;/RunSynchronousCommand&gt;
        &lt;RunSynchronousCommand&gt;
          &lt;Order&gt;3&lt;/Order&gt;
          &lt;Path&gt;cmd /c winrm set winrm/config/service @{AllowUnencrypted="true"}&lt;/Path&gt;
          &lt;Description&gt;Allow Unencrypted&lt;/Description&gt;
        &lt;/RunSynchronousCommand&gt;
        &lt;RunSynchronousCommand&gt;
          &lt;Order&gt;4&lt;/Order&gt;
          &lt;Path&gt;cmd /c netsh advfirewall set allprofiles state off&lt;/Path&gt;
          &lt;Description&gt;Disable Firewall&lt;/Description&gt;
        &lt;/RunSynchronousCommand&gt;
        &lt;RunSynchronousCommand&gt;
          &lt;Order&gt;5&lt;/Order&gt;
          &lt;Path&gt;cmd /c winrm set winrm/config/client @{TrustedHosts="*"}&lt;/Path&gt;
          &lt;Description&gt;Disable Firewall&lt;/Description&gt;
        &lt;/RunSynchronousCommand&gt;
      &lt;/RunSynchronous&gt;
    &lt;/component&gt;
  &lt;/settings&gt;

   &lt;settings pass="oobeSystem"&gt;
    &lt;component name="Microsoft-Windows-International-Core" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"&gt;
      &lt;InputLocale&gt;de-AT&lt;/InputLocale&gt;
      &lt;SystemLocale&gt;en-US&lt;/SystemLocale&gt;
      &lt;UILanguage&gt;en-US&lt;/UILanguage&gt;
      &lt;UserLocale&gt;de-AT&lt;/UserLocale&gt;
    &lt;/component&gt;
    &lt;component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"&gt;
      &lt;OOBE&gt;
        &lt;HideEULAPage&gt;true&lt;/HideEULAPage&gt;
        &lt;NetworkLocation&gt;Work&lt;/NetworkLocation&gt;
        &lt;ProtectYourPC&gt;3&lt;/ProtectYourPC&gt;
      &lt;/OOBE&gt;
      &lt;UserAccounts&gt;
        &lt;AdministratorPassword&gt;
          &lt;Value&gt;!XxXxXxXxXxXxXxXxX!&lt;/Value&gt;
          &lt;PlainText&gt;true&lt;/PlainText&gt;
        &lt;/AdministratorPassword&gt;
      &lt;/UserAccounts&gt;
      &lt;AutoLogon&gt;
        &lt;Username&gt;Administrator&lt;/Username&gt;
        &lt;Password&gt;
          &lt;Value&gt;!XxXxXxXxXxXxXxXxX!&lt;/Value&gt;
          &lt;PlainText&gt;true&lt;/PlainText&gt;
        &lt;/Password&gt;
        &lt;Enabled&gt;true&lt;/Enabled&gt;
        &lt;LogonCount&gt;1&lt;/LogonCount&gt;
      &lt;/AutoLogon&gt;

      &lt;FirstLogonCommands&gt;
      &lt;/FirstLogonCommands&gt;
      
    &lt;/component&gt;
  &lt;/settings&gt;

  &lt;cpi:offlineImage cpi:source="wim://wimfile/install.wim#Windows Server 2025 SERVERSTANDARD" xmlns:cpi="urn:schemas-microsoft-com:cpi" /&gt;
&lt;/unattend&gt;Running the Packer IaC snippet should create a XenServer XVA that can then be used as a Master Image. azadmin@tacg-cicd:~$ packer build packer-tmpl-w2k25-xs.pkr.hcl
xenserver-iso.XSTEST: output will be in this color.

==&gt; xenserver-iso.XSTEST: XAPI client session established
==&gt; xenserver-iso.XSTEST: Retrieving ISO
==&gt; xenserver-iso.XSTEST: Trying http://10.10.100.1/iso/windows_server_2025.iso
==&gt; xenserver-iso.XSTEST: Trying http://10.10.100.1/iso/windows_server_2025.iso
==&gt; xenserver-iso.XSTEST: http://10.10.100.1/iso/windows_server_2025.iso =&gt; C:\__PPMM\_PACKER\_XENSERVER\packer_cache\4282f11098333441b4a8c39028041a36490797cb
==&gt; xenserver-iso.XSTEST: Creating CD disk...
    xenserver-iso.XSTEST: OSCDIMG 2.56 CD-ROM and DVD-ROM Premastering Utility
    xenserver-iso.XSTEST: Copyright (C) Microsoft, 1993-2012. All rights reserved.
    xenserver-iso.XSTEST: Licensed only for producing Microsoft authorized content.
    xenserver-iso.XSTEST: Scanning source tree
    xenserver-iso.XSTEST: Scanning source tree complete (1 files in 1 directories)
    xenserver-iso.XSTEST: Computing directory information complete
    xenserver-iso.XSTEST: Image file is 57344 bytes (before optimization)
    xenserver-iso.XSTEST: Writing 1 files in 1 directories to tmp/packer1625305315.iso
    xenserver-iso.XSTEST: Storage optimization saved 0 files, 0 bytes (0% of image)
    xenserver-iso.XSTEST: After optimization, image file is 57344 bytes
    xenserver-iso.XSTEST: Done.
    xenserver-iso.XSTEST: 100% complete
    xenserver-iso.XSTEST: Done copying paths from CD_dirs
==&gt; xenserver-iso.XSTEST: Step: Upload VDI 'Packer-CD'
==&gt; xenserver-iso.XSTEST: Step: Found SR for upload 'OpaqueRef:ed24af24-cd3e-4ed0-2303-d885e72a691a'
==&gt; xenserver-iso.XSTEST: PUT 'https://10.10.119.1/import_raw_vdi?session_id=OpaqueRef%3A93b232ae-a9bf-8b83-bb54-9aaf7412b6ba&amp;task_id=OpaqueRef%3A99d66110-5044-37b2-7b21-baa23c54d2e1&amp;vdi=OpaqueRef%3A65a7da41-3aba-e5b1-d1f1-0e5f006286f4'
==&gt; xenserver-iso.XSTEST: Attemping to find VDI 'windows_server_2025.iso'
==&gt; xenserver-iso.XSTEST: Step: Upload VDI 'windows_server_2025.iso'
==&gt; xenserver-iso.XSTEST: Step: Found SR for upload 'OpaqueRef:ed24af24-cd3e-4ed0-2303-d885e72a691a'
==&gt; xenserver-iso.XSTEST: PUT 'https://10.10.119.1/import_raw_vdi?session_id=OpaqueRef%3A93b232ae-a9bf-8b83-bb54-9aaf7412b6ba&amp;task_id=OpaqueRef%3Ac8d26e51-2289-49b5-5363-220848bb4b6c&amp;vdi=OpaqueRef%3A4893a784-2514-12b3-20bf-9880fc5bf3be'
==&gt; xenserver-iso.XSTEST: Step: Create Instance
==&gt; xenserver-iso.XSTEST: Using the following SR for the VM: OpaqueRef:67690a5f-2856-f785-7cac-fb59728d7ef2
==&gt; xenserver-iso.XSTEST: Created instance '2d4f4efc-5a3d-d915-380c-f86b0108eabe'
==&gt; xenserver-iso.XSTEST: Step: Start VM Paused
==&gt; xenserver-iso.XSTEST: Step: Set SSH address to VM host IP
==&gt; xenserver-iso.XSTEST: Set host SSH address to '10.10.119.1'.
==&gt; xenserver-iso.XSTEST: Set host SSH port to 22.
==&gt; xenserver-iso.XSTEST: Unpausing VM 2d4f4efc-5a3d-d915-380c-f86b0108eabe
==&gt; xenserver-iso.XSTEST: Waiting 10s for boot...
==&gt; xenserver-iso.XSTEST: Connecting to VNC over XAPI...
    xenserver-iso.XSTEST: Making HTTP request to initiate VNC connection: CONNECT /console?uuid=6e861319-fb00-a8c8-d607-615b6c4561fe HTTP/1.0
    xenserver-iso.XSTEST: Host: 10.10.119.1
    xenserver-iso.XSTEST: Cookie: session_id=OpaqueRef:93b232ae-a9bf-8b83-bb54-9aaf7412b6ba
    xenserver-iso.XSTEST:
    xenserver-iso.XSTEST:
    xenserver-iso.XSTEST: Received response: HTTP/1.1 200 OK
    xenserver-iso.XSTEST: Connection: keep-alive
    xenserver-iso.XSTEST: Cache-Control: no-cache, no-store
    xenserver-iso.XSTEST:
    xenserver-iso.XSTEST:
    xenserver-iso.XSTEST: Found local IP: 10.10.11.99
==&gt; xenserver-iso.XSTEST: Typing boot commands over VNC...
==&gt; xenserver-iso.XSTEST: Step: Wait for VMs IP to become known to us.
==&gt; xenserver-iso.XSTEST: Successfully installed
==&gt; xenserver-iso.XSTEST: Destroying VDI
==&gt; xenserver-iso.XSTEST: Destroyed VDI 'windows_server_2025.iso'
==&gt; xenserver-iso.XSTEST: Destroyed VDI 'Packer-CD'
==&gt; xenserver-iso.XSTEST: Deleting output directory...
==&gt; xenserver-iso.XSTEST: Closing sessions ....
Build 'xenserver-iso.XSTEST' finished after 43 minutes 52 seconds.

==&gt; Wait completed after 43 minutes 52 seconds

==&gt; Builds finished. The artifacts of successful builds are:
--&gt; ==&gt; xenserver-iso.XSTEST: W2K25.2511

azadmin@tacg-cicd:~$ After a successful run, XenServer should reflect the created VM, and after conversion, the corresponding template:  The exact same process is done to create the Windows 11 Master Image and Template. Since the Master Images have been created, we can proceed to the next step.  Part 2: Using Terraform to create the needed VMs based on the Master ImagesTerraform now creates all the required Infrastructure VMs for deploying CVAD: Two Windows Server 2025-based VMs for the Delivery Controllers One Windows Server 2025-based VM for the StoreFront server One Windows Server 2025-based VM for the WebStudio server. All needed settings are defined in a dedicated JSON file, where all variables and their values are stored. This allows maximum code flexibility and reusability, as it does not need to be changed when other VM configurations arise. Note Those are the key principles of IaC – repeatability, reusability, and flexibility. Example of the VM settings in Terraform: {
    "TACG-TMM-XS-CL-VMSR":"local storage",
    "TACG-TMM-XS-CL-ISOSR":"ENTERPRISE ISO",
    "TACG-TMM-XS-CL-NW":"Network 0",
    "TACG-TMM-XS-CL-W2K25-MI":"W2K25.2511",
    "TACG-TMM-XS-CL-VM-DDC1-Name":"VM-TF-TACG-DDC1",
    "TACG-TMM-XS-CL-VM-DDC2-Name":"VM-TF-TACG-DDC2",
    "TACG-TMM-XS-CL-VM-StoreFront-Name":"VM-TF-TACG-SF",
    "TACG-TMM-XS-CL-VM-WebStudio-Name":"VM-TF-TACG-WS",
    "TACG-TMM-XS-CL-VM-DDC1-DiskName":"vdi-tacg-ddc1",
    "TACG-TMM-XS-CL-VM-DDC2-DiskName":"vdi-tacg-ddc2",
    "TACG-TMM-XS-CL-VM-StoreFront-DiskName":"vdi-tacg-sf",
    "TACG-TMM-XS-CL-VM-WebStudio-DiskName":"vdi-tacg-ws",
    "TACG-TMM-XS-CL-VM-DDC1-DiskSize":64424509440,
    "TACG-TMM-XS-CL-VM-DDC2-DiskSize":64424509440,
    "TACG-TMM-XS-CL-VM-StoreFront-DiskSize":64424509440,
    "TACG-TMM-XS-CL-VM-WebStudio-DiskSize":64424509440,
    "TACG-TMM-XS-CL-VM-DDC1-MemSize":8589934592,
    "TACG-TMM-XS-CL-VM-DDC2-MemSize":8589934592,
    "TACG-TMM-XS-CL-VM-StoreFront-MemSize":8589934592,
    "TACG-TMM-XS-CL-VM-WebStudio-MemSize":8589934592,
    "TACG-TMM-XS-CL-VM-DDC1-CPUs":2,
    "TACG-TMM-XS-CL-VM-DDC2-CPUs":2,
    "TACG-TMM-XS-CL-VM-StoreFront-CPUs":2,
    "TACG-TMM-XS-CL-VM-DDC1-Cores":2,
    "TACG-TMM-XS-CL-VM-DDC2-Cores":2,
    "TACG-TMM-XS-CL-VM-StoreFront-Cores":2
}For example, if you want to change the main disk size, you only need to change the corresponding value: From  to  would double the disk size to 128GB. Important As the current Terraform provider for XenServer does not support joining a newly created VM to an AD domain, this step will be performed in the next block using an Ansible playbook.  Caution The current version of the Terraform provider (1.0.30) at the time of writing this guide has a bug: The storage name is processed internally only in lowercase; however, if it contains an uppercase letter, an error message is returned.  That is why the code snippets always refer to “local storage” rather than its original name, “Local storage”.  Please rename the storage name you want to use in XenCenter™ to lowercase characters. Example Terraform snippet for VM creation: # Terraform Deployment of Citrix CVAD on XenServer 8 - 25.11 
## Definition of XenServer Provider Variables - not all available variables are defined, only these we use
### Reference https://registry.terraform.io/providers/xenserver/xenserver/latest/docs

## Definition of all required local variables
### Get XenServer-related Data
#### Get the VM SR object
data "xenserver_sr" "vmsr" {
  name_label = var.TACG-TMM-XS-CL-VMSR
}

#### Get the ISO SR object
data "xenserver_sr" "isosr" {
  name_label = var.TACG-TMM-XS-CL-ISOSR
}

#### Get the Network object
data "xenserver_network" "network" {
}

# Create a Windows Server 2025 VM for DDC1 from the custom template
resource "xenserver_vm" "VM-DDC1" {
  name_label            = var.TACG-TMM-XS-CL-VM-DDC1-Name
  template_name         = var.TACG-TMM-XS-CL-W2K25-MI
  static_mem_max        = var.TACG-TMM-XS-CL-VM-DDC1-MemSize
  vcpus                 = var.TACG-TMM-XS-CL-VM-DDC1-CPUs
  cores_per_socket      = var.TACG-TMM-XS-CL-VM-DDC1-Cores
  sr_for_full_disk_copy = data.xenserver_sr.vmsr.data_items[0].uuid

  network_interface = [
    {
      device       = "0"
      network_uuid = data.xenserver_network.network.data_items[0].uuid,
    },
  ]
}

# Create a Windows Server 2025 VM for DDC2 from the custom template
resource "xenserver_vm" "VM-DDC2" {
  name_label            = var.TACG-TMM-XS-CL-VM-DDC2-Name
  template_name         = var.TACG-TMM-XS-CL-W2K25-MI
  static_mem_max        = var.TACG-TMM-XS-CL-VM-DDC2-MemSize
  vcpus                 = var.TACG-TMM-XS-CL-VM-DDC2-CPUs
  cores_per_socket      = var.TACG-TMM-XS-CL-VM-DDC2-Cores
  sr_for_full_disk_copy = data.xenserver_sr.vmsr.data_items[0].uuid

  network_interface = [
    {
      device       = "0"
      network_uuid = data.xenserver_network.network.data_items[0].uuid,
    },
  ]
}

# Create a Windows Server 2025 VM for SF from the custom template
resource "xenserver_vm" "VM-StoreFront" {
  name_label            = var.TACG-TMM-XS-CL-VM-StoreFront-Name
  template_name         = var.TACG-TMM-XS-CL-W2K25-MI
  static_mem_max        = var.TACG-TMM-XS-CL-VM-StoreFront-MemSize
  vcpus                 = var.TACG-TMM-XS-CL-VM-StoreFront-CPUs
  cores_per_socket      = var.TACG-TMM-XS-CL-VM-StoreFront-Cores
  sr_for_full_disk_copy = data.xenserver_sr.vmsr.data_items[0].uuid

  network_interface = [
    {
      device       = "0"
      network_uuid = data.xenserver_network.network.data_items[0].uuid,
    },
  ]
}

# Create a Windows Server 2025 VM for WebStudio from the custom template
resource "xenserver_vm" "VM-WebStudio" {
  name_label            = var.TACG-TMM-XS-CL-VM-WebStudio-Name
  template_name         = var.TACG-TMM-XS-CL-W2K25-MI
  static_mem_max        = var.TACG-TMM-XS-CL-VM-WebStudio-MemSize
  vcpus                 = var.TACG-TMM-XS-CL-VM-StoreFront-CPUs
  cores_per_socket      = var.TACG-TMM-XS-CL-VM-StoreFront-Cores
  sr_for_full_disk_copy = data.xenserver_sr.vmsr.data_items[0].uuid

  network_interface = [
    {
      device       = "0"
      network_uuid = data.xenserver_network.network.data_items[0].uuid,
    },
  ]
}Running this snippet should create all four VMs on XenServer: azadmin@tacg-cicd:/etc/terraform-tmm/CVADXS/_Creation$ terraform apply
data.xenserver_sr.vmsr: Reading...
data.xenserver_sr.isosr: Reading...
data.xenserver_network.network: Reading...
data.xenserver_sr.vmsr: Read complete after 0s
data.xenserver_network.network: Read complete after 0s
data.xenserver_sr.isosr: Read complete after 0s

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:

  # xenserver_vm.VM-DDC1 will be created
  + resource "xenserver_vm" "VM-DDC1" {
      + boot_mode             = (known after apply)
      + boot_order            = (known after apply)
      + cdrom                 = "Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso"
      + check_ip_timeout      = 0
      + cores_per_socket      = 2
      + default_ip            = (known after apply)
      + dynamic_mem_max       = (known after apply)
      + dynamic_mem_min       = (known after apply)
      + hard_drive            = (known after apply)
      + id                    = (known after apply)
      + name_description      = ""
      + name_label            = "VM-TF-TACG-DDC1"
      + network_interface     = [
          + {
              + device       = "0"
              + mac          = (known after apply)
              + network_uuid = "48045591-9653-12a9-edd9-62b345080a75"
              + other_config = (known after apply)
              + vif_ref      = (known after apply)
            },
        ]
      + other_config          = {}
      + sr_for_full_disk_copy = "ab9a9821-a664-8727-788e-0bb4bbd692b8"
      + static_mem_max        = 8589934592
      + static_mem_min        = (known after apply)
      + template_name         = "W2K25.1025"
      + uuid                  = (known after apply)
      + vcpus                 = 2
    }

  # xenserver_vm.VM-DDC2 will be created
  + resource "xenserver_vm" "VM-DDC2" {
      + boot_mode             = (known after apply)
      + boot_order            = (known after apply)
      + cdrom                 = "Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso"
      + check_ip_timeout      = 0
      + cores_per_socket      = 2
      + default_ip            = (known after apply)
      + dynamic_mem_max       = (known after apply)
      + dynamic_mem_min       = (known after apply)
      + hard_drive            = (known after apply)
      + id                    = (known after apply)
      + name_description      = ""
      + name_label            = "VM-TF-TACG-DDC2"
      + network_interface     = [
          + {
              + device       = "0"
              + mac          = (known after apply)
              + network_uuid = "48045591-9653-12a9-edd9-62b345080a75"
              + other_config = (known after apply)
              + vif_ref      = (known after apply)
            },
        ]
      + other_config          = {}
      + sr_for_full_disk_copy = "ab9a9821-a664-8727-788e-0bb4bbd692b8"
      + static_mem_max        = 8589934592
      + static_mem_min        = (known after apply)
      + template_name         = "W2K25.1025"
      + uuid                  = (known after apply)
      + vcpus                 = 2
    }

  # xenserver_vm.VM-StoreFront will be created
  + resource "xenserver_vm" "VM-StoreFront" {
      + boot_mode             = (known after apply)
      + boot_order            = (known after apply)
      + cdrom                 = "Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso"
      + check_ip_timeout      = 0
      + cores_per_socket      = 2
      + default_ip            = (known after apply)
      + dynamic_mem_max       = (known after apply)
      + dynamic_mem_min       = (known after apply)
      + hard_drive            = (known after apply)
      + id                    = (known after apply)
      + name_description      = ""
      + name_label            = "VM-TF-TACG-SF"
      + network_interface     = [
          + {
              + device       = "0"
              + mac          = (known after apply)
              + network_uuid = "48045591-9653-12a9-edd9-62b345080a75"
              + other_config = (known after apply)
              + vif_ref      = (known after apply)
            },
        ]
      + other_config          = {}
      + sr_for_full_disk_copy = "ab9a9821-a664-8727-788e-0bb4bbd692b8"
      + static_mem_max        = 8589934592
      + static_mem_min        = (known after apply)
      + template_name         = "W2K25.1025"
      + uuid                  = (known after apply)
      + vcpus                 = 2
    }
 # xenserver_vm.VM-WebStudio will be created
  + resource "xenserver_vm" "VM-WebStudio" {
      + boot_mode             = (known after apply)
      + boot_order            = (known after apply)
      + cdrom                 = "Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso"
      + check_ip_timeout      = 0
      + cores_per_socket      = 2
      + default_ip            = (known after apply)
      + dynamic_mem_max       = (known after apply)
      + dynamic_mem_min       = (known after apply)
      + hard_drive            = (known after apply)
      + id                    = (known after apply)
      + name_description      = ""
      + name_label            = "VM-TF-TACG-WS"
      + network_interface     = [
          + {
              + device       = "0"
              + mac          = (known after apply)
              + network_uuid = "48045591-9653-12a9-edd9-62b345080a75"
              + other_config = (known after apply)
              + vif_ref      = (known after apply)
            },
        ]
      + other_config          = {}
      + sr_for_full_disk_copy = "ab9a9821-a664-8727-788e-0bb4bbd692b8"
      + static_mem_max        = 8589934592
      + static_mem_min        = (known after apply)
      + template_name         = "W2K25.1025"
      + uuid                  = (known after apply)
      + vcpus                 = 2
    }

Plan: 4 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

xenserver_vm.VM-DDC2: Creating...
xenserver_vm.VM-DDC1: Creating...
xenserver_vm.VM-StoreFront: Creating...
xenserver_vm.VM-WebStudio: Creating...
xenserver_vm.VM-DDC2: Still creating... [00m10s elapsed]
xenserver_vm.VM-StoreFront: Still creating... [00m10s elapsed]
xenserver_vm.VM-DDC1: Still creating... [00m10s elapsed]
xenserver_vm.VM-WebStudio: Still creating... [00m10s elapsed]
...
xenserver_vm.VM-DDC2: Still creating... [01m50s elapsed]
xenserver_vm.VM-StoreFront: Still creating... [01m50s elapsed]
xenserver_vm.VM-DDC1: Still creating... [01m50s elapsed]
xenserver_vm.VM-WebStudio: Still creating... [01m50s elapsed]
xenserver_vm.VM-DDC2: Creation complete after 1m59s [id=5e08ba70-f038-f926-9650-99148985018e]
xenserver_vm.VM-DDC1: Creation complete after 1m59s [id=16330ad2-4c9a-e2db-cdc5-9c9b765fa879]
xenserver_vm.VM-StoreFront: Creation complete after 1m59s [id=db28575f-34f1-7624-184a-9f9c69bebc0b]
xenserver_vm.VM-WebStudio: Creation complete after 1m59s [id=88e132dd-45ed-1121-9eab-1e7fda2361aa]

Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
azadmin@tacg-cicd:/etc/terraform-tmm/CVADXS/_Creation$Since the VMs have been created, we can proceed to the next step.  Part 3: Using Terraform and Ansible to configure the VMsThis configuration block now uses the combination of Terraform and Ansible for all needed configuration steps. As mentioned, Terraform excels at creating components; Ansible excels at configuring them. Together, we have everything we need for a successful deployment. Note: In all our guides, we try to be as Hypervisor- and Hyperscaler-agnostic as possible. For example, the vSphere provider could add a VM to an AD domain, but not all providers support this, and each provider uses a different syntax. Using the same Ansible playbook across all Hypervisors/Hyperscalers, regardless of built-in capabilities, also improves code reuse, as no specific code needs to be changed for different Hypervisor-/Hyperscaler deployments. This block also shows the main flow: Terraform decides which steps are necessary and starts the corresponding Ansible playbooks to achieve the needed outcome.  The following configuration steps are sequentially done: Change the IP addresses to known IPs. Change the Ansible configuration file to reflect the IP changes. Put the VMs into the AD domain. As the VMs are created, they will get an IP address from a DHCP server. This is expected behavior, but it will break any communication flow between Ansible and the VMs, as Ansible needs to know how to contact the VM. It can also lead to interruptions if the VMs are still configured to use DHCP when the Delivery Controllers and StoreFront are installed, so it is imperative to update the Ansible hosts file to reflect the assigned, static IP addresses after IP assignment. In our example, the VMs will get DHCP-based IP addresses in the 10.10.11.x/24 range. This is reflected in Ansible´s etc/ansible/HOSTS file: ...
[ddc1_ipaddr]
10.10.11.178

[ddc2_ipaddr]
10.10.11.179

[sf_ipaddr]
10.10.11.180

[ws_ipaddr]
10.10.11.181
...After the VMs are created, Ansible can contact them using the originally assigned DHCP-based IPs. Terraform now takes the three steps mentioned above to correct that. Therefore, it changes the IPs to fixed IPs defined in the Ansible playbook's corresponding variables. The etc/ansible/HOSTS file is updated with the correct IPs, and the VMs are added to the domain. Terraform monitors each Ansible Playbook run and stops further steps if a Playbook fails. 
# Terraform Deployment of Citrix CVAD on XenServer 8 - 25.11 
## Definition of XenServer Provider Variables - not all available variables are defined, only these we use
### Reference https://registry.terraform.io/providers/xenserver/xenserver/latest/docs

## Definition of all required local variables
### Get Needed Data
#### Call Ansible to Join VM to Domain
##### Copy Ansible Playbooks to Ansible Server

###### ***** IMPORTANT: You must set the permissions to the destination directory accordingly (sudo) chmod 777 otherwise the provisioning will fail *****
resource "null_resource" "CopyPlaybooksForCC1ToAnsibleServer" {
  connection {
    type     = var.TACG-TMM-XS-CL-Ansible-Connection-Type
    user     = var.TACG-TMM-XS-CL-Ansible-Connection-User
    password = var.TACG-TMM-XS-CL-Ansible-Connection-Password
    host     = var.TACG-TMM-XS-CL-Ansible-Connection-HostIP
    port     = var.TACG-TMM-XS-CL-Ansible-Connection-Port
    https    = var.TACG-TMM-XS-CL-Ansible-Connection-HTTPS
    timeout  = var.TACG-TMM-XS-CL-Ansible-Connection-Timeout
  }

  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-DomainJoinPlaybook-CC1-Source
    destination = var.TACG-TMM-XS-CL-Ansible-DomainJoinPlaybook-CC1-Destination
  }

  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-IPChangePlaybook-CC1-Source
    destination = var.TACG-TMM-XS-CL-Ansible-IPChangePlaybook-CC1-Destination
  }

  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-HostChangePlaybook-CC1-Source
    destination = var.TACG-TMM-XS-CL-Ansible-HostChangePlaybook-CC1-Destination
  }
}

resource "null_resource" "CopyPlaybooksForCC2ToAnsibleServer" {
  connection {
    type     = var.TACG-TMM-XS-CL-Ansible-Connection-Type
    user     = var.TACG-TMM-XS-CL-Ansible-Connection-User
    password = var.TACG-TMM-XS-CL-Ansible-Connection-Password
    host     = var.TACG-TMM-XS-CL-Ansible-Connection-HostIP
    port     = var.TACG-TMM-XS-CL-Ansible-Connection-Port
    https    = var.TACG-TMM-XS-CL-Ansible-Connection-HTTPS
    timeout  = var.TACG-TMM-XS-CL-Ansible-Connection-Timeout
  }

  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-DomainJoinPlaybook-CC2-Source
    destination = var.TACG-TMM-XS-CL-Ansible-DomainJoinPlaybook-CC2-Destination
  }

  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-IPChangePlaybook-CC2-Source
    destination = var.TACG-TMM-XS-CL-Ansible-IPChangePlaybook-CC2-Destination
  }

  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-HostChangePlaybook-CC2-Source
    destination = var.TACG-TMM-XS-CL-Ansible-HostChangePlaybook-CC2-Destination
  }
}

resource "null_resource" "CopyPlaybooksForSFToAnsibleServer" {
  connection {
    type     = var.TACG-TMM-XS-CL-Ansible-Connection-Type
    user     = var.TACG-TMM-XS-CL-Ansible-Connection-User
    password = var.TACG-TMM-XS-CL-Ansible-Connection-Password
    host     = var.TACG-TMM-XS-CL-Ansible-Connection-HostIP
    port     = var.TACG-TMM-XS-CL-Ansible-Connection-Port
    https    = var.TACG-TMM-XS-CL-Ansible-Connection-HTTPS
    timeout  = var.TACG-TMM-XS-CL-Ansible-Connection-Timeout
  }

  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-DomainJoinPlaybook-SF-Source
    destination = var.TACG-TMM-XS-CL-Ansible-DomainJoinPlaybook-SF-Destination
  }

  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-IPChangePlaybook-SF-Source
    destination = var.TACG-TMM-XS-CL-Ansible-IPChangePlaybook-SF-Destination
  }

  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-HostChangePlaybook-SF-Source
    destination = var.TACG-TMM-XS-CL-Ansible-HostChangePlaybook-SF-Destination
  }
}

resource "null_resource" "CopyPlaybooksForWebStudioToAnsibleServer" {
  connection {
    type     = var.TACG-TMM-XS-CL-Ansible-Connection-Type
    user     = var.TACG-TMM-XS-CL-Ansible-Connection-User
    password = var.TACG-TMM-XS-CL-Ansible-Connection-Password
    host     = var.TACG-TMM-XS-CL-Ansible-Connection-HostIP
    port     = var.TACG-TMM-XS-CL-Ansible-Connection-Port
    https    = var.TACG-TMM-XS-CL-Ansible-Connection-HTTPS
    timeout  = var.TACG-TMM-XS-CL-Ansible-Connection-Timeout
  }

  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-DomainJoinPlaybook-WS-Source
    destination = var.TACG-TMM-XS-CL-Ansible-DomainJoinPlaybook-WS-Destination
  }

  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-IPChangePlaybook-WS-Source
    destination = var.TACG-TMM-XS-CL-Ansible-IPChangePlaybook-WS-Destination
  }

  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-HostChangePlaybook-WS-Source
    destination = var.TACG-TMM-XS-CL-Ansible-HostChangePlaybook-WS-Destination
  }
}

#### Wait 10s Minute for Backgroud Processes to Settle
resource "time_sleep" "wait_10_seconds" {
  depends_on = [null_resource.CopyPlaybooksForCC1ToAnsibleServer, null_resource.CopyPlaybooksForCC2ToAnsibleServer, null_resource.CopyPlaybooksForSFToAnsibleServer, null_resource.CopyPlaybooksForWebStudioToAnsibleServer]

  create_duration = "10s"
}

#### Use Ansible to change all relevant settings on DDC1 
##### Connect to Ansible Interpreter and change IP of DDC1
resource "null_resource" "ChangeIPOfDDC1" {
  depends_on = [time_sleep.wait_10_seconds]
  provisioner "local-exec" {
    working_dir = "/etc/ansible"
    command     = var.TACG-TMM-XS-CL-Ansible-IPChangeCMDForCC1
  }
}

##### Connect to Ansible Interpreter and change IP of DDC1 in Ansible´s HOSTS file
resource "null_resource" "ChangeHostIPOfDDC1" {
  depends_on = [null_resource.ChangeIPOfDDC1]
  provisioner "local-exec" {
    working_dir = "/etc/ansible"
    command     = var.TACG-TMM-XS-CL-Ansible-HostChangeCMDForCC1
  }
}

##### Check Ansible Return Code
data "external" "CheckAnsibleReturnCode1" {
  depends_on = [null_resource.ChangeHostIPOfDDC1]
  program    = ["bash", "/etc/ansible/assets/CheckAnsibleReturnCode.sh", "/etc/ansible/assets/ansible_exit_code.txt", "0"]
}

##### Terminate if Return Code is not 0
resource "null_resource" "IfExitCodeIsNotZeroThenHalt1" {
  depends_on = [data.external.CheckAnsibleReturnCode1]
  provisioner "local-exec" {
    interpreter = ["/bin/bash", "-c"]
    command     = "[ \"${data.external.CheckAnsibleReturnCode1.result.found}\" = \"1\" ] &amp;&amp; echo \"Changing IP/HOSTS: Everything OK\" || { echo \"Changing the IP of DDC1 and/or changing the Ansible HOSTS file failed. Please review the settings. Halting Terraform.\"; exit 1; }"
  }
}

resource "time_sleep" "wait_10_seconds_1" {
  depends_on      = [null_resource.IfExitCodeIsNotZeroThenHalt1]
  create_duration = "10s"
}

##### Connect to Ansible Interpreter and Add DDC1 To Domain
resource "null_resource" "AddCC1ToDomain" {
  depends_on = [time_sleep.wait_10_seconds_1]

  provisioner "local-exec" {
    working_dir = "/etc/ansible"
    command     = var.TACG-TMM-XS-CL-Ansible-DomainJoinCMDForCC1
  }
}

##### Check Ansible Return Code
data "external" "CheckAnsibleReturnCodeD1" {
  depends_on = [null_resource.AddCC1ToDomain]
  program    = ["bash", "/etc/ansible/assets/CheckAnsibleReturnCode.sh", "/etc/ansible/assets/ansible_exit_code.txt", "0"]
}

##### Terminate if Return Code is not 0
resource "null_resource" "IfExitCodeIsNotZeroThenHaltD1" {
  depends_on = [data.external.CheckAnsibleReturnCodeD1]
  provisioner "local-exec" {
    interpreter = ["/bin/bash", "-c"]
    command     = "[ \"${data.external.CheckAnsibleReturnCodeD1.result.found}\" = \"1\" ] &amp;&amp; echo \"Adding DDC1 to Domain: Everything OK\" || { echo \"Adding DDC1 to the domain failed. Please review the settings. Halting Terraform.\"; exit 1; }"
  }
}

##### End of necessary changes on DDC1 

#### Wait 10s Minute for Backgroud Processes to Settle
resource "time_sleep" "wait_10_seconds_afterddc1" {
  depends_on = [null_resource.IfExitCodeIsNotZeroThenHaltD1]

  create_duration = "10s"
}

...
The same steps are needed for all the other servers
...

##### All necessary changes on the VMs are completeExample Ansible Playbook for changing the IP Address: ...
- name: Change DDC1 IP address
  hosts: ddc1_ipaddr
  gather_facts: yes
  vars:
    ip: "10.10.119.11"
    sn: "16"
    gw: "10.10.0.1"
    dns: "10.10.100.1"

  tasks:
    - name: Set up static IP address
      ansible.windows.win_shell: |
        $interface = Get-NetAdapter | ? {$_.Status -eq "up"}
        $IpAddre = "{{ ip }}"
        $Prefx = "{{ sn }}"
        $GW = "{{ gw }}"
        $DNSAddre = "{{ dns }}"
        $interface | New-NetIPAddress -IPAddress $IpAddre -PrefixLength $Prefx -DefaultGateway $GW
        $interface | Set-DnsClientServerAddress -ServerAddresses $DNSAddre
        Start-Sleep -Seconds 10
      async: 100
      poll: 0
      register: ip_resultExample Ansible Playbook for changing the etc/ansible/HOSTS file: ...
- name: Replace old IP with new IP in /etc/ansible/hosts
  hosts: localhost
  connection: local
  become: true
  vars:
    ip: "10.10.119.11"
    oldip: "10.10.11.163"
  tasks:
    - name: Replace old IP with new IP in /etc/ansible/hosts
      lineinfile:
        path: /etc/ansible/hosts
        search_string: '{{ oldip }}'  
        line: '{{ ip }}'       
        state: presentExample Ansible Playbook for adding the VM to the AD domain: ...
- name: join host to domain with automatic reboot
  hosts: ddc1_ipaddr

  tasks:
  - name: join host to domain with automatic reboot
    microsoft.ad.membership:
      dns_domain_name: the-austrian-citrix-guy.at
      hostname: tf-cvad-ddc1
      domain_admin_user: XxXxXxXxX@the-austrian-citrix-guy.at
      domain_admin_password: "!XxXxXxXxXxXxXxXxX!"
      domain_ou_path: "OU=_COMPUTER,OU=TACG-CVAD,DC=the-austrian-citrix-guy,DC=at"
      state: domain
      reboot: trueRunning the snippet should fulfill all needed steps: azadmin@tacg-cicd:/etc/terraform-tmm/CVADXS/_UseAnsibleToJoinDomain$ terraform apply

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create
 &lt;= read (data resources)

Terraform will perform the following actions:

  # data.external.CheckAnsibleReturnCode1 will be read during apply
  # (depends on a resource or a module with changes pending)
 &lt;= data "external" "CheckAnsibleReturnCode1" {
      + id      = (known after apply)
      + program = [
          + "bash",
          + "/etc/ansible/assets/CheckAnsibleReturnCode.sh",
          + "/etc/ansible/assets/ansible_exit_code.txt",
          + "0",
        ]
      + result  = (known after apply)
    }

  # data.external.CheckAnsibleReturnCode2 will be read during apply
  # (depends on a resource or a module with changes pending)
 &lt;= data "external" "CheckAnsibleReturnCode2" {
      + id      = (known after apply)
      + program = [
          + "bash",
          + "/etc/ansible/assets/CheckAnsibleReturnCode.sh",
          + "/etc/ansible/assets/ansible_exit_code.txt",
          + "0",
        ]
      + result  = (known after apply)
    }

  ...

  # time_sleep.wait_10_seconds_afterddc2 will be created
  + resource "time_sleep" "wait_10_seconds_afterddc2" {
      + create_duration = "10s"
      + id              = (known after apply)
    }

  # time_sleep.wait_10_seconds_aftersf will be created
  + resource "time_sleep" "wait_10_seconds_aftersf" {
      + create_duration = "10s"
      + id              = (known after apply)
    }

Plan: 32 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.CopyPlaybooksForWebStudioToAnsibleServer: Creating...
null_resource.CopyPlaybooksForCC2ToAnsibleServer: Creating...
null_resource.CopyPlaybooksForCC1ToAnsibleServer: Creating...
null_resource.CopyPlaybooksForSFToAnsibleServer: Creating...
null_resource.CopyPlaybooksForCC2ToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPlaybooksForCC1ToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPlaybooksForWebStudioToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPlaybooksForSFToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPlaybooksForSFToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPlaybooksForCC2ToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPlaybooksForWebStudioToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPlaybooksForCC1ToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPlaybooksForSFToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPlaybooksForCC2ToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPlaybooksForCC1ToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPlaybooksForWebStudioToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPlaybooksForSFToAnsibleServer: Creation complete after 0s [id=9099395716454281124]
null_resource.CopyPlaybooksForCC2ToAnsibleServer: Creation complete after 1s [id=2239027084142302025]
null_resource.CopyPlaybooksForCC1ToAnsibleServer: Creation complete after 1s [id=8241870746583503880]
null_resource.CopyPlaybooksForWebStudioToAnsibleServer: Creation complete after 1s [id=85414301571074153]
time_sleep.wait_10_seconds: Creating...
time_sleep.wait_10_seconds: Still creating... [00m10s elapsed]
time_sleep.wait_10_seconds: Creation complete after 10s [id=2025-11-03T12:50:18Z]
null_resource.ChangeIPOfDDC1: Creating...
null_resource.ChangeIPOfDDC1: Provisioning with 'local-exec'...
null_resource.ChangeIPOfDDC1 (local-exec): Executing: ["/bin/sh" "-c" "ansible-playbook /etc/ansible/assets/ChangeIPAddress-DDC1.microsoft.ad.ansible.yml -v -c winrm; echo $? &gt; /etc/ansible/assets/ansible_exit_code.txt"]

null_resource.ChangeIPOfDDC1 (local-exec): PLAY [Change DDC1 IP address] **************************************************

null_resource.ChangeIPOfDDC1 (local-exec): TASK [Gathering Facts] *********************************************************
null_resource.ChangeIPOfDDC1 (local-exec): ok: [10.10.11.163]

null_resource.ChangeIPOfDDC1 (local-exec): TASK [Set up static IP address] ************************************************
null_resource.ChangeIPOfDDC1 (local-exec): ok: [10.10.11.163] =&gt; {"ansible_async_watchdog_pid": 2392, "ansible_job_id": "j399189549114.3256", "changed": false, "finished": false, "results_file": "C:\\Users\\Administrator\\.ansible_async\\j399189549114.3256", "started": true}

null_resource.ChangeIPOfDDC1 (local-exec): PLAY RECAP *********************************************************************
null_resource.ChangeIPOfDDC1 (local-exec): 10.10.11.163               : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

null_resource.ChangeIPOfDDC1: Creation complete after 5s [id=679400677203292951]
null_resource.ChangeHostIPOfDDC1: Creating...
null_resource.ChangeHostIPOfDDC1: Provisioning with 'local-exec'...
null_resource.ChangeHostIPOfDDC1 (local-exec): Executing: ["/bin/sh" "-c" "ansible-playbook /etc/ansible/assets/ChangeHostFile-DDC1.microsoft.ad.ansible.yml -v -c winrm; echo $? &gt; /etc/ansible/assets/ansible_exit_code.txt"]

null_resource.ChangeHostIPOfDDC1 (local-exec): PLAY [Replace old IP with new IP in /etc/ansible/hosts] ************************

null_resource.ChangeHostIPOfDDC1 (local-exec): TASK [Gathering Facts] *********************************************************
null_resource.ChangeHostIPOfDDC1 (local-exec): ok: [localhost]

null_resource.ChangeHostIPOfDDC1 (local-exec): TASK [Replace old IP with new IP in /etc/ansible/hosts] ************************
null_resource.ChangeHostIPOfDDC1 (local-exec): changed: [localhost] =&gt; {"backup": "", "changed": true, "msg": "line replaced"}

null_resource.ChangeHostIPOfDDC1 (local-exec): PLAY RECAP *********************************************************************
null_resource.ChangeHostIPOfDDC1 (local-exec): localhost                  : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

null_resource.ChangeHostIPOfDDC1: Creation complete after 1s [id=4435900660469540446]
data.external.CheckAnsibleReturnCode1: Reading...
data.external.CheckAnsibleReturnCode1: Read complete after 0s [id=-]
null_resource.IfExitCodeIsNotZeroThenHalt1: Creating...
null_resource.IfExitCodeIsNotZeroThenHalt1: Provisioning with 'local-exec'...
null_resource.IfExitCodeIsNotZeroThenHalt1 (local-exec): Executing: ["/bin/bash" "-c" "[ \"1\" = \"1\" ] &amp;&amp; echo \"Changing IP/HOSTS: Everything OK\" || { echo \"Changing the IP of DDC1 and/or changing the Ansible HOSTS file failed. Please review the settings. Halting Terraform.\"; exit 1; }"]
null_resource.IfExitCodeIsNotZeroThenHalt1 (local-exec): Changing IP/HOSTS: Everything OK
null_resource.IfExitCodeIsNotZeroThenHalt1: Creation complete after 0s [id=8720650856500940604]
time_sleep.wait_10_seconds_1: Creating...
time_sleep.wait_10_seconds_1: Still creating... [00m10s elapsed]
time_sleep.wait_10_seconds_1: Creation complete after 10s [id=2025-11-03T12:50:35Z]
null_resource.AddCC1ToDomain: Creating...
null_resource.AddCC1ToDomain: Provisioning with 'local-exec'...
null_resource.AddCC1ToDomain (local-exec): Executing: ["/bin/sh" "-c" "ansible-playbook /etc/ansible/assets/Join-VMToDomain-DDC1.microsoft.ad.ansible.yml -v -c winrm; echo $? &gt; /etc/ansible/assets/ansible_exit_code.txt"]

null_resource.AddCC1ToDomain (local-exec): PLAY [join host to domain with automatic reboot] *******************************

null_resource.AddCC1ToDomain (local-exec): TASK [Gathering Facts] *********************************************************
null_resource.AddCC1ToDomain (local-exec): ok: [10.10.119.11]

null_resource.AddCC1ToDomain (local-exec): TASK [join host to domain with automatic reboot] *******************************
null_resource.AddCC1ToDomain: Still creating... [00m10s elapsed]
null_resource.AddCC1ToDomain: Still creating... [00m20s elapsed]
null_resource.AddCC1ToDomain (local-exec): changed: [10.10.119.11] =&gt; {"changed": true, "reboot_required": false}

null_resource.AddCC1ToDomain (local-exec): PLAY RECAP *********************************************************************
null_resource.AddCC1ToDomain (local-exec): 10.10.119.11               : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

null_resource.AddCC1ToDomain: Creation complete after 28s [id=6625761552218837946]
data.external.CheckAnsibleReturnCodeD1: Reading...
data.external.CheckAnsibleReturnCodeD1: Read complete after 0s [id=-]
null_resource.IfExitCodeIsNotZeroThenHaltD1: Creating...
null_resource.IfExitCodeIsNotZeroThenHaltD1: Provisioning with 'local-exec'...
null_resource.IfExitCodeIsNotZeroThenHaltD1 (local-exec): Executing: ["/bin/bash" "-c" "[ \"1\" = \"1\" ] &amp;&amp; echo \"Adding DDC1 to Domain: Everything OK\" || { echo \"Adding DDC1 to the domain failed. Please review the settings. Halting Terraform.\"; exit 1; }"]
null_resource.IfExitCodeIsNotZeroThenHaltD1 (local-exec): Adding DDC1 to Domain: Everything OK
null_resource.IfExitCodeIsNotZeroThenHaltD1: Creation complete after 0s [id=7818126381059512591]
time_sleep.wait_10_seconds_afterddc1: Creating...
time_sleep.wait_10_seconds_afterddc1: Still creating... [00m10s elapsed]
time_sleep.wait_10_seconds_afterddc1: Creation complete after 10s [id=2025-11-03T12:51:13Z]
...
All these steps are the same for the other three servers.
Output omitted due to length
...
data.external.CheckAnsibleReturnCodeDWS: Reading...
data.external.CheckAnsibleReturnCodeDWS: Read complete after 0s [id=-]
null_resource.IfExitCodeIsNotZeroThenHaltDWS: Creating...
null_resource.IfExitCodeIsNotZeroThenHaltDWS: Provisioning with 'local-exec'...
null_resource.IfExitCodeIsNotZeroThenHaltDWS (local-exec): Executing: ["/bin/bash" "-c" "[ \"1\" = \"1\" ] &amp;&amp; echo \"Adding WebStudio to Domain: Everything OK\" || { echo \"Adding WebStudio to the domain failed. Please review the settings. Halting Terraform.\"; exit 1; }"]
null_resource.IfExitCodeIsNotZeroThenHaltDWS (local-exec): Adding WebStudio to Domain: Everything OK
null_resource.IfExitCodeIsNotZeroThenHaltDWS: Creation complete after 0s [id=975638781208917805]

Apply complete! Resources: 32 added, 0 changed, 0 destroyed.
azadmin@tacg-cicd:/etc/terraform-tmm/CVADXS/_UseAnsibleToJoinDomain$The etc/ansible/HOSTS file is updated with the correct IPs: ...
[ddc1_ipaddr]
10.10.119.11

[ddc2_ipaddr]
10.10.119.12

[sf_ipaddr]
10.10.119.13

[ws_ipaddr]
10.10.119.14
..Ansible´s further used inventory.ini file also reflects all these changes: [cvad:children]
cvad-ddcs
cvad-sfs
cvad-ws

[cvad-ddcs]
tf-cvad-ddc1.the-austrian-citrix-guy.at
tf-cvad-ddc2.the-austrian-citrix-guy.at

[cvad-ddc1]
tf-cvad-ddc1.the-austrian-citrix-guy.at

[cvad-ddc2]
tf-cvad-ddc2.the-austrian-citrix-guy.at

[cvad-ddc1-nb]
TF-CVAD-DDC1

[cvad-ddc2-nb]
TF-CVAD-DDC2

[cvad-sfs]
tf-cvad-sf.the-austrian-citrix-guy.at

[cvad-ws]
tf-cvad-ws.the-austrian-citrix-guy.at

[all:vars]
ansible_user="svc_ansible"
ansible_domainuser="XxXxXxXxX@the-austrian-citrix-guy.at"
ansible_password="!XxXxXxXxXxXxXxXxXx!”
ansible_connection=winrm
ansible_winrm_scheme=https
ansible_winrm_transport=certificate
ansible_winrm_port=5986
ansible_winrm_server_cert_validation=ignore
ansible_winrm_cert_pem=/etc/ssl/certs/svc_ansible.pem
ansible_winrm_cert_key_pem=/etc/ssl/certs/svc_ansible.pem
ansible_winrm_server_cert_validation=ignoreSince the VMs and their dependencies were successfully reconfigured, we can proceed to the next step.  Part 4: Using Terraform and Ansible to deploy the Delivery ControllersThe VMs dedicated to holding the Delivery Controller™ roles are now ready for the deployment of the Delivery Controller components. This is a multi-step approach: Mount the CVAD ISO installer Copy the publicly signed SSL certificate to the DDCs Install all required server roles and features before installing the DDCs Change some necessary registry settings Therefore, we again use the well-proven combination of Terraform and Ansible. Terraform monitors each Ansible Playbook run and stops further steps if a Playbook fails. Example Terraform snippet: # Terraform Deployment of Citrix CVAD on XenServer 8 - 25.11 
## Definition of XenServer Provider Variables - not all available variables are defined, only these we use
### Reference https://registry.terraform.io/providers/xenserver/xenserver/latest/docs
#### Copy the required Ansible Assets to the Ansible Server
###### ***** IMPORTANT: You must set the permissions to the destination directory accordingly (sudo) chmod 777 otherwise the provisioning will fail *****
resource "null_resource" "CopyAnsibleDDCAssetsToAnsibleServer" {
  connection {
    type     = var.TACG-TMM-XS-CL-Ansible-Connection-Type
    user     = var.TACG-TMM-XS-CL-Ansible-Connection-User
    password = var.TACG-TMM-XS-CL-Ansible-Connection-Password
    host     = var.TACG-TMM-XS-CL-Ansible-Connection-HostIP
    port     = var.TACG-TMM-XS-CL-Ansible-Connection-Port
    https    = var.TACG-TMM-XS-CL-Ansible-Connection-HTTPS
    timeout  = var.TACG-TMM-XS-CL-Ansible-Connection-Timeout
  }

  # Copy vars-ddcs.yml file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-Vars-Source
    destination = var.TACG-TMM-XS-CL-Ansible-Vars-Destination
  }

  # Copy inventory.ini file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-Inventory-Source
    destination = var.TACG-TMM-XS-CL-Ansible-Inventory-Destination
  }

  # Copy InitialConfig Playbook file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-InitialPlaybookForDDCs-Source
    destination = var.TACG-TMM-XS-CL-Ansible-InitialPlaybookForDDCs-Destination
  }
}

#### Wait 10s for Backgroud Processes to Settle
resource "time_sleep" "wait_10_seconds" {
  depends_on      = [null_resource.CopyAnsibleDDCAssetsToAnsibleServer]
  create_duration = "10s"
}

##### Connect to Ansible Interpreter and run Initial Configuration Playbook on DDCs
resource "null_resource" "RunInitialPlaybook" {
  depends_on = [time_sleep.wait_10_seconds]

  provisioner "local-exec" {
    working_dir = "/etc/ansible"
    command     = var.TACG-TMM-XS-CL-Ansible-InitialCMDForDDCs
  }
}

##### Check Ansible Return Code
data "external" "CheckAnsibleReturnCode" {
  depends_on = [null_resource.RunInitialPlaybook]
  program    = ["bash", "/etc/ansible/assets/CheckAnsibleReturnCode.sh", "/etc/ansible/assets/ansible_exit_code.txt", "0"]
}

##### Terminate if Return Code is not 0
resource "null_resource" "IfExitCodeIsNotZeroThenHalt" {
  depends_on = [data.external.CheckAnsibleReturnCode]
  provisioner "local-exec" {
    interpreter = ["/bin/bash", "-c"]
    command     = "[ \"${data.external.CheckAnsibleReturnCode.result.found}\" = \"1\" ] &amp;&amp; echo \"Deploying CVAD: Everything OK\" || { echo \"Deploying the CVAD software and its prerequisites failed. Please review the settings. Halting Terraform.\"; exit 1; }"
  }
}
Example Ansible Playbook for deploying and configuring the DDCs and all needed further configurations: ---
# Terraform Deployment of Citrix CVAD on XenServer 8 - 25.11 
## For installation and configuration of the Delivery Controllers, StoreFront-Servers and WebStudio-Servers, we use Ansible Playbooks 
### Reference https://docs.ansible.com/ansible/latest/getting_started/index.html
#### We assume that the communication between the Ansible Server and all needed Hosts via WinRM is working as intended - check your Group Policies
##### All hosts and its corresponding communication-related Ansible variables are stored in assets/inventory.ini

###### Prepare the Delivery Controllers
- name: Configure the DDCs initially
  hosts: cvad-ddcs
  vars_files:
    - ./vars-ddcs.yml
  tasks:

  - name: Create Installer directory  
    ansible.windows.win_file: 
      path: "{{ installdir }}"
      state: directory

  - name: Create Log Folder
    ansible.windows.win_file:
      path: "{{ logdir }}"
      state: directory

  - name: Copy Citrix iso to the host
    ansible.windows.win_copy:
      src: "{{ sourceiso }}"
      dest: "{{ destiso }}" 
      force: no

  - name: Copy the SSL certificate to the hosts
    ansible.windows.win_copy:
      src: "{{ certificate_source_path }}"
      dest: "{{ certificate_destination_path }}" 
      force: no

  - name: Install Required Server Roles for Citrix
    ansible.windows.win_feature:
      name: "{{ ddc_req_roles }}"
      state: present
    register: requirements

  - name: Reboot when Required Roles need a restart
    ansible.windows.win_reboot:
      post_reboot_delay: 120
    when: requirements.reboot_required

  - name: Install SSL-Certificate on Servers
    ansible.windows.win_certificate_store:
      path: "{{ certificate_destination_path }}" 
      password: "{{ certificate_pfx_password }}"
      key_exportable: yes
      file_type: pkcs12
      store_location: LocalMachine
      key_storage: machine
      state: present
    become: true
    become_method: runas
    become_user: SYSTEM

  - name: Run installer from mounted ISO
    ansible.windows.win_package:
      path: "{{ install_exe }} "
      arguments: "{{ install_params }}"
      state: present
      expected_return_code: [0, 3, 3010]
      creates_path: C:\Program Files\Citrix\Desktop Studio
    register: ddc_install

  - name: Reboot after DDC
    ansible.windows.win_reboot:
    when: ddc_install.changed

  - name: Remove DDC RunOnce Key
    ansible.windows.win_regedit:
      path: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce
      name: "!XenDesktopSetup"
      state: absent
    register: ddc_resume

  - name: Resume DDC Install
    ansible.windows.win_package:
      path: C:\ProgramData\Citrix\XenDesktopSetup\XenDesktopServerSetup.exe
      state: present
      expected_return_code: [0, 3, 3010]
      creates_path: C:\Program Files\Citrix\Desktop Studio
    when: ddc_resume.changed

  - name: Alter unwanted Registry Keys-UserIEESC
    ansible.windows.win_regedit:
      path: HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A8-37EF-4b3f-8CFC-4F3A74704073}
      name: IsInstalled
      data: 0

  - name: Alter unwanted Registry Keys-AdminIEESC
    ansible.windows.win_regedit:
      path: HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A7-37EF-4b3f-8CFC-4F3A74704073}
      name: IsInstalled
      data: 0

  - name: Restart a service
    ansible.windows.win_service:
      name: CitrixHostService
      state: startedRunning the snippet should fulfill all needed steps: azadmin@tacg-cicd:/etc/terraform-tmm/CVADXS/_InstallCVAD$ terraform apply

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
  + create
 &lt;= read (data resources)

Terraform will perform the following actions:

  # data.external.CheckAnsibleReturnCode will be read during apply
  # (depends on a resource or a module with changes pending)
 &lt;= data "external" "CheckAnsibleReturnCode" {
      + id      = (known after apply)
      + program = [
          + "bash",
          + "/etc/ansible/assets/CheckAnsibleReturnCode.sh",
          + "/etc/ansible/assets/ansible_exit_code.txt",
          + "0",
        ]
      + result  = (known after apply)
    }

  # null_resource.CopyAnsibleDDCAssetsToAnsibleServer will be created
  + resource "null_resource" "CopyAnsibleDDCAssetsToAnsibleServer" {
      + id = (known after apply)
    }

  # null_resource.IfExitCodeIsNotZeroThenHalt will be created
  + resource "null_resource" "IfExitCodeIsNotZeroThenHalt" {
      + id = (known after apply)
    }

  # null_resource.RunInitialPlaybook will be created
  + resource "null_resource" "RunInitialPlaybook" {
      + id = (known after apply)
    }

  # time_sleep.wait_10_seconds will be created
  + resource "time_sleep" "wait_10_seconds" {
      + create_duration = "10s"
      + id              = (known after apply)
    }

Plan: 4 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.CopyAnsibleDDCAssetsToAnsibleServer: Creating...
null_resource.CopyAnsibleDDCAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyAnsibleDDCAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyAnsibleDDCAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyAnsibleDDCAssetsToAnsibleServer: Creation complete after 0s [id=3392935108841645613]
time_sleep.wait_10_seconds: Creating...
time_sleep.wait_10_seconds: Still creating... [00m10s elapsed]
time_sleep.wait_10_seconds: Creation complete after 10s [id=2025-11-12T07:13:29Z]
null_resource.RunInitialPlaybook: Creating...
null_resource.RunInitialPlaybook: Provisioning with 'local-exec'...
null_resource.RunInitialPlaybook (local-exec): Executing: ["/bin/sh" "-c" "ansible-playbook /etc/ansible/assets/InitialConfigurationForDDCs.ansible.yml -i /etc/ansible/assets/inventory.ini -v -c winrm; echo $? &gt; /etc/ansible/assets/ansible_exit_code.txt"]
null_resource.RunInitialPlaybook (local-exec): Using /etc/ansible/ansible.cfg as config file

null_resource.RunInitialPlaybook (local-exec): PLAY [Configure the DDCs initially] ********************************************

null_resource.RunInitialPlaybook (local-exec): TASK [Gathering Facts] *********************************************************
null_resource.RunInitialPlaybook (local-exec): ok: [tf-cvad-ddc1.the-austrian-citrix-guy.at]
null_resource.RunInitialPlaybook (local-exec): ok: [tf-cvad-ddc2.the-austrian-citrix-guy.at]

null_resource.RunInitialPlaybook (local-exec): TASK [Create Installer directory] **********************************************
null_resource.RunInitialPlaybook (local-exec): ok: [tf-cvad-ddc2.the-austrian-citrix-guy.at] =&gt; {"changed": false}
null_resource.RunInitialPlaybook (local-exec): ok: [tf-cvad-ddc1.the-austrian-citrix-guy.at] =&gt; {"changed": false}

null_resource.RunInitialPlaybook (local-exec): TASK [Create Log Folder] *******************************************************
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ddc1.the-austrian-citrix-guy.at] =&gt; {"changed": true}
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ddc2.the-austrian-citrix-guy.at] =&gt; {"changed": true}

null_resource.RunInitialPlaybook (local-exec): TASK [Copy Citrix iso to the host] *********************************************
null_resource.RunInitialPlaybook: Still creating... [00m10s elapsed]
null_resource.RunInitialPlaybook: Still creating... [00m20s elapsed]

...

null_resource.RunInitialPlaybook: Still creating... [12m10s elapsed]
null_resource.RunInitialPlaybook: Still creating... [12m20s elapsed]
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ddc1.the-austrian-citrix-guy.at] =&gt; {"changed": true, "checksum": null, "dest": "C:\\InstallCVAD\\Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso", "operation": "file_copy", "original_basename": "Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso", "size": 4124442624, "src": "/etc/ansible/isos/Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso"}
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ddc2.the-austrian-citrix-guy.at] =&gt; {"changed": true, "checksum": null, "dest": "C:\\InstallCVAD\\Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso", "operation": "file_copy", "original_basename": "Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso", "size": 4124442624, "src": "/etc/ansible/isos/Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso"}

null_resource.RunInitialPlaybook (local-exec): TASK [Copy the SSL certificate to the hosts] ***********************************
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ddc2.the-austrian-citrix-guy.at] =&gt; {"changed": true, "checksum": null, "dest": "C:\\InstallCVAD\\2025-wildcard.the-austrian-citrix-guy.at.PFX", "operation": "file_copy", "original_basename": "2025-wildcard.the-austrian-citrix-guy.at.PFX", "size": 3593, "src": "/etc/ansible/assets/2025-wildcard.the-austrian-citrix-guy.at.PFX"}
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ddc1.the-austrian-citrix-guy.at] =&gt; {"changed": true, "checksum": null, "dest": "C:\\InstallCVAD\\2025-wildcard.the-austrian-citrix-guy.at.PFX", "operation": "file_copy", "original_basename": "2025-wildcard.the-austrian-citrix-guy.at.PFX", "size": 3593, "src": "/etc/ansible/assets/2025-wildcard.the-austrian-citrix-guy.at.PFX"}

null_resource.RunInitialPlaybook (local-exec): TASK [Install Required Server Roles for Citrix] ********************************

null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ddc1.the-austrian-citrix-guy.at] =&gt; {"changed": true, "exitcode": "Success", "feature_result": [{"display_name": "Group Policy Management", "id": 69, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ASP.NET 4.8", "id": 429, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Remote Server Administration Tools", "id": 67, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "AD DS and AD LDS Tools", "id": 329, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "AD DS Tools", "id": 257, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "AD DS Snap-Ins and Command-Line Tools", "id": 299, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Role Administration Tools", "id": 256, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Application Development", "id": 147, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ASP.NET 4.8", "id": 413, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Common HTTP Features", "id": 141, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Default Document", "id": 143, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Directory Browsing", "id": 144, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Request Filtering", "id": 169, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Health and Diagnostics", "id": 155, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "HTTP Errors", "id": 145, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "HTTP Logging", "id": 156, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ISAPI Extensions", "id": 152, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ISAPI Filters", "id": 153, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "IIS Management Console", "id": 175, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Management Tools", "id": 174, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": ".NET Extensibility 4.8", "id": 414, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Performance", "id": 171, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Security", "id": 162, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Web Server (IIS)", "id": 2, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Static Content Compression", "id": 172, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Static Content", "id": 142, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Web Server", "id": 140, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Windows Authentication", "id": 164, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}], "reboot_required": false, "success": true}
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ddc2.the-austrian-citrix-guy.at] =&gt; {"changed": true, "exitcode": "Success", "feature_result": [{"display_name": "Group Policy Management", "id": 69, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ASP.NET 4.8", "id": 429, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Remote Server Administration Tools", "id": 67, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "AD DS and AD LDS Tools", "id": 329, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "AD DS Tools", "id": 257, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "AD DS Snap-Ins and Command-Line Tools", "id": 299, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Role Administration Tools", "id": 256, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Application Development", "id": 147, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ASP.NET 4.8", "id": 413, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Common HTTP Features", "id": 141, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Default Document", "id": 143, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Directory Browsing", "id": 144, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Request Filtering", "id": 169, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Health and Diagnostics", "id": 155, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "HTTP Errors", "id": 145, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "HTTP Logging", "id": 156, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ISAPI Extensions", "id": 152, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ISAPI Filters", "id": 153, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "IIS Management Console", "id": 175, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Management Tools", "id": 174, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": ".NET Extensibility 4.8", "id": 414, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Performance", "id": 171, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Security", "id": 162, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Web Server (IIS)", "id": 2, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Static Content Compression", "id": 172, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Static Content", "id": 142, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Web Server", "id": 140, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Windows Authentication", "id": 164, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}], "reboot_required": false, "success": true}

null_resource.RunInitialPlaybook (local-exec): TASK [Reboot when Required Roles need a restart] *******************************
null_resource.RunInitialPlaybook (local-exec): skipping: [tf-cvad-ddc1.the-austrian-citrix-guy.at] =&gt; {"changed": false, "false_condition": "requirements.reboot_required", "skip_reason": "Conditional result was False"}
null_resource.RunInitialPlaybook (local-exec): skipping: [tf-cvad-ddc2.the-austrian-citrix-guy.at] =&gt; {"changed": false, "false_condition": "requirements.reboot_required", "skip_reason": "Conditional result was False"}

null_resource.RunInitialPlaybook (local-exec): TASK [Install SSL-Certificate on Servers] **************************************
null_resource.RunInitialPlaybook: Still creating... [15m00s elapsed]
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ddc1.the-austrian-citrix-guy.at] =&gt; {"changed": true, "thumbprints": ["0BB52BDDEB78E6E8E32FF3B1949CAA462B8EC386"]}
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ddc2.the-austrian-citrix-guy.at] =&gt; {"changed": true, "thumbprints": ["0BB52BDDEB78E6E8E32FF3B1949CAA462B8EC386"]}

null_resource.RunInitialPlaybook (local-exec): TASK [Run installer from mounted ISO] ******************************************
null_resource.RunInitialPlaybook: Still creating... [15m10s elapsed]
null_resource.RunInitialPlaybook: Still creating... [15m20s elapsed]

...

null_resource.RunInitialPlaybook: Still creating... [25m10s elapsed]
null_resource.RunInitialPlaybook: Still creating... [25m20s elapsed]
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ddc1.the-austrian-citrix-guy.at] =&gt; {"changed": true, "checksum": "EADE21C7B09DED5626F3BB51D4DF7A4C0E7AC607", "rc": 0, "reboot_required": false}
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ddc2.the-austrian-citrix-guy.at] =&gt; {"changed": true, "checksum": "EADE21C7B09DED5626F3BB51D4DF7A4C0E7AC607", "rc": 0, "reboot_required": false}

null_resource.RunInitialPlaybook (local-exec): TASK [Reboot after DDC] ********************************************************
null_resource.RunInitialPlaybook: Still creating... [25m30s elapsed]
null_resource.RunInitialPlaybook: Still creating... [25m40s elapsed]
null_resource.RunInitialPlaybook: Still creating... [25m50s elapsed]
null_resource.RunInitialPlaybook: Still creating... [26m00s elapsed]
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ddc1.the-austrian-citrix-guy.at] =&gt; {"changed": true, "elapsed": 34, "rebooted": true, "unreachable": false}
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ddc2.the-austrian-citrix-guy.at] =&gt; {"changed": true, "elapsed": 43, "rebooted": true, "unreachable": false}

null_resource.RunInitialPlaybook (local-exec): TASK [Remove DDC RunOnce Key] **************************************************
null_resource.RunInitialPlaybook (local-exec): ok: [tf-cvad-ddc2.the-austrian-citrix-guy.at] =&gt; {"changed": false, "data_changed": false, "data_type_changed": false}
null_resource.RunInitialPlaybook (local-exec): ok: [tf-cvad-ddc1.the-austrian-citrix-guy.at] =&gt; {"changed": false, "data_changed": false, "data_type_changed": false}

null_resource.RunInitialPlaybook (local-exec): TASK [Resume DDC Install] ******************************************************
null_resource.RunInitialPlaybook (local-exec): skipping: [tf-cvad-ddc1.the-austrian-citrix-guy.at] =&gt; {"changed": false, "false_condition": "ddc_resume.changed", "skip_reason": "Conditional result was False"}
null_resource.RunInitialPlaybook (local-exec): skipping: [tf-cvad-ddc2.the-austrian-citrix-guy.at] =&gt; {"changed": false, "false_condition": "ddc_resume.changed", "skip_reason": "Conditional result was False"}

null_resource.RunInitialPlaybook (local-exec): TASK [Alter unwanted Registry Keys-UserIEESC] **********************************
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ddc1.the-austrian-citrix-guy.at] =&gt; {"changed": true, "data_changed": false, "data_type_changed": true}
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ddc2.the-austrian-citrix-guy.at] =&gt; {"changed": true, "data_changed": false, "data_type_changed": true}

null_resource.RunInitialPlaybook (local-exec): TASK [Alter unwanted Registry Keys-AdminIEESC] *********************************
null_resource.RunInitialPlaybook: Still creating... [26m10s elapsed]
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ddc1.the-austrian-citrix-guy.at] =&gt; {"changed": true, "data_changed": false, "data_type_changed": true}
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ddc2.the-austrian-citrix-guy.at] =&gt; {"changed": true, "data_changed": false, "data_type_changed": true}

null_resource.RunInitialPlaybook (local-exec): PLAY RECAP *********************************************************************
null_resource.RunInitialPlaybook (local-exec): tf-cvad-ddc1.the-austrian-citrix-guy.at : ok=12   changed=9    unreachable=0    failed=0    skipped=2    rescued=0    ignored=0
null_resource.RunInitialPlaybook (local-exec): tf-cvad-ddc2.the-austrian-citrix-guy.at : ok=12   changed=9    unreachable=0    failed=0    skipped=2    rescued=0    ignored=0

null_resource.RunInitialPlaybook: Creation complete after 26m11s [id=3927491137125445482]
data.external.CheckAnsibleReturnCode: Reading...
data.external.CheckAnsibleReturnCode: Read complete after 0s [id=-]
null_resource.IfExitCodeIsNotZeroThenHalt: Creating...
null_resource.IfExitCodeIsNotZeroThenHalt: Provisioning with 'local-exec'...
null_resource.IfExitCodeIsNotZeroThenHalt (local-exec): Executing: ["/bin/bash" "-c" "[ \"1\" = \"1\" ] &amp;&amp; echo \"Deploying CVAD: Everything OK\" || { echo \"Deploying the CVAD software and its prerequisites failed. Please review the settings. Halting Terraform.\"; exit 1; }"]
null_resource.IfExitCodeIsNotZeroThenHalt (local-exec): Deploying CVAD: Everything OK
null_resource.IfExitCodeIsNotZeroThenHalt: Creation complete after 0s [id=8978265277076340502]

Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
azadmin@tacg-cicd:/etc/terraform-tmm/CVADXS/_InstallCVAD$ After successful deployment, the CVAD software is installed and can be configured later. Caution We have noticed that in some environments, the Citrix Host Service does not start after the initial reboot, even though it is set to Automatic start. Therefore, we added a new task to the Ansible Playbook to ensure that the Citrix Host Service starts after the initial reboot and before the following configuration steps. Since the CVAD software and its dependencies were successfully deployed, we can proceed to the next step.  Part 5: Using Terraform and Ansible to deploy the StoreFront serverThe VM dedicated to holding the StoreFront™ role is now ready for the deployment of the StoreFront components. This is a multi-step approach: Mount the CVAD ISO installer Copy the publicly signed SSL certificate to the DDCs Install all required server roles and features before installing the DDCs Change some necessary registry settings Therefore, we again use the well-proven combination of Terraform and Ansible. Initially, no Citrix software is installed on the VMs. Terraform monitors each Ansible Playbook run and stops further steps if a Playbook fails. Example Terraform snippet: # Terraform Deployment of Citrix CVAD on XenServer 8 - 25.11 
## Definition of XenServer Provider Variables - not all available variables are defined, only these we use
### Reference https://registry.terraform.io/providers/xenserver/xenserver/latest/docs
#### Copy the required Ansible Assets for installing StoreFront to the Ansible Server
###### ***** IMPORTANT: You must set the permissions to the destination directory accordingly (sudo) chmod 777 otherwise the provisioning will fail *****
resource "null_resource" "CopyAnsibleSFAssetsToAnsibleServer" {
  connection {
    type     = var.TACG-TMM-XS-CL-Ansible-Connection-Type
    user     = var.TACG-TMM-XS-CL-Ansible-Connection-User
    password = var.TACG-TMM-XS-CL-Ansible-Connection-Password
    host     = var.TACG-TMM-XS-CL-Ansible-Connection-HostIP
    port     = var.TACG-TMM-XS-CL-Ansible-Connection-Port
    https    = var.TACG-TMM-XS-CL-Ansible-Connection-HTTPS
    timeout  = var.TACG-TMM-XS-CL-Ansible-Connection-Timeout
  }

  # Copy vars-sf.yml file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-Vars-Source
    destination = var.TACG-TMM-XS-CL-Ansible-Vars-Destination
  }

  # Copy inventory.ini file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-Inventory-Source
    destination = var.TACG-TMM-XS-CL-Ansible-Inventory-Destination
  }

  # Copy InitialConfig Playbook file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-InitialPlaybookForSF-Source
    destination = var.TACG-TMM-XS-CL-Ansible-InitialPlaybookForSF-Destination
  }
}

#### Wait 10s for Backgroud Processes to Settle
resource "time_sleep" "wait_10_seconds" {
  depends_on      = [null_resource.CopyAnsibleSFAssetsToAnsibleServer]
  create_duration = "10s"
}

##### Connect to Ansible Interpreter and run Initial Configuration Playbook on SF
resource "null_resource" "RunInitialPlaybook" {
  depends_on = [time_sleep.wait_10_seconds]

  provisioner "local-exec" {
    working_dir = "/etc/ansible"
    command     = var.TACG-TMM-XS-CL-Ansible-InitialCMDForSF
  }
}

##### Check Ansible Return Code
data "external" "CheckAnsibleReturnCode" {
  depends_on = [null_resource.RunInitialPlaybook]
  program    = ["bash", "/etc/ansible/assets/CheckAnsibleReturnCode.sh", "/etc/ansible/assets/ansible_exit_code.txt", "0"]
}

##### Terminate if Return Code is not 0
resource "null_resource" "IfExitCodeIsNotZeroThenHalt" {
  depends_on = [data.external.CheckAnsibleReturnCode]
  provisioner "local-exec" {
    interpreter = ["/bin/bash", "-c"]
    command     = "[ \"${data.external.CheckAnsibleReturnCode.result.found}\" = \"1\" ] &amp;&amp; echo \"Deploying StoreFront - Everything OK\" || { echo \"Deploying the StoreFront software and/or its prerequisites failed. Please review the settings. Halting Terraform.\"; exit 1; }"
  }
}Example Ansible Playbook for deploying and configuring Storefront and all needed further configurations: # Terraform Deployment of Citrix CVAD on XenServer 8 - 25.11 
## For installation and configuration of the Delivery Controllers, StoreFront-Servers and WebStudio-Servers, we use Ansible Playbooks 
### Reference https://docs.ansible.com/ansible/latest/getting_started/index.html
#### We assume that the communication between the Ansible Server and all needed Hosts via WinRM is working as intended - check your Group Policies
##### All hosts and its corresponding communication-related Ansible variables are stored in assets/inventory.ini

###### Prepare the StoreFront Server
- name: Configure the StoreFront Server initially
  hosts: cvad-sf
  vars_files:
    - ./vars-sf.yml
  tasks:

  - name: Create Installer directory  
    ansible.windows.win_file: 
      path: "{{ installdir }}"
      state: directory

  - name: Create Log Folder
    ansible.windows.win_file:
      path: "{{ logdir }}"
      state: directory

  - name: Alter unwanted Registry Keys-UserIEESC
    ansible.windows.win_regedit:
      path: HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A8-37EF-4b3f-8CFC-4F3A74704073}
      name: IsInstalled
      data: 0

  - name: Alter unwanted Registry Keys-AdminIEESC
    ansible.windows.win_regedit:
      path: HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A7-37EF-4b3f-8CFC-4F3A74704073}
      name: IsInstalled
      data: 0

  - name: Copy Citrix iso to the host
    ansible.windows.win_copy:
      src: "{{ sourceiso }}"
      dest: "{{ destiso }}" 
      force: no

  - name: Copy the SSL certificate to the hosts
    ansible.windows.win_copy:
      src: "{{ certificate_source_path }}"
      dest: "{{ certificate_destination_path }}" 
      force: no

  - name: Install Required Server Roles for Citrix
    ansible.windows.win_feature:
      name: "{{ sf_req_roles }}"
      state: present
    register: requirements
    become: true
    become_method: runas
    become_user: SYSTEM

  - name: Reboot when Required Roles need a restart
    ansible.windows.win_reboot:
      post_reboot_delay: 120
    when: requirements.reboot_required

  - name: Install SSL-Certificate on Servers
    ansible.windows.win_certificate_store:
      path: "{{ certificate_destination_path }}" 
      password: "{{ certificate_pfx_password }}"
      key_exportable: yes
      file_type: pkcs12
      store_location: LocalMachine
      key_storage: machine
      state: present
    become: true
    become_method: runas
    become_user: SYSTEM

  - name: Create database server record
    ansible.windows.win_dns_record:
      computer_name: "{{ sf_dns_ip }}"
      name: "{{ sf_dns_name }}"
      type: "A"
      value: "{{ sf_dns_value }}"
      zone: "{{ sf_dns_zone }}"
      state: present
    become: true
    become_method: runas
    become_user: SYSTEM  

  - name: Run installer from mounted ISO
    ansible.windows.win_package:
      path: "{{ install_exe }} "
      arguments: "{{ install_params }}"
      state: present
      expected_return_code: [0, 3, 4, 3010]
      creates_path: C:\ProgramData\Citrix\Storefront Install
    register: sf_install
    become: true
    become_method: runas
    become_user: "{{ ansible_domainuser }}"

  - name: Reboot after sf
    ansible.windows.win_reboot:
    when: sf_install.changed
Running the snippet should fulfill all required steps: At first, all missing Windows features are installed. azadmin@tacg-cicd:/etc/terraform-tmm/CVADXS/_InstallStoreFront$ terraform apply

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create
 &lt;= read (data resources)

Terraform will perform the following actions:

  # data.external.CheckAnsibleReturnCode will be read during apply
  # (depends on a resource or a module with changes pending)
 &lt;= data "external" "CheckAnsibleReturnCode" {
      + id      = (known after apply)
      + program = [
          + "bash",
          + "/etc/ansible/assets/CheckAnsibleReturnCode.sh",
          + "/etc/ansible/assets/ansible_exit_code.txt",
          + "0",
        ]
      + result  = (known after apply)
    }

  # null_resource.CopyAnsibleSFAssetsToAnsibleServer will be created
  + resource "null_resource" "CopyAnsibleSFAssetsToAnsibleServer" {
      + id = (known after apply)
    }

  # null_resource.IfExitCodeIsNotZeroThenHalt will be created
  + resource "null_resource" "IfExitCodeIsNotZeroThenHalt" {
      + id = (known after apply)
    }

  # null_resource.RunInitialPlaybook will be created
  + resource "null_resource" "RunInitialPlaybook" {
      + id = (known after apply)
    }

  # time_sleep.wait_10_seconds will be created
  + resource "time_sleep" "wait_10_seconds" {
      + create_duration = "10s"
      + id              = (known after apply)
    }

Plan: 4 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.CopyAnsibleSFAssetsToAnsibleServer: Creating...
null_resource.CopyAnsibleSFAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyAnsibleSFAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyAnsibleSFAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyAnsibleSFAssetsToAnsibleServer: Creation complete after 0s [id=3119681656948818723]
time_sleep.wait_10_seconds: Creating...
time_sleep.wait_10_seconds: Still creating... [00m10s elapsed]
time_sleep.wait_10_seconds: Creation complete after 10s [id=2025-11-04T16:59:29Z]
null_resource.RunInitialPlaybook: Creating...
null_resource.RunInitialPlaybook: Provisioning with 'local-exec'...
null_resource.RunInitialPlaybook (local-exec): Executing: ["/bin/sh" "-c" "ansible-playbook /etc/ansible/assets/InitialConfigurationForStoreFront.ansible.yml -i /etc/ansible/assets/inventory.ini -v -c winrm; echo $? &gt; /etc/ansible/assets/ansible_exit_code.txt"]
null_resource.RunInitialPlaybook (local-exec): Using /etc/ansible/ansible.cfg as config file

null_resource.RunInitialPlaybook (local-exec): PLAY [Configure the StoreFront Server initially] *******************************

null_resource.RunInitialPlaybook (local-exec): TASK [Gathering Facts] *********************************************************
null_resource.RunInitialPlaybook (local-exec): ok: [tf-cvad-sf.the-austrian-citrix-guy.at]

null_resource.RunInitialPlaybook (local-exec): TASK [Create Installer directory] **********************************************
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-sf.the-austrian-citrix-guy.at] =&gt; {"changed": true}

null_resource.RunInitialPlaybook (local-exec): TASK [Create Log Folder] *******************************************************
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-sf.the-austrian-citrix-guy.at] =&gt; {"changed": true}

null_resource.RunInitialPlaybook (local-exec): TASK [Alter unwanted Registry Keys-UserIEESC] **********************************
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-sf.the-austrian-citrix-guy.at] =&gt; {"changed": true, "data_changed": false, "data_type_changed": true}

null_resource.RunInitialPlaybook (local-exec): TASK [Alter unwanted Registry Keys-AdminIEESC] *********************************
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-sf.the-austrian-citrix-guy.at] =&gt; {"changed": true, "data_changed": false, "data_type_changed": true}

null_resource.RunInitialPlaybook (local-exec): TASK [Copy Citrix iso to the host] *********************************************
null_resource.RunInitialPlaybook: Still creating... [00m10s elapsed]
null_resource.RunInitialPlaybook: Still creating... [00m20s elapsed]
...
null_resource.RunInitialPlaybook: Still creating... [09m50s elapsed]
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-sf.the-austrian-citrix-guy.at] =&gt; {"changed": true, "checksum": null, "dest": "C:\\InstallCVAD\\Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso", "operation": "file_copy", "original_basename": "Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso", "size": 4124442624, "src": "/etc/ansible/isos/Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso"}

null_resource.RunInitialPlaybook (local-exec): TASK [Copy the SSL certificate to the hosts] ***********************************
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-sf.the-austrian-citrix-guy.at] =&gt; {"changed": true, "checksum": null, "dest": "C:\\InstallCVAD\\2025-wildcard.the-austrian-citrix-guy.at.PFX", "operation": "file_copy", "original_basename": "2025-wildcard.the-austrian-citrix-guy.at.PFX", "size": 3593, "src": "/etc/ansible/assets/2025-wildcard.the-austrian-citrix-guy.at.PFX"}

null_resource.RunInitialPlaybook (local-exec): TASK [Install Required Server Roles for Citrix] ********************************
null_resource.RunInitialPlaybook: Still creating... [10m00s elapsed]
null_resource.RunInitialPlaybook: Still creating... [10m10s elapsed]
...
null_resource.RunInitialPlaybook: Still creating... [19m00s elapsed]
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-sf.the-austrian-citrix-guy.at] =&gt; {"changed": true, "exitcode": "Success", "feature_result": [{"display_name": "Message Queuing", "id": 49, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Message Queuing Server", "id": 191, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Message Queuing Services", "id": 190, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ASP.NET 4.8", "id": 429, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": ".NET Framework 3.5 (includes .NET 2.0 and 3.0)", "id": 220, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": ".NET Framework 3.5 Features", "id": 475, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "HTTP Activation", "id": 421, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Message Queuing (MSMQ) Activation", "id": 422, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Named Pipe Activation", "id": 423, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "TCP Activation", "id": 424, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Telnet Client", "id": 44, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Windows Process Activation Service", "id": 41, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Configuration APIs", "id": 217, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Process Model", "id": 219, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Application Development", "id": 147, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Application Initialization", "id": 445, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ASP", "id": 150, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ASP.NET 4.8", "id": 413, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Basic Authentication", "id": 163, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "CGI", "id": 151, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Common HTTP Features", "id": 141, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Default Document", "id": 143, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Directory Browsing", "id": 144, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Dynamic Content Compression", "id": 173, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Request Filtering", "id": 169, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Health and Diagnostics", "id": 155, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "HTTP Errors", "id": 145, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "HTTP Logging", "id": 156, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "HTTP Redirection", "id": 146, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Tracing", "id": 159, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Server Side Includes", "id": 154, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ISAPI Extensions", "id": 152, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ISAPI Filters", "id": 153, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Logging Tools", "id": 157, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "IIS 6 Metabase Compatibility", "id": 179, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "IIS 6 Management Compatibility", "id": 178, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "IIS Management Console", "id": 175, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Management Tools", "id": 174, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": ".NET Extensibility 4.8", "id": 414, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Performance", "id": 171, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "IIS Management Scripts and Tools", "id": 176, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Security", "id": 162, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Web Server (IIS)", "id": 2, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Static Content Compression", "id": 172, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Static Content", "id": 142, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Web Server", "id": 140, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Windows Authentication", "id": 164, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "IIS 6 WMI Compatibility", "id": 180, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}], "reboot_required": false, "success": true}

null_resource.RunInitialPlaybook (local-exec): TASK [Reboot when Required Roles need a restart] *******************************
null_resource.RunInitialPlaybook (local-exec): skipping: [tf-cvad-sf.the-austrian-citrix-guy.at] =&gt; {"changed": false, "false_condition": "requirements.reboot_required", "skip_reason": "Conditional result was False"}

null_resource.RunInitialPlaybook (local-exec): TASK [Install SSL-Certificate on Servers] **************************************
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-sf.the-austrian-citrix-guy.at] =&gt; {"changed": true, "thumbprints": ["0BB52BDDEB78E6E8E32FF3B1949CAA462B8EC386"]}

null_resource.RunInitialPlaybook (local-exec): TASK [Run installer from mounted ISO] ******************************************
null_resource.RunInitialPlaybook: Still creating... [19m10s elapsed]
null_resource.RunInitialPlaybook: Still creating... [19m20s elapsed]
null_resource.RunInitialPlaybook: Still creating... [19m30s elapsed]
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-sf.the-austrian-citrix-guy.at] =&gt; {"changed": true, "checksum": "22A6C5D5BB516ABD6DF5DC6E6CA0C0CEFBB19082", "rc": 3010, "reboot_required": true}

null_resource.RunInitialPlaybook (local-exec): TASK [Reboot after sf] *********************************************************
null_resource.RunInitialPlaybook: Still creating... [19m40s elapsed]
null_resource.RunInitialPlaybook: Still creating... [19m50s elapsed]
null_resource.RunInitialPlaybook: Still creating... [20m00s elapsed]
null_resource.RunInitialPlaybook: Still creating... [20m10s elapsed]
null_resource.RunInitialPlaybook: Still creating... [20m20s elapsed]
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-sf.the-austrian-citrix-guy.at] =&gt; {"changed": true, "elapsed": 42, "rebooted": true, "unreachable": false}

null_resource.RunInitialPlaybook (local-exec): PLAY RECAP *********************************************************************
null_resource.RunInitialPlaybook (local-exec): tf-cvad-sf.the-austrian-citrix-guy.at : ok=11   changed=10   unreachable=0    failed=0    skipped=1    rescued=0    ignored=0

null_resource.RunInitialPlaybook: Creation complete after 20m24s [id=7847329303251523226]
data.external.CheckAnsibleReturnCode: Reading...
data.external.CheckAnsibleReturnCode: Read complete after 0s [id=-]
null_resource.IfExitCodeIsNotZeroThenHalt: Creating...
null_resource.IfExitCodeIsNotZeroThenHalt: Provisioning with 'local-exec'...
null_resource.IfExitCodeIsNotZeroThenHalt (local-exec): Executing: ["/bin/bash" "-c" "[ \"1\" = \"1\" ] &amp;&amp; echo \"Deploying StoreFront - Everything OK\" || { echo \"Deploying the StoreFront software and/or its prerequisites failed. Please review the settings. Halting Terraform.\"; exit 1; }"]
null_resource.IfExitCodeIsNotZeroThenHalt (local-exec): Deploying StoreFront - Everything OK
null_resource.IfExitCodeIsNotZeroThenHalt: Creation complete after 0s [id=6843415180162545622]

Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
azadmin@tacg-cicd:/etc/terraform-tmm/CVADXS/_InstallStoreFront$ After successful deployment, the StoreFront software is installed and can be configured later.  Since the StoreFront software and its dependencies were successfully deployed, we can proceed to the next step. Part 6: Using Terraform and Ansible to deploy the Web Studio ServerThe VM dedicated to holding the Web Studio role is now ready for the deployment of the Web Studio components. This is a multi-step approach: Mount the CVAD ISO installer Copy the publicly signed SSL certificate to the Web Studio server Install all required server roles and features before installing the Web Studio server Change some necessary registry settings Configure the Web Studio server as a proxy for the DDCs Therefore, we again use the well-proven combination of Terraform and Ansible. Initially, no Citrix software is installed on the VM again. Terraform monitors each Ansible Playbook run and stops further steps if a Playbook fails. Example Terraform snippet: # Terraform Deployment of Citrix CVAD on XenServer 8 - 25.11 
## Definition of XenServer Provider Variables - not all available variables are defined, only these we use
### Reference https://registry.terraform.io/providers/xenserver/xenserver/latest/docs
#### Copy the required Ansible Assets for installing WebStudio to the Ansible Server
###### ***** IMPORTANT: You must set the permissions to the destination directory accordingly (sudo) chmod 777 otherwise the provisioning will fail *****
resource "null_resource" "CopyAnsibleWSAssetsToAnsibleServer" {
  connection {
    type     = var.TACG-TMM-XS-CL-Ansible-Connection-Type
    user     = var.TACG-TMM-XS-CL-Ansible-Connection-User
    password = var.TACG-TMM-XS-CL-Ansible-Connection-Password
    host     = var.TACG-TMM-XS-CL-Ansible-Connection-HostIP
  }

  # Copy vars-ws.yml file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-Vars-Source
    destination = var.TACG-TMM-XS-CL-Ansible-Vars-Destination
  }

  # Copy inventory.ini file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-Inventory-Source
    destination = var.TACG-TMM-XS-CL-Ansible-Inventory-Destination
  }

  # Copy InitialConfig Playbook file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-InitialPlaybookForWS-Source
    destination = var.TACG-TMM-XS-CL-Ansible-InitialPlaybookForWS-Destination
  }

  # Copy SetProxy Playbook file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-SetProxyPlaybookWS-Source
    destination = var.TACG-TMM-XS-CL-Ansible-InitialPlaybookForWS-Destination
  }
}

#### Wait 10s for Backgroud Processes to Settle
resource "time_sleep" "wait_10_seconds" {
  depends_on      = [null_resource.CopyAnsibleWSAssetsToAnsibleServer]
  create_duration = "10s"
}

#### Run initial configuration
##### Connect to Ansible Interpreter and run Initial Configuration Playbook on WS
resource "null_resource" "RunInitialPlaybook" {
  depends_on = [time_sleep.wait_10_seconds]

  provisioner "local-exec" {
    working_dir = "/etc/ansible"
    command     = var.TACG-TMM-XS-CL-Ansible-InitialCMDForWS
  }
}

##### Check Ansible Return Code
data "external" "CheckAnsibleReturnCode" {
  depends_on = [null_resource.RunInitialPlaybook]
  program    = ["bash", "/etc/ansible/assets/CheckAnsibleReturnCode.sh", "/etc/ansible/assets/ansible_exit_code.txt", "0"]
}

##### Terminate if Return Code is not 0
resource "null_resource" "IfExitCodeIsNotZeroThenHalt" {
  depends_on = [data.external.CheckAnsibleReturnCode]
  provisioner "local-exec" {
    interpreter = ["/bin/bash", "-c"]
    command     = "[ \"${data.external.CheckAnsibleReturnCode.result.found}\" = \"1\" ] &amp;&amp; echo \"Deploying WebStudio: Everything OK\" || { echo \"Deploying the WebStudio software and/or its prerequisites failed. Please review the settings. Halting Terraform.\"; exit 1; }"
  }
}

#### Wait 10s for Backgroud Processes to Settle
resource "time_sleep" "wait_10_seconds_IC" {
  depends_on      = [null_resource.IfExitCodeIsNotZeroThenHalt]
  create_duration = "10s"
}
##### End of intitial configuration

#### Run SetProxy configuration
##### Connect to Ansible Interpreter and run SetProxy Playbook on WS
resource "null_resource" "RunSetProxyConfigurationPlaybook" {
  depends_on = [time_sleep.wait_10_seconds_IC]

  provisioner "local-exec" {
    working_dir = "/etc/ansible"
    command     = var.TACG-TMM-XS-CL-Ansible-SetProxyPlaybookWS
  }
}

##### Check Ansible Return Code
data "external" "CheckAnsibleReturnCodePC" {
  depends_on = [null_resource.RunSetProxyConfigurationPlaybook]
  program    = ["bash", "/etc/ansible/assets/CheckAnsibleReturnCode.sh", "/etc/ansible/assets/ansible_exit_code.txt", "0"]
}

##### Terminate if Return Code is not 0
resource "null_resource" "IfExitCodeIsNotZeroThenHaltPC" {
  depends_on = [data.external.CheckAnsibleReturnCodePC]
  provisioner "local-exec" {
    interpreter = ["/bin/bash", "-c"]
    command     = "[ \"${data.external.CheckAnsibleReturnCodePC.result.found}\" = \"1\" ] &amp;&amp; echo \"Configuring WebStudio as Proxy: Everything OK\" || { echo \"Configuring WebStudio as Proxy failed. Please review the settings. Halting Terraform.\"; exit 1; }"
  }
}

#### Wait 10s for Backgroud Processes to Settle
resource "time_sleep" "wait_10_seconds_IC" {
  depends_on      = [null_resource.IfExitCodeIsNotZeroThenHalt]
  create_duration = "10s"
}
##### End of intitial configuration
Example Ansible Playbook for deploying and configuring WebStudio and all needed further configurations: # Terraform Deployment of Citrix CVAD on XenServer 8 - 25.11 
## For installation and configuration of the Delivery Controllers, StoreFront-Servers and WebStudio-Servers, we use Ansible Playbooks 
### Reference https://docs.ansible.com/ansible/latest/getting_started/index.html
#### We assume that the communication between the Ansible Server and all needed Hosts via WinRM is working as intended - check your Group Policies
##### All hosts and its corresponding communication-related Ansible variables are stored in assets/inventory.ini

###### Prepare the WebStudio Server
- name: Configure the WebStudio Server initially
  hosts: cvad-ws
  vars_files:
    - ./vars-ws.yml
  tasks:

  - name: Create Installer directory  
    ansible.windows.win_file: 
      path: "{{ installdir }}"
      state: directory

  - name: Create Log Folder
    ansible.windows.win_file:
      path: "{{ logdir }}"
      state: directory

  - name: Alter unwanted Registry Keys-UserIEESC
    ansible.windows.win_regedit:
      path: HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A8-37EF-4b3f-8CFC-4F3A74704073}
      name: IsInstalled
      data: 0

  - name: Alter unwanted Registry Keys-AdminIEESC
    ansible.windows.win_regedit:
      path: HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A7-37EF-4b3f-8CFC-4F3A74704073}
      name: IsInstalled
      data: 0

  - name: Copy Citrix iso to the host
    ansible.windows.win_copy:
      src: "{{ sourceiso }}"
      dest: "{{ destiso }}" 
      force: no

  - name: Copy the SSL certificate to the hosts
    ansible.windows.win_copy:
      src: "{{ certificate_source_path }}"
      dest: "{{ certificate_destination_path }}" 
      force: no

  - name: Install Required Server Roles for Citrix
    ansible.windows.win_feature:
      name: "{{ ws_req_roles }}"
      state: present
    register: requirements
    become: true
    become_method: runas
    become_user: SYSTEM

  - name: Reboot when Required Roles need a restart
    ansible.windows.win_reboot:
      post_reboot_delay: 120
    when: requirements.reboot_required

  - name: Install SSL-Certificate on Servers
    ansible.windows.win_certificate_store:
      path: "{{ certificate_destination_path }}" 
      password: "{{ certificate_pfx_password }}"
      key_exportable: yes
      file_type: pkcs12
      store_location: LocalMachine
      key_storage: machine
      state: present
    become: true
    become_method: runas
    become_user: SYSTEM

  - name: Run installer from mounted ISO
    ansible.windows.win_package:
      path: "{{ install_exe }} "
      arguments: "{{ install_params }}"
      state: present
      expected_return_code: [0, 3, 4, 3010]
      creates_path: C:\ProgramData\Citrix\Storefront Install
    register: ws_install
    become: true
    become_method: runas
    become_user: "{{ ansible_domainuser }}"

  - name: Reboot after ws
    ansible.windows.win_reboot:
    when: ws_install.changedExample Ansible Playbook for configuring Web Studio as Proxy: # Terraform Deployment of Citrix CVAD on XenServer 8 - 25.11 
## For installation and configuration of the Delivery Controllers, StoreFront-Servers and WebStudio-Servers, we use Ansible Playbooks 
### Reference https://docs.ansible.com/ansible/latest/getting_started/index.html
#### We assume that the communication between the Ansible Server and all needed Hosts via WinRM is working as intended - check your Group Policies
##### All hosts and its corresponding communication-related Ansible variables are stored in assets/inventory.ini
###### Configure WS as Proxy
- name: Configure WebStudio as Proxy
  hosts: cvad-ws
  gather_facts: no

  tasks:
    - name: Run PS to enable Proxy Mode
      ansible.windows.win_powershell:
        script: |
          &amp; "c:\Program Files\Citrix\Web Studio\Tool\StudioConfig.exe" --enableproxy --proxyserver "tf-cvad-ws.the-austrian-citrix-guy.at"
      register: runps

    - name: Print results
      ansible.builtin.debug:
        var: runps
Running the snippet should fulfill all needed steps: azadmin@tacg-cicd:/etc/terraform-tmm/CVADXS/_InstallWebStudio$ terraform apply

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
  + create
 &lt;= read (data resources)

Terraform will perform the following actions:

  # data.external.CheckAnsibleReturnCode will be read during apply
  # (depends on a resource or a module with changes pending)
 &lt;= data "external" "CheckAnsibleReturnCode" {
      + id      = (known after apply)
      + program = [
          + "bash",
          + "/etc/ansible/assets/CheckAnsibleReturnCode.sh",
          + "/etc/ansible/assets/ansible_exit_code.txt",
          + "0",
        ]
      + result  = (known after apply)
    }

  # null_resource.CopyAnsibleWSAssetsToAnsibleServer will be created
  + resource "null_resource" "CopyAnsibleWSAssetsToAnsibleServer" {
      + id = (known after apply)
    }

  # null_resource.IfExitCodeIsNotZeroThenHalt will be created
  + resource "null_resource" "IfExitCodeIsNotZeroThenHalt" {
      + id = (known after apply)
    }

  # null_resource.RunInitialPlaybook will be created
  + resource "null_resource" "RunInitialPlaybook" {
      + id = (known after apply)
    }

  # time_sleep.wait_10_seconds will be created
  + resource "time_sleep" "wait_10_seconds" {
      + create_duration = "10s"
      + id              = (known after apply)
    }

Plan: 4 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.CopyAnsibleWSAssetsToAnsibleServer: Creating...
null_resource.CopyAnsibleWSAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyAnsibleWSAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyAnsibleWSAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyAnsibleWSAssetsToAnsibleServer: Creation complete after 1s [id=8218302926752497323]
time_sleep.wait_10_seconds: Creating...
time_sleep.wait_10_seconds: Still creating... [00m10s elapsed]
time_sleep.wait_10_seconds: Creation complete after 10s [id=2025-11-12T07:52:23Z]
null_resource.RunInitialPlaybook: Creating...
null_resource.RunInitialPlaybook: Provisioning with 'local-exec'...
null_resource.RunInitialPlaybook (local-exec): Executing: ["/bin/sh" "-c" "ansible-playbook /etc/ansible/assets/InitialConfigurationForWS.ansible.yml -i /etc/ansible/assets/inventory.ini -v -c winrm; echo $? &gt; /etc/ansible/assets/ansible_exit_code.txt"]

null_resource.RunInitialPlaybook (local-exec): Using /etc/ansible/ansible.cfg as config file

null_resource.RunInitialPlaybook (local-exec): PLAY [Configure the WebStudio Server initially] ********************************

null_resource.RunInitialPlaybook (local-exec): TASK [Gathering Facts] *********************************************************
null_resource.RunInitialPlaybook (local-exec): ok: [tf-cvad-ws.the-austrian-citrix-guy.at]

null_resource.RunInitialPlaybook (local-exec): TASK [Create Installer directory] **********************************************
null_resource.RunInitialPlaybook (local-exec): ok: [tf-cvad-ws.the-austrian-citrix-guy.at] =&gt; {"changed": false}

null_resource.RunInitialPlaybook (local-exec): TASK [Create Log Folder] *******************************************************
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ws.the-austrian-citrix-guy.at] =&gt; {"changed": true}

null_resource.RunInitialPlaybook (local-exec): TASK [Alter unwanted Registry Keys-UserIEESC] **********************************
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ws.the-austrian-citrix-guy.at] =&gt; {"changed": true, "data_changed": false, "data_type_changed": true}

null_resource.RunInitialPlaybook (local-exec): TASK [Alter unwanted Registry Keys-AdminIEESC] *********************************
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ws.the-austrian-citrix-guy.at] =&gt; {"changed": true, "data_changed": false, "data_type_changed": true}

null_resource.RunInitialPlaybook (local-exec): TASK [Copy Citrix iso to the host] *********************************************
null_resource.RunInitialPlaybook: Still creating... [00m10s elapsed]

...

null_resource.RunInitialPlaybook: Still creating... [09m20s elapsed]
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ws.the-austrian-citrix-guy.at] =&gt; {"changed": true, "checksum": null, "dest": "C:\\InstallCVAD\\Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso", "operation": "file_copy", "original_basename": "Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso", "size": 4124442624, "src": "/etc/ansible/isos/Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso"}

null_resource.RunInitialPlaybook (local-exec): TASK [Copy the SSL certificate to the hosts] ***********************************
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ws.the-austrian-citrix-guy.at] =&gt; {"changed": true, "checksum": null, "dest": "C:\\InstallCVAD\\2025-wildcard.the-austrian-citrix-guy.at.PFX", "operation": "file_copy", "original_basename": "2025-wildcard.the-austrian-citrix-guy.at.PFX", "size": 3593, "src": "/etc/ansible/assets/2025-wildcard.the-austrian-citrix-guy.at.PFX"}

null_resource.RunInitialPlaybook (local-exec): TASK [Install Required Server Roles for Citrix] ********************************
null_resource.RunInitialPlaybook: Still creating... [09m30s elapsed]

...

null_resource.RunInitialPlaybook: Still creating... [18m40s elapsed]
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ws.the-austrian-citrix-guy.at] =&gt; {"changed": true, "exitcode": "Success", "feature_result": [{"display_name": "Message Queuing", "id": 49, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Message Queuing Server", "id": 191, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Message Queuing Services", "id": 190, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ASP.NET 4.8", "id": 429, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": ".NET Framework 3.5 (includes .NET 2.0 and 3.0)", "id": 220, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": ".NET Framework 3.5 Features", "id": 475, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "HTTP Activation", "id": 421, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Message Queuing (MSMQ) Activation", "id": 422, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Named Pipe Activation", "id": 423, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "TCP Activation", "id": 424, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Telnet Client", "id": 44, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Windows Process Activation Service", "id": 41, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Configuration APIs", "id": 217, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Process Model", "id": 219, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Application Development", "id": 147, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Application Initialization", "id": 445, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ASP", "id": 150, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ASP.NET 4.8", "id": 413, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Basic Authentication", "id": 163, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "CGI", "id": 151, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Common HTTP Features", "id": 141, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Default Document", "id": 143, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Directory Browsing", "id": 144, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Dynamic Content Compression", "id": 173, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Request Filtering", "id": 169, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Health and Diagnostics", "id": 155, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "HTTP Errors", "id": 145, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "HTTP Logging", "id": 156, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "HTTP Redirection", "id": 146, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Tracing", "id": 159, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Server Side Includes", "id": 154, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ISAPI Extensions", "id": 152, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ISAPI Filters", "id": 153, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Logging Tools", "id": 157, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "IIS 6 Metabase Compatibility", "id": 179, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "IIS 6 Management Compatibility", "id": 178, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "IIS Management Console", "id": 175, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Management Tools", "id": 174, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": ".NET Extensibility 4.8", "id": 414, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Performance", "id": 171, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "IIS Management Scripts and Tools", "id": 176, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Security", "id": 162, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Web Server (IIS)", "id": 2, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Static Content Compression", "id": 172, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Static Content", "id": 142, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Web Server", "id": 140, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Windows Authentication", "id": 164, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "IIS 6 WMI Compatibility", "id": 180, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}], "reboot_required": false, "success": true}

null_resource.RunInitialPlaybook (local-exec): TASK [Reboot when Required Roles need a restart] *******************************
null_resource.RunInitialPlaybook (local-exec): skipping: [tf-cvad-ws.the-austrian-citrix-guy.at] =&gt; {"changed": false, "false_condition": "requirements.reboot_required", "skip_reason": "Conditional result was False"}

null_resource.RunInitialPlaybook (local-exec): TASK [Install SSL-Certificate on Servers] **************************************
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ws.the-austrian-citrix-guy.at] =&gt; {"changed": true, "thumbprints": ["0BB52BDDEB78E6E8E32FF3B1949CAA462B8EC386"]}

null_resource.RunInitialPlaybook (local-exec): TASK [Run installer from mounted ISO] ******************************************
null_resource.RunInitialPlaybook: Still creating... [18m50s elapsed]
...
null_resource.RunInitialPlaybook: Still creating... [22m20s elapsed]
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ws.the-austrian-citrix-guy.at] =&gt; {"changed": true, "checksum": "EADE21C7B09DED5626F3BB51D4DF7A4C0E7AC607", "rc": 0, "reboot_required": false}

null_resource.RunInitialPlaybook (local-exec): TASK [Reboot after sf] *********************************************************
null_resource.RunInitialPlaybook: Still creating... [22m30s elapsed]
null_resource.RunInitialPlaybook: Still creating... [22m40s elapsed]
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ws.the-austrian-citrix-guy.at] =&gt; {"changed": true, "elapsed": 23, "rebooted": true, "unreachable": false}

null_resource.RunInitialPlaybook (local-exec): PLAY RECAP *********************************************************************
null_resource.RunInitialPlaybook (local-exec): tf-cvad-ws.the-austrian-citrix-guy.at : ok=11   changed=9    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0

null_resource.RunInitialPlaybook: Creation complete after 22m45s [id=7191293509250199804]
data.external.CheckAnsibleReturnCode: Reading...
data.external.CheckAnsibleReturnCode: Read complete after 0s [id=-]
null_resource.IfExitCodeIsNotZeroThenHalt: Creating...
null_resource.IfExitCodeIsNotZeroThenHalt: Provisioning with 'local-exec'...
null_resource.IfExitCodeIsNotZeroThenHalt (local-exec): Executing: ["/bin/bash" "-c" "[ \"1\" = \"1\" ] &amp;&amp; echo \"Deploying WebStudio: Everything OK\" || { echo \"Deploying the WebStudio software and/or its prerequisites failed. Please review the settings. Halting Terraform.\"; exit 1; }"]
null_resource.IfExitCodeIsNotZeroThenHalt (local-exec): Deploying WebStudio: Everything OK
null_resource.IfExitCodeIsNotZeroThenHalt: Creation complete after 0s [id=416418525198442619]

Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
azadmin@tacg-cicd:/etc/terraform-tmm/CVADXS/_InstallWebStudio$
After successful deployment, the WebStudio server is now installed and ready. We can continue with the next step.  Part 7: Using Terraform and Ansible to Change the SSL BindingsWe want to configure all transport in our environment as securely as possible. Therefore, Terraform and Ansible take additional steps to configure all SSL bindings across all servers and listeners to enable complete HTTPS communication. Where applicable, the SSL certificate is changed to a publicly signed certificate to avoid certificate trust errors. The change is applied by a PowerShell script, which Ansible runs on each applicable host. To obtain all required elevated privileges, we use PsExec64.exe as the host to run the PowerShell scripts. Terraform monitors each Ansible Playbook run and stops further steps if a Playbook fails. Example Terraform snippet: # Terraform Deployment of Citrix CVAD on XenServer 8 - 25.11 
## Definition of XenServer Provider Variables - not all available variables are defined, only these we use
### Reference https://registry.terraform.io/providers/xenserver/xenserver/latest/docs

#### Copy the required Ansible Assets to the Ansible Server
###### ***** IMPORTANT: You must set the permissions to the destination directory accordingly (sudo) chmod 777 otherwise the provisioning will fail *****
resource "null_resource" "CopyAnsibleSiteAssetsToAnsibleServer" {
  connection {
    type     = var.TACG-TMM-XS-CL-WinRM-Connection-Type
    user     = var.TACG-TMM-XS-CL-WinRM-Connection-User
    password = var.TACG-TMM-XS-CL-WinRM-Connection-Password
    host     = var.TACG-TMM-XS-CL-WinRM-Connection-Ansible
    port     = var.TACG-TMM-XS-CL-WinRM-Connection-Port
    https    = var.TACG-TMM-XS-CL-WinRM-Connection-HTTPS
    timeout  = var.TACG-TMM-XS-CL-WinRM-Connection-Timeout
  }

  # Copy AdditionalSiteConfiguration Playbook file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-ChangeSSLOnServers-Playbook-Source
    destination = var.TACG-TMM-XS-CL-Ansible-ChangeSSLOnServers-Playbook-Destination
  }
}

#### Copy the required PowerShell Scripts to the DDC1
resource "null_resource" "CopyPowerShellScriptsToDDC1" {
  connection {
    type     = var.TACG-TMM-XS-CL-WinRM-Connection-Type
    user     = var.TACG-TMM-XS-CL-WinRM-Connection-User
    password = var.TACG-TMM-XS-CL-WinRM-Connection-Password
    host     = var.TACG-TMM-XS-CL-WinRM-Connection-DDC1
    port     = var.TACG-TMM-XS-CL-WinRM-Connection-Port
    https    = var.TACG-TMM-XS-CL-WinRM-Connection-HTTPS
    timeout  = var.TACG-TMM-XS-CL-WinRM-Connection-Timeout
  }

  # Copy ChangeSSLCOnServers.ps1 file To the DDC1
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-ChangeSSLOnServers-Source
    destination = var.TACG-TMM-XS-CL-Ansible-ChangeSSLOnServers-Destination
  }

  # Copy PSExec64.exe file To the DDC1
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-PsExec64-Source
    destination = var.TACG-TMM-XS-CL-PsExec64-Destination
  }
}

#### Copy the required PowerShell Scripts to the DDC2
resource "null_resource" "CopyPowerShellScriptsToDDC2" {
  connection {
    type     = var.TACG-TMM-XS-CL-WinRM-Connection-Type
    user     = var.TACG-TMM-XS-CL-WinRM-Connection-User
    password = var.TACG-TMM-XS-CL-WinRM-Connection-Password
    host     = var.TACG-TMM-XS-CL-WinRM-Connection-DDC2
    port     = var.TACG-TMM-XS-CL-WinRM-Connection-Port
    https    = var.TACG-TMM-XS-CL-WinRM-Connection-HTTPS
    timeout  = var.TACG-TMM-XS-CL-WinRM-Connection-Timeout
  }

  # Copy ChangeSSLCOnServers.ps1 file To the DDC2
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-ChangeSSLOnServers-Source
    destination = var.TACG-TMM-XS-CL-Ansible-ChangeSSLOnServers-Destination
  }

  # Copy PSExec64.exe file To the DDC2
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-PsExec64-Source
    destination = var.TACG-TMM-XS-CL-PsExec64-Destination
  }
}

#### Copy the required PowerShell Scripts to the SF
resource "null_resource" "CopyPowerShellScriptsToSF" {
  connection {
    type     = var.TACG-TMM-XS-CL-WinRM-Connection-Type
    user     = var.TACG-TMM-XS-CL-WinRM-Connection-User
    password = var.TACG-TMM-XS-CL-WinRM-Connection-Password
    host     = var.TACG-TMM-XS-CL-WinRM-Connection-SF
    port     = var.TACG-TMM-XS-CL-WinRM-Connection-Port
    https    = var.TACG-TMM-XS-CL-WinRM-Connection-HTTPS
    timeout  = var.TACG-TMM-XS-CL-WinRM-Connection-Timeout
  }

  # Copy ChangeSSLCOnServers.ps1 file To the SF
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-ChangeSSLOnServers-Source
    destination = var.TACG-TMM-XS-CL-Ansible-ChangeSSLOnServers-Destination
  }

  # Copy PSExec64.exe file To the SF
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-PsExec64-Source
    destination = var.TACG-TMM-XS-CL-PsExec64-Destination
  }
}

#### Copy the required PowerShell Scripts to the WS
resource "null_resource" "CopyPowerShellScriptsToWS" {
  connection {
    type     = var.TACG-TMM-XS-CL-WinRM-Connection-Type
    user     = var.TACG-TMM-XS-CL-WinRM-Connection-User
    password = var.TACG-TMM-XS-CL-WinRM-Connection-Password
    host     = var.TACG-TMM-XS-CL-WinRM-Connection-WS
    port     = var.TACG-TMM-XS-CL-WinRM-Connection-Port
    https    = var.TACG-TMM-XS-CL-WinRM-Connection-HTTPS
    timeout  = var.TACG-TMM-XS-CL-WinRM-Connection-Timeout
  }

  # Copy ChangeSSLCOnServers.ps1 file To the WS
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-ChangeSSLOnServers-Source
    destination = var.TACG-TMM-XS-CL-Ansible-ChangeSSLOnServers-Destination
  }

  # Copy PSExec64.exe file To the WS
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-PsExec64-Source
    destination = var.TACG-TMM-XS-CL-PsExec64-Destination
  }
}

resource "time_sleep" "wait_10_seconds" {
  depends_on      = [null_resource.CopyAnsibleSiteAssetsToAnsibleServer, null_resource.CopyPowerShellScriptsToDDC1]
  create_duration = "10s"
}

##### Change the SSL Bindings Start
##### Connect to Ansible Interpreter and run ChangeSSLOnServers-Playbook
resource "null_resource" "RunChangeSSLPlaybook" {
  depends_on = [time_sleep.wait_10_seconds]

  provisioner "local-exec" {
    working_dir = "/etc/ansible"
    command     = var.TACG-TMM-XS-CL-Ansible-ChangeSSLOnServers-CMD
  }
}

##### Check Ansible Return Code
data "external" "CheckAnsibleReturnCode" {
  depends_on = [null_resource.RunChangeSSLPlaybook]
  program    = ["bash", "/etc/ansible/assets/CheckAnsibleReturnCode.sh", "/etc/ansible/assets/ansible_exit_code.txt", "0"]
}

##### Terminate if Return Code is not 0
resource "null_resource" "IfExitCodeIsNotZeroThenHalt" {
  depends_on = [data.external.CheckAnsibleReturnCode]
  provisioner "local-exec" {
    interpreter = ["/bin/bash", "-c"]
    command     = "[ \"${data.external.CheckAnsibleReturnCode.result.found}\" = \"1\" ] &amp;&amp; echo \"Change SSL Bindings - Everything OK\" || { echo \"Changing the SSL bindings failed. Please review the settings. Halting Terraform.\"; exit 1; }"
  }
}

##### Change the SSL Bindings EndExample Ansible Playbook for changing the SSL bindings on the DDCs: # Terraform Deployment of Citrix CVAD on XenServer 8 - 25.11 
## For installation and configuration of the Delivery Controllers, StoreFront-Servers and WebStudio-Servers, we use Ansible Playbooks 
### Reference https://docs.ansible.com/ansible/latest/getting_started/index.html
#### We assume that the communication between the Ansible Server and all needed Hosts via WinRM is working as intended - check your Group Policies
##### All hosts and its corresponding communication-related Ansible variables are stored in assets/inventory.ini
###### Change SSL Certificate bindings to the previously uploaded public SSL Certificate by running a PowerShell script on all CVAD servers
- name: Change SSL Certificate bindings
  hosts: cvad-ddcs
  gather_facts: no
  collections:
    - community.windows
  vars:
    domain_user: "TACG\\XxXxXxXxX"
    domain_pass: "!XxXxXxXxXxXxXxXxX!"
    psexec_path: "C:\\InstallCVAD\\PsExec64.exe"
    remote_script: "C:\\InstallCVAD\\ChangeSSLOnServers.ps1"
    ansible_user: "svc_ansible"      
    ansible_password: "!XxXxXxXxXxXxXxXxX!"    
    ansible_connection: winrm
    ansible_winrm_transport: certificate

  tasks:
   - name: Run the ChangeSSLOnDDCs-PowerShell script as Domain Administrator
     community.windows.win_psexec:
       command: powershell.exe -NoProfile -ExecutionPolicy Bypass -File "{{ remote_script }}"
       executable: "{{ psexec_path }}"
       username: "{{ domain_user }}"
       password: "{{ domain_pass }}"
       elevated: true
       noprofile: true
       interactive: true
       wait: true
Example PowerShell script to change the SSL bindings: Try {  
$friendlyName = "*.the-austrian-citrix-guy.at"
$Thumbprint = (Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object {$_.FriendlyName -eq $friendlyName}).Thumbprint -join ';'
$ThumbprintC = ($ThumbPrint.Replace(" ",""))

netsh http delete sslcert ipport=0.0.0.0:443

$NGuid = [guid]::NewGuid().ToString("B")   

netsh http add sslcert ipport=0.0.0.0:443 certhash=$ThumbprintC appid=$NGuid disablelegacytls=enable

netsh http show sslcert

    } catch {
    $logFile = "C:\Logs\MyLogFile.log"
    $errorMessage = $_.Exception.Message
    Add-Content -Path $logFile -Value "$(Get-Date) - ChangeSSLCertificateBindings: Error: $errorMessage"
    Write-Output "An error occurred trying to change the SSL-Certificate bindings (error: $($Error[0]))"
    Exit 1  

    }Running the snippet should fulfill all needed steps – example for running it on the DDCs: azadmin@tacg-cicd:/etc/terraform-tmm/CVADXS/_ChangeSSLOnServers$ terraform apply

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create
 &lt;= read (data resources)

Terraform will perform the following actions:

  # data.external.CheckAnsibleReturnCode will be read during apply
  # (depends on a resource or a module with changes pending)
 &lt;= data "external" "CheckAnsibleReturnCode" {
      + id      = (known after apply)
      + program = [
          + "bash",
          + "/etc/ansible/assets/CheckAnsibleReturnCode.sh",
          + "/etc/ansible/assets/ansible_exit_code.txt",
          + "0",
        ]
      + result  = (known after apply)
    }

  # null_resource.CopyAnsibleSiteAssetsToAnsibleServer will be created
  + resource "null_resource" "CopyAnsibleSiteAssetsToAnsibleServer" {
      + id = (known after apply)
    }

  # null_resource.CopyPowerShellScriptsToDDC1 will be created
  + resource "null_resource" "CopyPowerShellScriptsToDDC1" {
      + id = (known after apply)
    }

  # null_resource.CopyPowerShellScriptsToDDC2 will be created
  + resource "null_resource" "CopyPowerShellScriptsToDDC2" {
      + id = (known after apply)
    }

  # null_resource.CopyPowerShellScriptsToSF will be created
  + resource "null_resource" "CopyPowerShellScriptsToSF" {
      + id = (known after apply)
    }

  # null_resource.CopyPowerShellScriptsToWS will be created
  + resource "null_resource" "CopyPowerShellScriptsToWS" {
      + id = (known after apply)
    }

  # null_resource.IfExitCodeIsNotZeroThenHalt will be created
  + resource "null_resource" "IfExitCodeIsNotZeroThenHalt" {
      + id = (known after apply)
    }

  # null_resource.RunChangeSSLPlaybook will be created
  + resource "null_resource" "RunChangeSSLPlaybook" {
      + id = (known after apply)
    }

  # time_sleep.wait_10_seconds will be created
  + resource "time_sleep" "wait_10_seconds" {
      + create_duration = "10s"
      + 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

null_resource.CopyAnsibleSiteAssetsToAnsibleServer: Creating...
null_resource.CopyPowerShellScriptsToWS: Creating...
null_resource.CopyPowerShellScriptsToDDC1: Creating...
null_resource.CopyPowerShellScriptsToDDC1: Provisioning with 'file'...
null_resource.CopyPowerShellScriptsToWS: Provisioning with 'file'...
null_resource.CopyPowerShellScriptsToSF: Creating...
null_resource.CopyPowerShellScriptsToDDC2: Creating...
null_resource.CopyAnsibleSiteAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPowerShellScriptsToDDC2: Provisioning with 'file'...
null_resource.CopyPowerShellScriptsToSF: Provisioning with 'file'...
null_resource.CopyAnsibleSiteAssetsToAnsibleServer: Creation complete after 0s [id=8882922836300404953]
#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;
...
null_resource.CopyPowerShellScriptsToDDC1: Creation complete after 10s [id=4814951330333809760]
null_resource.CopyPowerShellScriptsToSF: Creation complete after 10s [id=6247443398511140586]
null_resource.CopyPowerShellScriptsToDDC2: Creation complete after 10s [id=4396737003381006003]

...
time_sleep.wait_10_seconds: Still creating... [00m10s elapsed]
time_sleep.wait_10_seconds: Creation complete after 10s [id=2025-11-13T12:24:08Z]
null_resource.RunChangeSSLPlaybook: Creating...
null_resource.RunChangeSSLPlaybook: Provisioning with 'local-exec'...
null_resource.RunChangeSSLPlaybook (local-exec): Executing: ["/bin/sh" "-c" "ansible-playbook /etc/ansible/assets/ChangeSSLOnServers.ansible.yml -i /etc/ansible/assets/inventory.ini -vvv -c winrm; echo $? &gt; /etc/ansible/assets/ansible_exit_code.txt"]
null_resource.RunChangeSSLPlaybook (local-exec): Using /etc/ansible/ansible.cfg as config file

null_resource.RunChangeSSLPlaybook (local-exec): PLAYBOOK: ChangeSSLOnServers.ansible.yml ***************************************
null_resource.RunChangeSSLPlaybook (local-exec): 1 plays in /etc/ansible/assets/ChangeSSLOnServers.ansible.yml

null_resource.RunChangeSSLPlaybook (local-exec): PLAY [Change SSL Certificate bindings] *****************************************

null_resource.RunChangeSSLPlaybook (local-exec): TASK [Run the ChangeSSLOnDDCs-PowerShell script as Domain Administrator] *******
null_resource.RunChangeSSLPlaybook (local-exec): task path: /etc/ansible/assets/ChangeSSLOnServers.ansible.yml:26
null_resource.RunChangeSSLPlaybook (local-exec): Using module file /usr/lib/python3/dist-packages/ansible_collections/community/windows/plugins/modules/win_psexec.ps1
null_resource.RunChangeSSLPlaybook (local-exec): Pipelining is enabled.
null_resource.RunChangeSSLPlaybook (local-exec): &lt;tf-cvad-ddc1.the-austrian-citrix-guy.at&gt; ESTABLISH WINRM CONNECTION FOR USER: svc_ansible on PORT 5986 TO tf-cvad-ddc1.the-austrian-citrix-guy.at
null_resource.RunChangeSSLPlaybook (local-exec): EXEC (via pipeline wrapper)
null_resource.RunChangeSSLPlaybook (local-exec): changed: [tf-cvad-ddc1.the-austrian-citrix-guy.at] =&gt; {
null_resource.RunChangeSSLPlaybook (local-exec):     "changed": true,
null_resource.RunChangeSSLPlaybook (local-exec):     "delta": "0:00:01.130213",
null_resource.RunChangeSSLPlaybook (local-exec):     "end": "2025-11-13 12:24:09.735282",
null_resource.RunChangeSSLPlaybook (local-exec):     "invocation": {
null_resource.RunChangeSSLPlaybook (local-exec):         "module_args": {
null_resource.RunChangeSSLPlaybook (local-exec):             "chdir": null,
null_resource.RunChangeSSLPlaybook (local-exec):             "command": "powershell.exe -NoProfile -ExecutionPolicy Bypass -File \"C:\\InstallCVAD\\ChangeSSLOnServers.ps1\"",
null_resource.RunChangeSSLPlaybook (local-exec):             "elevated": true,
null_resource.RunChangeSSLPlaybook (local-exec):             "executable": "C:\\InstallCVAD\\PsExec64.exe",
null_resource.RunChangeSSLPlaybook (local-exec):             "hostnames": null,
null_resource.RunChangeSSLPlaybook (local-exec):             "interactive": true,
null_resource.RunChangeSSLPlaybook (local-exec):             "limited": false,
null_resource.RunChangeSSLPlaybook (local-exec):             "nobanner": false,
null_resource.RunChangeSSLPlaybook (local-exec):             "noprofile": true,
null_resource.RunChangeSSLPlaybook (local-exec):             "password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
null_resource.RunChangeSSLPlaybook (local-exec):             "priority": null,
null_resource.RunChangeSSLPlaybook (local-exec):             "session": null,
null_resource.RunChangeSSLPlaybook (local-exec):             "system": false,
null_resource.RunChangeSSLPlaybook (local-exec):             "timeout": null,
null_resource.RunChangeSSLPlaybook (local-exec):             "username": "TACG\\administrator",
null_resource.RunChangeSSLPlaybook (local-exec):             "wait": true
null_resource.RunChangeSSLPlaybook (local-exec):         }
null_resource.RunChangeSSLPlaybook (local-exec):     },
null_resource.RunChangeSSLPlaybook (local-exec):     "psexec_command": "C:\\InstallCVAD\\PsExec64.exe -u TACG\\administrator -p *PASSWORD_REPLACED* -e -h -i -accepteula powershell.exe -NoProfile -ExecutionPolicy Bypass -File \"C:\\InstallCVAD\\ChangeSSLOnServers.ps1\"",
null_resource.RunChangeSSLPlaybook (local-exec):     "rc": 0,
null_resource.RunChangeSSLPlaybook (local-exec):     "start": "2025-11-13 12:24:08.605068",
null_resource.RunChangeSSLPlaybook (local-exec):     "stderr": "Connecting to local system...\r\r\rStarting PSEXESVC service on local system...\r\r\rCopying authentication key to TF-CVAD-DDC1...\r\r\rConnecting with PsExec service on TF-CVAD-DDC1...\r\r\rStarting powershell.exe on TF-CVAD-DDC1...\r\r\r\r\npowershell.exe exited on TF-CVAD-DDC1 with error code 0.\r\n",
null_resource.RunChangeSSLPlaybook (local-exec):     "stderr_lines": [
null_resource.RunChangeSSLPlaybook (local-exec):         "Connecting to local system...",
null_resource.RunChangeSSLPlaybook (local-exec):         "",
null_resource.RunChangeSSLPlaybook (local-exec):         "",
null_resource.RunChangeSSLPlaybook (local-exec):         "Starting PSEXESVC service on local system...",
null_resource.RunChangeSSLPlaybook (local-exec):         "",
null_resource.RunChangeSSLPlaybook (local-exec):         "",
null_resource.RunChangeSSLPlaybook (local-exec):         "Copying authentication key to TF-CVAD-DDC1...",
null_resource.RunChangeSSLPlaybook (local-exec):         "",
null_resource.RunChangeSSLPlaybook (local-exec):         "",
null_resource.RunChangeSSLPlaybook (local-exec):         "Connecting with PsExec service on TF-CVAD-DDC1...",
null_resource.RunChangeSSLPlaybook (local-exec):         "",
null_resource.RunChangeSSLPlaybook (local-exec):         "",
null_resource.RunChangeSSLPlaybook (local-exec):         "Starting powershell.exe on TF-CVAD-DDC1...",
null_resource.RunChangeSSLPlaybook (local-exec):         "",
null_resource.RunChangeSSLPlaybook (local-exec):         "",
null_resource.RunChangeSSLPlaybook (local-exec):         "",
null_resource.RunChangeSSLPlaybook (local-exec):         "powershell.exe exited on TF-CVAD-DDC1 with error code 0."
null_resource.RunChangeSSLPlaybook (local-exec):     ],
null_resource.RunChangeSSLPlaybook (local-exec):     "stdout": "\r\nPsExec v2.43 - Execute processes remotely\r\nCopyright (C) 2001-2023 Mark Russinovich\r\nSysinternals - www.sysinternals.com\r\n\r\n",
null_resource.RunChangeSSLPlaybook (local-exec):     "stdout_lines": [
null_resource.RunChangeSSLPlaybook (local-exec):         "",
null_resource.RunChangeSSLPlaybook (local-exec):         "PsExec v2.43 - Execute processes remotely",
null_resource.RunChangeSSLPlaybook (local-exec):         "Copyright (C) 2001-2023 Mark Russinovich",
null_resource.RunChangeSSLPlaybook (local-exec):         "Sysinternals - www.sysinternals.com",
null_resource.RunChangeSSLPlaybook (local-exec):         ""
null_resource.RunChangeSSLPlaybook (local-exec):     ]
null_resource.RunChangeSSLPlaybook (local-exec): }
null_resource.RunChangeSSLPlaybook (local-exec): PLAY RECAP *********************************************************************
null_resource.RunChangeSSLPlaybook (local-exec): tf-cvad-ddc1.the-austrian-citrix-guy.at : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
null_resource.RunChangeSSLPlaybook (local-exec): tf-cvad-ddc2.the-austrian-citrix-guy.at : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

null_resource.RunChangeSSLPlaybook: Creation complete after 3s [id=6734265951530601448]
data.external.CheckAnsibleReturnCode: Reading...
data.external.CheckAnsibleReturnCode: Read complete after 0s [id=-]
null_resource.IfExitCodeIsNotZeroThenHalt: Creating...
null_resource.IfExitCodeIsNotZeroThenHalt: Provisioning with 'local-exec'...
null_resource.IfExitCodeIsNotZeroThenHalt (local-exec): Executing: ["/bin/bash" "-c" "[ \"1\" = \"1\" ] &amp;&amp; echo \"Change SSL Bindings - Everything OK\" || { echo \"Changing the SSL bindings failed. Please review the settings. Halting Terraform.\"; exit 1; }"]
null_resource.IfExitCodeIsNotZeroThenHalt (local-exec): Change SSL Bindings - Everything OK
null_resource.IfExitCodeIsNotZeroThenHalt: Creation complete after 0s [id=5221894585109127965]

Apply complete! Resources: 8 added, 0 changed, 0 destroyed.
azadmin@tacg-cicd:/etc/terraform-tmm/CVADXS/_ChangeSSLOnServers$As all server operations are complete, we can now start with creating the CVAD site, the StoreFront site, and all the CVAD entities.  Part 8: Using Terraform and Ansible to create the CVAD site and all its necessitiesCreating the CVAD site, including its requirements and dependencies, is a complex task that involves many steps. Terraform and Ansible together fulfill the following tasks: Create all databases Create the site Add the second DDC to the site Configure additional site configurations Configure a dedicated Administrators group Configure the licensing Before running these tasks, we see that no related databases exist on SQL Server, and no site is configured on the primary Delivery Controller. Important All Terraform configurations and Ansible Playbooks, as well as the corresponding PowerShell scripts, must be run in the Site Administrator´s context – it must be a Domain user account. To obtain all required elevated privileges, we use PsExec64.exe as the host to run the PowerShell scripts. If we do not use the correct user account and privileges, the deployment will fail. Terraform monitors each Ansible Playbook run and stops further steps if a Playbook fails. Example Terraform snippet: # Terraform Deployment of Citrix CVAD on XenServer 8 - 25.11 
## Definition of XenServer Provider Variables - not all available variables are defined, only these we use
### Reference https://registry.terraform.io/providers/xenserver/xenserver/latest/docs

#### Copy the required Ansible Assets to the Ansible Server
###### ***** IMPORTANT: You must set the permissions to the destination directory accordingly (sudo) chmod 777 otherwise the provisioning will fail *****
resource "null_resource" "CopyAnsibleSiteAssetsToAnsibleServer" {
  connection {
    type     = var.TACG-TMM-XS-CL-WinRM-Connection-Type
    user     = var.TACG-TMM-XS-CL-WinRM-Connection-User
    password = var.TACG-TMM-XS-CL-WinRM-Connection-Password
    host     = var.TACG-TMM-XS-CL-WinRM-Connection-HostIP
    port     = var.TACG-TMM-XS-CL-WinRM-Connection-Port
    https    = var.TACG-TMM-XS-CL-WinRM-Connection-HTTPS
    timeout  = var.TACG-TMM-XS-CL-WinRM-Connection-Timeout
  }

  # Copy AdditionalSiteConfiguration Playbook file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-AdditionalSiteConfiguration-Playbook-Source
    destination = var.TACG-TMM-XS-CL-Ansible-AdditionalSiteConfiguration-Playbook-Destination
  }

  # Copy AddSecondDDCToSite Playbook file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-AddSecondDDCToSite-Playbook-Source
    destination = var.TACG-TMM-XS-CL-Ansible-AddSecondDDCToSite-Playbook-Destination
  }

  # Copy ConfigureAdminGroup Playbook file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-ConfigureAdminGroup-Playbook-Source
    destination = var.TACG-TMM-XS-CL-Ansible-ConfigureAdminGroup-Playbook-Destination
  }

  # Copy CreateDatabase Playbook file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-CreateDatabase-Playbook-Source
    destination = var.TACG-TMM-XS-CL-Ansible-CreateDatabase-Playbook-Destination
  }

  # Copy CreateSite Playbook file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-CreateSite-Playbook-Source
    destination = var.TACG-TMM-XS-CL-Ansible-CreateSite-Playbook-Destination
  }

  # Copy SetLicensing Playbook file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-SetLicensing-Playbook-Source
    destination = var.TACG-TMM-XS-CL-Ansible-SetLicensing-Playbook-Destination
  }
}

#### Copy the required PowerShell Scripts to the DDC
resource "null_resource" "CopyPowerShellScriptsToDDC1" {
  connection {
    type     = var.TACG-TMM-XS-CL-WinRM-Connection-Type
    user     = var.TACG-TMM-XS-CL-WinRM-Connection-User
    password = var.TACG-TMM-XS-CL-WinRM-Connection-Password
    host     = var.TACG-TMM-XS-CL-WinRM-Connection-HostIP
    port     = var.TACG-TMM-XS-CL-WinRM-Connection-Port
    https    = var.TACG-TMM-XS-CL-WinRM-Connection-HTTPS
    timeout  = var.TACG-TMM-XS-CL-WinRM-Connection-Timeout
  }

  # Copy AdditionalSiteConfiguration.ps1 file To the DDC
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-DDC-AdditionalSiteConfiguration-Source
    destination = var.TACG-TMM-XS-CL-DDC-AdditionalSiteConfiguration-Destination
  }

  # Copy AddSecondDDCToSite.ps1 file To the DDC
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-DDC-AddSecondDDCToSite-Source
    destination = var.TACG-TMM-XS-CL-DDC-AddSecondDDCToSite-Destination
  }

  # Copy ConfigureAdminGroup.ps1 file To the DDC
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-DDC-ConfigureAdminGroup-Source
    destination = var.TACG-TMM-XS-CL-DDC-ConfigureAdminGroup-Destination
  }

  # Copy CreateDatabase.ps1 file To the DDC
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-DDC-CreateDatabase-Source
    destination = var.TACG-TMM-XS-CL-DDC-CreateDatabase-Destination
  }

  # Copy CreateSite.ps1 file To the DDC
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-DDC-CreateSite-Source
    destination = var.TACG-TMM-XS-CL-DDC-CreateSite-Destination
  }

  # Copy SetLicensing.ps1 file To the DDC
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-DDC-SetLicensing-Source
    destination = var.TACG-TMM-XS-CL-DDC-SetLicensing-Destination
  }

  # Copy PsExec.exe file To the DDC
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-DDC-PsExec-Source
    destination = var.TACG-TMM-XS-CL-DDC-PsExec-Destination
  }
}

resource "time_sleep" "wait_10_seconds" {
  depends_on      = [null_resource.CopyAnsibleSiteAssetsToAnsibleServer, null_resource.CopyPowerShellScriptsToDDC1]
  create_duration = "10s"
}

##### Create Databases Start
##### Connect to Ansible Interpreter and run CreateDatabase-Playbook on DDCs
resource "null_resource" "RunCreateDatabasePlaybook" {
  depends_on = [time_sleep.wait_10_seconds]

  provisioner "local-exec" {
    working_dir = "/etc/ansible"
    command     = var.TACG-TMM-XS-CL-Ansible-CreateDatabase-CMD
  }
}

##### Check Ansible Return Code
data "external" "CheckAnsibleReturnCodeCDB" {
  depends_on = [null_resource.RunCreateDatabasePlaybook]
  program    = ["bash", "/etc/ansible/assets/CheckAnsibleReturnCode.sh", "/etc/ansible/assets/ansible_exit_code.txt", "0"]
}

##### Terminate if Return Code is not 0
resource "null_resource" "IfExitCodeIsNotZeroThenHaltCDB" {
  depends_on = [data.external.CheckAnsibleReturnCodeCDB]
  provisioner "local-exec" {
    interpreter = ["/bin/bash", "-c"]
    command     = "[ \"${data.external.CheckAnsibleReturnCodeCDB.result.found}\" = \"1\" ] &amp;&amp; echo \"Create Databases - Everything OK\" || { echo \"Deploying the CVAD Databases failed. Please review the settings. Halting Terraform.\"; exit 1; }"
  }
}

#### Wait 10s for Background Processes to Settle
resource "time_sleep" "wait_300_secondsCDB" {
  depends_on      = [null_resource.IfExitCodeIsNotZeroThenHaltCDB]
  create_duration = "300s"
}

##### Create Databases End

##### Create Site Start
##### Connect to Ansible Interpreter and run CreateSite-Playbook on DDCs
resource "null_resource" "RunCreateSitePlaybook" {
  depends_on = [time_sleep.wait_300_secondsCDB]
  provisioner "local-exec" {
    working_dir = "/etc/ansible"
    command     = var.TACG-TMM-XS-CL-Ansible-CreateSite-CMD
  }
}

##### Check Ansible Return Code
data "external" "CheckAnsibleReturnCodeCS" {
  depends_on = [null_resource.RunCreateSitePlaybook]
  program    = ["bash", "/etc/ansible/assets/CheckAnsibleReturnCode.sh", "/etc/ansible/assets/ansible_exit_code.txt", "0"]
}

##### Terminate if Return Code is not 0
resource "null_resource" "IfExitCodeIsNotZeroThenHaltCS" {
  depends_on = [data.external.CheckAnsibleReturnCodeCS]
  provisioner "local-exec" {
    interpreter = ["/bin/bash", "-c"]
    command     = "[ \"${data.external.CheckAnsibleReturnCodeCS.result.found}\" = \"1\" ] &amp;&amp; echo \"Create Site - Everything OK\" || { echo \"Deploying the CVAD Site failed. Please review the settings. Halting Terraform.\"; exit 1; }"
  }
}

#### Wait 10s for Background Processes to Settle
resource "time_sleep" "wait_60_secondsCS" {
  depends_on      = [null_resource.IfExitCodeIsNotZeroThenHaltCS]
  create_duration = "60s"
}

##### Create Site End

##### Create SiteAddSecondDDC Start
##### Connect to Ansible Interpreter and run AddSecondDDC-Playbook on DDCs
resource "null_resource" "RunAddSecondDDCPlaybook" {
  depends_on = [time_sleep.wait_60_secondsCS]
  provisioner "local-exec" {
    working_dir = "/etc/ansible"
    command     = var.TACG-TMM-XS-CL-Ansible-AddSecondDDCToSite-CMD
  }
}

##### Check Ansible Return Code
data "external" "CheckAnsibleReturnCodeSecondDDC" {
  depends_on = [null_resource.RunAddSecondDDCPlaybook]
  program    = ["bash", "/etc/ansible/assets/CheckAnsibleReturnCode.sh", "/etc/ansible/assets/ansible_exit_code.txt", "0"]
}

##### Terminate if Return Code is not 0
resource "null_resource" "IfExitCodeIsNotZeroThenHaltSecondDDC" {
  depends_on = [data.external.CheckAnsibleReturnCodeSecondDDC]
  provisioner "local-exec" {
    interpreter = ["/bin/bash", "-c"]
    command     = "[ \"${data.external.CheckAnsibleReturnCodeSecondDDC.result.found}\" = \"1\" ] &amp;&amp; echo \"Add Second DDC - Everything OK\" || { echo \"Adding second DDC to the CVAD Site failed. Please review the settings. Halting Terraform.\"; exit 1; }"
  }
}

#### Wait 10s for Background Processes to Settle
resource "time_sleep" "wait_10_secondsSecondDDC" {
  depends_on      = [null_resource.IfExitCodeIsNotZeroThenHaltSecondDDC]
  create_duration = "10s"
}

##### Create SiteAddSecondDDC End

##### Create AdditionalSiteConfiguration Start
##### Connect to Ansible Interpreter and run AdditionalSiteConfiguration-Playbook on DDCs
resource "null_resource" "RunAdditionalSiteConfigurationPlaybook" {
  depends_on = [time_sleep.wait_10_secondsSecondDDC]

  provisioner "local-exec" {
    working_dir = "/etc/ansible"
    command     = var.TACG-TMM-XS-CL-Ansible-AdditionalSiteConfiguration-CMD
  }
}

##### Check Ansible Return Code
data "external" "CheckAnsibleReturnCodeAdditionalSiteConfiguration" {
  depends_on = [null_resource.RunAdditionalSiteConfigurationPlaybook]
  program    = ["bash", "/etc/ansible/assets/CheckAnsibleReturnCode.sh", "/etc/ansible/assets/ansible_exit_code.txt", "0"]
}

##### Terminate if Return Code is not 0
resource "null_resource" "IfExitCodeIsNotZeroThenHaltAdditionalSiteConfiguration" {
  depends_on = [data.external.CheckAnsibleReturnCodeAdditionalSiteConfiguration]
  provisioner "local-exec" {
    interpreter = ["/bin/bash", "-c"]
    command     = "[ \"${data.external.CheckAnsibleReturnCodeAdditionalSiteConfiguration.result.found}\" = \"1\" ] &amp;&amp; echo \"Additional Site Configuration - Everything OK\" || { echo \"Running the additional Site Configurations failed. Please review the settings. Halting Terraform.\"; exit 1; }"
  }
}

#### Wait 10s for Background Processes to Settle
resource "time_sleep" "wait_10_secondsAdditionalSiteConfiguration" {
  depends_on      = [null_resource.IfExitCodeIsNotZeroThenHaltAdditionalSiteConfiguration]
  create_duration = "10s"
}

##### Create AdditionalSiteConfiguration End

##### Create ConfigureAdminGroup Start
##### Connect to Ansible Interpreter and run ConfigureAdminGroup-Playbook on DDCs
resource "null_resource" "RunConfigureAdminGroupPlaybook" {
  depends_on = [time_sleep.wait_10_secondsAdditionalSiteConfiguration]

  provisioner "local-exec" {
    working_dir = "/etc/ansible"
    command     = var.TACG-TMM-XS-CL-Ansible-ConfigureAdminGroup-CMD
  }
}

##### Check Ansible Return Code
data "external" "CheckAnsibleReturnCodeConfigureAdminGroup" {
  depends_on = [null_resource.RunConfigureAdminGroupPlaybook]
  program    = ["bash", "/etc/ansible/assets/CheckAnsibleReturnCode.sh", "/etc/ansible/assets/ansible_exit_code.txt", "0"]
}

##### Terminate if Return Code is not 0
resource "null_resource" "IfExitCodeIsNotZeroThenHaltConfigureAdminGroup" {
  depends_on = [data.external.CheckAnsibleReturnCodeConfigureAdminGroup]
  provisioner "local-exec" {
    interpreter = ["/bin/bash", "-c"]
    command     = "[ \"${data.external.CheckAnsibleReturnCodeConfigureAdminGroup.result.found}\" = \"1\" ] &amp;&amp; echo \"Admin Group Configuration - Everything OK\" || { echo \"Running the Configuration of the AdminGroup failed. Please review the settings. Halting Terraform.\"; exit 1; }"
  }
}

#### Wait 10s for Background Processes to Settle
resource "time_sleep" "wait_10_secondsConfigureAdminGroup" {
  depends_on      = [null_resource.IfExitCodeIsNotZeroThenHaltConfigureAdminGroup]
  create_duration = "10s"
}

##### Create ConfigureAdminGroup End

##### Create SetLicensing Start
##### Connect to Ansible Interpreter and run SetLicensing-Playbook on DDCs
resource "null_resource" "RunConfigureSetLicensingPlaybook" {
  depends_on = [time_sleep.wait_10_secondsConfigureAdminGroup]

  provisioner "local-exec" {
    working_dir = "/etc/ansible"
    command     = var.TACG-TMM-XS-CL-Ansible-SetLicensing-CMD
  }
}

##### Check Ansible Return Code
data "external" "CheckAnsibleReturnCodeSetLicensing" {
  depends_on = [null_resource.RunConfigureSetLicensingPlaybook]
  program    = ["bash", "/etc/ansible/assets/CheckAnsibleReturnCode.sh", "/etc/ansible/assets/ansible_exit_code.txt", "0"]
}

##### Terminate if Return Code is not 0
resource "null_resource" "IfExitCodeIsNotZeroThenHaltSetLicensing" {
  depends_on = [data.external.CheckAnsibleReturnCodeSetLicensing]
  provisioner "local-exec" {
    interpreter = ["/bin/bash", "-c"]
    command     = "[ \"${data.external.CheckAnsibleReturnCodeSetLicensing.result.found}\" = \"1\" ] &amp;&amp; echo \"Licensing Configuration - Everything OK\" || { echo \"Configuration of Licensing failed. Please review the settings. Halting Terraform.\"; exit 1; }"
  }
}

##### Create SetLicensing End
Example Ansible Playbook calling the corresponding PowerShell script in PsExec64.exe for creating the required databases: # Terraform Deployment of Citrix CVAD on XenServer 8 - 25.11 
## For installation and configuration of the Delivery Controllers, StoreFront-Servers and WebStudio-Servers, we use Ansible Playbooks 
### Reference https://docs.ansible.com/ansible/latest/getting_started/index.html
#### We assume that the communication between the Ansible Server and all needed Hosts via WinRM is working as intended - check your Group Policies
##### All hosts and its corresponding communication-related Ansible variables are stored in assets/inventory.ini
###### Create CVAD Databases by running a PowerShell script on DDC1
- name: Create CVAD Databases
  hosts: cvad-ddc1
  gather_facts: no
  collections:
    - community.windows
  vars:
    domain_user: "TACG\\XxXxXxXxXxXxX"
    domain_pass: "!XxXxXxXxXxXxXxXxX!"
    psexec_path: "C:\\InstallCVAD\\PsExec64.exe"
    target_host: "tf-cvad-ddc1.the-austrian-citrix-guy.at"
    remote_script: "C:\\InstallCVAD\\CreateDatabases.ps1"
    ansible_user: "svc_ansible"      
    ansible_password: "!XxXxXxXxXxXxXxXxX!"
    ansible_connection: winrm
    ansible_winrm_transport: certificate

  tasks:
   - name: Run the Create Database-PowerShell script as Domain Administrator
     community.windows.win_psexec:
       command: powershell.exe -NoProfile -ExecutionPolicy Bypass -File "{{ remote_script }}"
       executable: "{{ psexec_path }}"
       username: "{{ domain_user }}"
       password: "{{ domain_pass }}"
       elevated: true
       noprofile: true
       interactive: true
       wait: trueExample PowerShell script for creating the required databases: Import-Module Citrix.XenDesktop®.Admin
asnp citrix.*
$SiteName = "TF-TACG-CVAD2507"
$DatabaseServer = "tacg-sql.the-austrian-citrix-guy.at"
$DDCName = "tf-cvad-ddc1.the-austrian-citrix-guy.at"
$DatabasePrefix = "DB-"

try {
        New-XDDatabase -AdminAddress $DDCName -SiteName $SiteName -AllDefaultDatabases -DatabaseServer $DatabaseServer -DatabaseNamePrefix $DatabasePrefix -Verbose

  } catch {
    $logFile = "C:\Logs\MyLogFile.log"
    $errorMessage = $_.Exception.Message
    Add-Content -Path $logFile -Value "$(Get-Date) - CreateDB: Error: $errorMessage"
    Write-Output "An error occurred trying to create the databases (error: $($Error[0]))"
    Exit 1     
}
 Note: The corresponding Ansible Playbooks for all the needed steps mentioned above are mostly the same – they differ only in running the feasible PowerShell script. Example PowerShell script for creating the CVAD site: Import-Module Citrix.XenDesktop.Admin
asnp citrix.*

$SiteName = "TF-TACG-CVAD2507"
$DatabaseServer = "tacg-sql.the-austrian-citrix-guy.at"
$DatabaseName_Site = "DB-CitrixTF-TACG-CVAD2507Site"
$DatabaseName_Logging = "DB-CitrixTF-TACG-CVAD2507Logging"
$DatabaseName_Monitoring = "DB-CitrixTF-TACG-CVAD2507Monitoring"
$DDCName = "tf-cvad-ddc1.the-austrian-citrix-guy.at"

try {
        New-XDSite -DatabaseServer $DatabaseServer -LoggingDatabaseName $DatabaseName_Logging -MonitorDatabaseName $DatabaseName_Monitoring -SiteDatabaseName $DatabaseName_Site -SiteName $SiteName -AdminAddress $DDCName -Verbose
  
  } catch {
    $logFile = "C:\Logs\MyLogFile.log"
    $errorMessage = $_.Exception.Message
    Add-Content -Path $logFile -Value "$(Get-Date) - CreateSite: Error: $errorMessage"
    Write-Output "An error occurred trying to create the CVAD site (error: $($Error[0]))"
    Exit 1  
  }

Example PowerShell script for adding the second DDC to the site:
Import-Module Citrix.XenDesktop.Admin
asnp citrix.*

$DDCName = "tf-cvad-ddc1.the-austrian-citrix-guy.at"
$NewDDCName = "tf-cvad-ddc2.the-austrian-citrix-guy.at"

try {
        Add-XDController -AdminAddress $NewDDCName -SiteControllerAddress $DDCName -Verbose
  
     } catch {
    $logFile = "C:\Logs\MyLogFile.log"
    $errorMessage = $_.Exception.Message
    Add-Content -Path $logFile -Value "$(Get-Date) - Add2ndDDC: Error: $errorMessage"
    Write-Output "An error occurred trying to add the 2nd DDC (error: $($Error[0]))"
    Exit 1  
    }
Example PowerShell script for adding the second DDC to the site: Import-Module Citrix.XenDesktop.Admin
asnp citrix.*

$DDCName = "tf-cvad-ddc1.the-austrian-citrix-guy.at"
$NewDDCName = "tf-cvad-ddc2.the-austrian-citrix-guy.at"

try {
        Add-XDController -AdminAddress $NewDDCName -SiteControllerAddress $DDCName -Verbose
  
     } catch {
    $logFile = "C:\Logs\MyLogFile.log"
    $errorMessage = $_.Exception.Message
    Add-Content -Path $logFile -Value "$(Get-Date) - Add2ndDDC: Error: $errorMessage"
    Write-Output "An error occurred trying to add the 2nd DDC (error: $($Error[0]))"
    Exit 1  
    }Example PowerShell script for running additional site configurations: Import-Module Citrix.XenDesktop.Admin
asnp citrix.*

$GroomingDays = 90

try {  
      Set-MonitorConfiguration -GroomApplicationInstanceRetentionDays $GroomingDays -GroomDeletedRetentionDays $GroomingDays -GroomFailuresRetentionDays $GroomingDays -GroomLoadIndexesRetentionDays $GroomingDays -GroomMachineHotfixLogRetentionDays $GroomingDays -GroomNotificationLogRetentionDays $GroomingDays -GroomResourceUsageDayDataRetentionDays $GroomingDays -GroomSessionsRetentionDays $GroomingDays -GroomSummariesRetentionDays $GroomingDays 
      Set-BrokerSite -TrustRequestsSentToTheXmlServicePort $true
      Set-BrokerSite -ConnectionLeasingEnabled $false
      Set-BrokerSite -LocalHostCacheEnabled $true
      Set-AnalyticsSite -Enabled $false

    } catch {
    $logFile = "C:\Logs\MyLogFile.log"
    $errorMessage = $_.Exception.Message
    Add-Content -Path $logFile -Value "$(Get-Date) - AdditionalSiteConfiguration: Error: $errorMessage"
    Write-Output "An error occurred trying to run the additional Site configuration (error: $($Error[0]))"
    Exit 1  
    }Example PowerShell script for adding a dedicated Administrators group to the site: Import-Module Citrix.XenDesktop.Admin
asnp citrix.*

$DDCName = "tf-cvad-ddc1.the-austrian-citrix-guy.at"
$AdminGroup = "TACG-CTX-Admins"

try {  
      New-AdminAdministrator -AdminAddress $DDCName -Name $AdminGroup
      Add-AdminRight -AdminAddress $DDCName -Administrator $AdminGroup -Role 'Full Administrator' -Scope "All"

    } catch {
    $logFile = "C:\Logs\MyLogFile.log"
    $errorMessage = $_.Exception.Message
    Add-Content -Path $logFile -Value "$(Get-Date) - ConfigureCTXAdminGroup: Error: $errorMessage"
    Write-Output "An error occurred trying to run the CTXAdminGroup script (error: $($Error[0]))"
    Exit 1  
    }Example PowerShell script for configuring the Licensing: Import-Module Citrix.XenDesktop.Admin
asnp citrix.*

$DDCName = "tf-cvad-ddc1.the-austrian-citrix-guy.at"
$LicenseServer = "tacg-dc.the-austrian-citrix-guy.at"
$LicenseServerPort = 27000
$LicensingModel = "UserDevice"
$ProductCode = "XDT"
$ProductEdition = "PLT"

try {  
      Set-XDLicensing -AdminAddress $DDCName -LicenseServerAddress $LicenseServer -LicenseServerPort $LicenseServerPort -Force
      Set-ConfigSite  -AdminAddress $DDCName -LicensingModel $LicensingModel -ProductCode $ProductCode -ProductEdition $ProductEdition -UseLicenseActivationService $true
      Set-ConfigSiteMetadata -AdminAddress $DDCName -Name 'CertificateHash' -Value $(Get-LicCertificate -AdminAddress "https://$($LicenseServer):8083").CertHash

    } catch {
    $logFile = "C:\Logs\MyLogFile.log"
    $errorMessage = $_.Exception.Message
    Add-Content -Path $logFile -Value "$(Get-Date) - ConfigureLicensing: Error: $errorMessage"
    Write-Output "An error occurred trying to run the ConfigureLicensing script (error: $($Error[0]))"
    Exit 1 
	}Running the snippet should fulfill all the steps mentioned above and create the CVAD site: azadmin@tacg-cicd:/etc/terraform-tmm/CVADXS/_ConfigureSite$ terraform apply
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
  + create
 &lt;= read (data resources)

Terraform will perform the following actions:

  # data.external.CheckAnsibleReturnCodeAdditionalSiteConfiguration will be read during apply
  # (depends on a resource or a module with changes pending)
 &lt;= data "external" "CheckAnsibleReturnCodeAdditionalSiteConfiguration" {
      + id      = (known after apply)
      + program = [
          + "bash",
          + "/etc/ansible/assets/CheckAnsibleReturnCode.sh",
          + "/etc/ansible/assets/ansible_exit_code.txt",
          + "0",
        ]
      + result  = (known after apply)
    }

  # data.external.CheckAnsibleReturnCodeCDB will be read during apply
  # (depends on a resource or a module with changes pending)
 &lt;= data "external" "CheckAnsibleReturnCodeCDB" {
      + id      = (known after apply)
      + program = [
          + "bash",
          + "/etc/ansible/assets/CheckAnsibleReturnCode.sh",
          + "/etc/ansible/assets/ansible_exit_code.txt",
          + "0",
        ]
      + result  = (known after apply)
    }

  # data.external.CheckAnsibleReturnCodeCS will be read during apply
  # (depends on a resource or a module with changes pending)
 &lt;= data "external" "CheckAnsibleReturnCodeCS" {
      + id      = (known after apply)
      + program = [
          + "bash",
          + "/etc/ansible/assets/CheckAnsibleReturnCode.sh",
          + "/etc/ansible/assets/ansible_exit_code.txt",
          + "0",
        ]
      + result  = (known after apply)
    }

  # data.external.CheckAnsibleReturnCodeConfigureAdminGroup will be read during apply
  # (depends on a resource or a module with changes pending)
 &lt;= data "external" "CheckAnsibleReturnCodeConfigureAdminGroup" {
      + id      = (known after apply)
      + program = [
          + "bash",
          + "/etc/ansible/assets/CheckAnsibleReturnCode.sh",
          + "/etc/ansible/assets/ansible_exit_code.txt",
          + "0",
        ]
      + result  = (known after apply)
    }

  # data.external.CheckAnsibleReturnCodeSecondDDC will be read during apply
  # (depends on a resource or a module with changes pending)
 &lt;= data "external" "CheckAnsibleReturnCodeSecondDDC" {
      + id      = (known after apply)
      + program = [
          + "bash",
          + "/etc/ansible/assets/CheckAnsibleReturnCode.sh",
          + "/etc/ansible/assets/ansible_exit_code.txt",
          + "0",
        ]
      + result  = (known after apply)
    }

  # data.external.CheckAnsibleReturnCodeSetLicensing will be read during apply
  # (depends on a resource or a module with changes pending)
 &lt;= data "external" "CheckAnsibleReturnCodeSetLicensing" {
      + id      = (known after apply)
      + program = [
          + "bash",
          + "/etc/ansible/assets/CheckAnsibleReturnCode.sh",
          + "/etc/ansible/assets/ansible_exit_code.txt",
          + "0",
        ]
      + result  = (known after apply)
    }

  # null_resource.CopyAnsibleSiteAssetsToAnsibleServer will be created
  + resource "null_resource" "CopyAnsibleSiteAssetsToAnsibleServer" {
      + id = (known after apply)
    }

  # null_resource.CopyPowerShellScriptsToDDC1 will be created
  + resource "null_resource" "CopyPowerShellScriptsToDDC1" {
      + id = (known after apply)
    }

  # null_resource.IfExitCodeIsNotZeroThenHaltAdditionalSiteConfiguration will be created
  + resource "null_resource" "IfExitCodeIsNotZeroThenHaltAdditionalSiteConfiguration" {
      + id = (known after apply)
    }

  # null_resource.IfExitCodeIsNotZeroThenHaltCDB will be created
  + resource "null_resource" "IfExitCodeIsNotZeroThenHaltCDB" {
      + id = (known after apply)
    }

  # null_resource.IfExitCodeIsNotZeroThenHaltCS will be created
  + resource "null_resource" "IfExitCodeIsNotZeroThenHaltCS" {
      + id = (known after apply)
    }

  # null_resource.IfExitCodeIsNotZeroThenHaltConfigureAdminGroup will be created
  + resource "null_resource" "IfExitCodeIsNotZeroThenHaltConfigureAdminGroup" {
      + id = (known after apply)
    }

  # null_resource.IfExitCodeIsNotZeroThenHaltSecondDDC will be created
  + resource "null_resource" "IfExitCodeIsNotZeroThenHaltSecondDDC" {
      + id = (known after apply)
    }

  # null_resource.IfExitCodeIsNotZeroThenHaltSetLicensing will be created
  + resource "null_resource" "IfExitCodeIsNotZeroThenHaltSetLicensing" {
      + id = (known after apply)
    }

  # null_resource.RunAddSecondDDCPlaybook will be created
  + resource "null_resource" "RunAddSecondDDCPlaybook" {
      + id = (known after apply)
    }

  # null_resource.RunAdditionalSiteConfigurationPlaybook will be created
  + resource "null_resource" "RunAdditionalSiteConfigurationPlaybook" {
      + id = (known after apply)
    }

  # null_resource.RunConfigureAdminGroupPlaybook will be created
  + resource "null_resource" "RunConfigureAdminGroupPlaybook" {
      + id = (known after apply)
    }

  # null_resource.RunConfigureSetLicensingPlaybook will be created
  + resource "null_resource" "RunConfigureSetLicensingPlaybook" {
      + id = (known after apply)
    }

  # null_resource.RunCreateDatabasePlaybook will be created
  + resource "null_resource" "RunCreateDatabasePlaybook" {
      + id = (known after apply)
    }

  # null_resource.RunCreateSitePlaybook will be created
  + resource "null_resource" "RunCreateSitePlaybook" {
      + id = (known after apply)
    }

  # time_sleep.wait_10_seconds will be created
  + resource "time_sleep" "wait_10_seconds" {
      + create_duration = "10s"
      + id              = (known after apply)
    }

  # time_sleep.wait_10_secondsAdditionalSiteConfiguration will be created
  + resource "time_sleep" "wait_10_secondsAdditionalSiteConfiguration" {
      + create_duration = "10s"
      + id              = (known after apply)
    }

  # time_sleep.wait_10_secondsConfigureAdminGroup will be created
  + resource "time_sleep" "wait_10_secondsConfigureAdminGroup" {
      + create_duration = "10s"
      + id              = (known after apply)
    }

  # time_sleep.wait_10_secondsSecondDDC will be created
  + resource "time_sleep" "wait_10_secondsSecondDDC" {
      + create_duration = "10s"
      + id              = (known after apply)
    }

  # time_sleep.wait_300_secondsCDB will be created
  + resource "time_sleep" "wait_300_secondsCDB" {
      + create_duration = "300s"
      + id              = (known after apply)
    }

  # time_sleep.wait_60_secondsCS will be created
  + resource "time_sleep" "wait_60_secondsCS" {
      + create_duration = "60s"
      + id              = (known after apply)
    }

Plan: 20 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.CopyPowerShellScriptsToDDC1: Creating...
null_resource.CopyAnsibleSiteAssetsToAnsibleServer: Creating...
null_resource.CopyAnsibleSiteAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPowerShellScriptsToDDC1: Provisioning with 'file'...
null_resource.CopyAnsibleSiteAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyAnsibleSiteAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyAnsibleSiteAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyAnsibleSiteAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPowerShellScriptsToDDC1: Provisioning with 'file'...
null_resource.CopyAnsibleSiteAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyAnsibleSiteAssetsToAnsibleServer: Creation complete after 1s [id=3364084582988389224]
time_sleep.wait_10_seconds: Creating...
#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;
null_resource.CopyPowerShellScriptsToDDC1: Provisioning with 'file'...
#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;
...
null_resource.CopyPowerShellScriptsToDDC1: Creation complete after 10s [id=6861429796874371568]
time_sleep.wait_10_seconds: Still creating... [00m10s elapsed]
time_sleep.wait_10_seconds: Creation complete after 10s [id=2025-11-12T16:58:47Z]
null_resource.RunCreateDatabasePlaybook: Creating...
null_resource.RunCreateDatabasePlaybook: Provisioning with 'local-exec'...
null_resource.RunCreateDatabasePlaybook (local-exec): Executing: ["/bin/sh" "-c" "ansible-playbook /etc/ansible/assets/CreateDatabases.ansible.yml -i /etc/ansible/assets/inventory.ini -vvv -c winrm; echo $? &gt; /etc/ansible/assets/ansible_exit_code.txt"]

null_resource.RunCreateDatabasePlaybook (local-exec): PLAYBOOK: CreateDatabases.ansible.yml ******************************************
null_resource.RunCreateDatabasePlaybook (local-exec): 1 plays in /etc/ansible/assets/CreateDatabases.ansible.yml

null_resource.RunCreateDatabasePlaybook (local-exec): PLAY [Run PowerShell script on Windows Server as Domain Admin] *****************

null_resource.RunCreateDatabasePlaybook (local-exec): TASK [Run the StoreFront installer PowerShell script as domain user] ***********
null_resource.RunCreateDatabasePlaybook (local-exec): task path: /etc/ansible/assets/CreateDatabases.ansible.yml:20
null_resource.RunCreateDatabasePlaybook (local-exec): Using module file /usr/lib/python3/dist-packages/ansible_collections/community/windows/plugins/modules/win_psexec.ps1
null_resource.RunCreateDatabasePlaybook (local-exec): Pipelining is enabled.
null_resource.RunCreateDatabasePlaybook (local-exec): &lt;tf-cvad-ddc1.the-austrian-citrix-guy.at&gt; ESTABLISH WINRM CONNECTION FOR USER: svc_ansible on PORT 5986 TO tf-cvad-ddc1.the-austrian-citrix-guy.at
null_resource.RunCreateDatabasePlaybook (local-exec): EXEC (via pipeline wrapper)
null_resource.RunCreateDatabasePlaybook (local-exec): changed: [tf-cvad-ddc1.the-austrian-citrix-guy.at] =&gt; {
null_resource.RunCreateDatabasePlaybook (local-exec):     "changed": true,
null_resource.RunCreateDatabasePlaybook (local-exec):     "delta": "0:00:32.533635",
null_resource.RunCreateDatabasePlaybook (local-exec):     "end": "2025-11-12 04:59:20.201075",
null_resource.RunCreateDatabasePlaybook (local-exec):     "invocation": {
null_resource.RunCreateDatabasePlaybook (local-exec):         "module_args": {
null_resource.RunCreateDatabasePlaybook (local-exec):             "chdir": null,
null_resource.RunCreateDatabasePlaybook (local-exec):             "command": "powershell.exe -NoProfile -ExecutionPolicy Bypass -File \"C:\\InstallCVAD\\CreateDatabases.ps1\"",
null_resource.RunCreateDatabasePlaybook (local-exec):             "elevated": true,
null_resource.RunCreateDatabasePlaybook (local-exec):             "executable": "C:\\InstallCVAD\\PsExec64.exe",
null_resource.RunCreateDatabasePlaybook (local-exec):             "hostnames": null,
null_resource.RunCreateDatabasePlaybook (local-exec):             "interactive": true,
null_resource.RunCreateDatabasePlaybook (local-exec):             "limited": false,
null_resource.RunCreateDatabasePlaybook (local-exec):             "nobanner": false,
null_resource.RunCreateDatabasePlaybook (local-exec):             "noprofile": true,
null_resource.RunCreateDatabasePlaybook (local-exec):             "password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
null_resource.RunCreateDatabasePlaybook (local-exec):             "priority": null,
null_resource.RunCreateDatabasePlaybook (local-exec):             "session": null,
null_resource.RunCreateDatabasePlaybook (local-exec):             "system": false,
null_resource.RunCreateDatabasePlaybook (local-exec):             "timeout": null,
null_resource.RunCreateDatabasePlaybook (local-exec):             "username": "TACG\\administrator",
null_resource.RunCreateDatabasePlaybook (local-exec):             "wait": true
null_resource.RunCreateDatabasePlaybook (local-exec):         }
null_resource.RunCreateDatabasePlaybook (local-exec):     },
null_resource.RunCreateDatabasePlaybook (local-exec):     "psexec_command": "C:\\InstallCVAD\\PsExec64.exe -u TACG\\administrator -p *PASSWORD_REPLACED* -e -h -i -accepteula powershell.exe -NoProfile -ExecutionPolicy Bypass -File \"C:\\InstallCVAD\\CreateDatabases.ps1\"",
null_resource.RunCreateDatabasePlaybook (local-exec):     "rc": 0,
null_resource.RunCreateDatabasePlaybook (local-exec):     "start": "2025-11-12 04:58:47.667440",
null_resource.RunCreateDatabasePlaybook (local-exec):     "stderr": "Connecting to local system...\r\r\rStarting PSEXESVC service on local system...\r\r\rCopying authentication key to TF-CVAD-DDC1...\r\r\rConnecting with PsExec service on TF-CVAD-DDC1...\r\r\rStarting powershell.exe on TF-CVAD-DDC1...\r\r\r\r\npowershell.exe exited on TF-CVAD-DDC1 with error code 0.\r\n",
null_resource.RunCreateDatabasePlaybook (local-exec):     "stderr_lines": [
null_resource.RunCreateDatabasePlaybook (local-exec):         "Connecting to local system...",
null_resource.RunCreateDatabasePlaybook (local-exec):         "",
null_resource.RunCreateDatabasePlaybook (local-exec):         "",
null_resource.RunCreateDatabasePlaybook (local-exec):         "Starting PSEXESVC service on local system...",
null_resource.RunCreateDatabasePlaybook (local-exec):         "",
null_resource.RunCreateDatabasePlaybook (local-exec):         "",
null_resource.RunCreateDatabasePlaybook (local-exec):         "Copying authentication key to TF-CVAD-DDC1...",
null_resource.RunCreateDatabasePlaybook (local-exec):         "",
null_resource.RunCreateDatabasePlaybook (local-exec):         "",
null_resource.RunCreateDatabasePlaybook (local-exec):         "Connecting with PsExec service on TF-CVAD-DDC1...",
null_resource.RunCreateDatabasePlaybook (local-exec):         "",
null_resource.RunCreateDatabasePlaybook (local-exec):         "",
null_resource.RunCreateDatabasePlaybook (local-exec):         "Starting powershell.exe on TF-CVAD-DDC1...",
null_resource.RunCreateDatabasePlaybook (local-exec):         "",
null_resource.RunCreateDatabasePlaybook (local-exec):         "",
null_resource.RunCreateDatabasePlaybook (local-exec):         "",
null_resource.RunCreateDatabasePlaybook (local-exec):         "powershell.exe exited on TF-CVAD-DDC1 with error code 0."
null_resource.RunCreateDatabasePlaybook (local-exec):     ],
null_resource.RunCreateDatabasePlaybook (local-exec):     "stdout": "\r\nPsExec v2.43 - Execute processes remotely\r\nCopyright (C) 2001-2023 Mark Russinovich\r\nSysinternals - www.sysinternals.com\r\n\r\n",
null_resource.RunCreateDatabasePlaybook (local-exec):     "stdout_lines": [
null_resource.RunCreateDatabasePlaybook (local-exec):         "",
null_resource.RunCreateDatabasePlaybook (local-exec):         "PsExec v2.43 - Execute processes remotely",
null_resource.RunCreateDatabasePlaybook (local-exec):         "Copyright (C) 2001-2023 Mark Russinovich",
null_resource.RunCreateDatabasePlaybook (local-exec):         "Sysinternals - www.sysinternals.com",
null_resource.RunCreateDatabasePlaybook (local-exec):         ""
null_resource.RunCreateDatabasePlaybook (local-exec):     ]
null_resource.RunCreateDatabasePlaybook (local-exec): }

null_resource.RunCreateDatabasePlaybook (local-exec): PLAY RECAP *********************************************************************
null_resource.RunCreateDatabasePlaybook (local-exec): tf-cvad-ddc1.the-austrian-citrix-guy.at : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

null_resource.RunCreateDatabasePlaybook: Creation complete after 35s [id=5576848404034541586]
data.external.CheckAnsibleReturnCodeCDB: Reading...
data.external.CheckAnsibleReturnCodeCDB: Read complete after 0s [id=-]
null_resource.IfExitCodeIsNotZeroThenHaltCDB: Creating...
null_resource.IfExitCodeIsNotZeroThenHaltCDB: Provisioning with 'local-exec'...
null_resource.IfExitCodeIsNotZeroThenHaltCDB (local-exec): Executing: ["/bin/bash" "-c" "[ \"1\" = \"1\" ] &amp;&amp; echo \"Create Databases - Everything OK\" || { echo \"Deploying the CVAD Databases failed. Please review the settings. Halting Terraform.\"; exit 1; }"]
null_resource.IfExitCodeIsNotZeroThenHaltCDB (local-exec): Create Databases - Everything OK
null_resource.IfExitCodeIsNotZeroThenHaltCDB: Creation complete after 0s [id=449387280135483830]
...
Running all Playbooks looks similar, omitted due to the length of the output.
...
null_resource.RunConfigureSetLicensingPlaybook (local-exec): PLAY RECAP *********************************************************************
null_resource.RunConfigureSetLicensingPlaybook (local-exec): tf-cvad-ddc1.the-austrian-citrix-guy.at : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

null_resource.RunConfigureSetLicensingPlaybook: Creation complete after 13s [id=5106544801416722566]
data.external.CheckAnsibleReturnCodeSetLicensing: Reading...
data.external.CheckAnsibleReturnCodeSetLicensing: Read complete after 0s [id=-]
null_resource.IfExitCodeIsNotZeroThenHaltSetLicensing: Creating...
null_resource.IfExitCodeIsNotZeroThenHaltSetLicensing: Provisioning with 'local-exec'...
null_resource.IfExitCodeIsNotZeroThenHaltSetLicensing (local-exec): Executing: ["/bin/bash" "-c" "[ \"1\" = \"1\" ] &amp;&amp; echo \"Licensing Configuration - Everything OK\" || { echo \"Configuration of Licensing failed. Please review the settings. Halting Terraform.\"; exit 1; }"]
null_resource.IfExitCodeIsNotZeroThenHaltSetLicensing (local-exec): Licensing Configuration - Everything OK
null_resource.IfExitCodeIsNotZeroThenHaltSetLicensing: Creation complete after 0s [id=1454545395290283154]

Apply complete! Resources: 26 added, 0 changed, 0 destroyed.
azadmin@tacg-cicd:/etc/terraform-tmm/CVADXS/_ConfigureSite$The Terraform snippet has successfully created the site and all needed configurations. The CVAD site was successfully created:  All site tests are successful:  With the CVAD site ready, we can proceed to the next step.  Part 9: Using Terraform to create the StoreFront deployment and all its necessitiesCreating a StoreFront deployment is a simple, straightforward task. Terraform can create the Storefront site on its own – no Ansible interaction is needed. Extreme Caution As already mentioned, this module, Module 9, must be run on a Windows-based, domain-joined VM due to limitations in StoreFront´s automation capabilities. Terraform will sequentially run the following tasks: Create the StoreFront deployment Create a StoreFront Authentication service Create a StoreFront Store service Create a StoreFront WebReceiver service  Warning The current Automation snippets of StoreFront will only run on Windows Powershell &lt;=5.1. They will not run on Powershell &gt;=6. Be sure to start the Terraform code on the correct PowerShell version. You may encounter this error after starting the Terraform snippet: Error: Error fetching StoreFront version
│
│   with citrix_stf_webreceiver_service.CreateSFWebReceiverService,
│   on CVADOnXS-ConfigureStoreFront.tf line 48, in resource "citrix_stf_webreceiver_service" "CreateSFWebReceiverService":
│   48: resource "citrix_stf_webreceiver_service" "CreateSFWebReceiverService" {
│
│ Error message: error executing command: Get-STFVersion
│  Error Message:
│  ConvertTo-SecureString : "ConvertTo-SecureString" was found in module "Microsoft.PowerShell.Security",
│ we were unable to load the module. Run "Import-Module
│ Microsoft.PowerShell.Security".
│ In row:1 column:205
│ + ... n@the-austrian-citrix-guy.at',(ConvertTo-SecureString -Force  ...
│ +                                            ~~~~~~~~~~~~~~~~~~~~~~
│     + CategoryInfo          : ObjectNotFound: (ConvertTo-SecureString:String) [], CommandNotFoundException
│     + FullyQualifiedErrorId : CouldNotAutoloadMatchingModuleThat error means you have not loaded the module Microsoft.PowerShell.Security in Windows PowerShell. Be sure to load the module in Windows PowerShell, not PowerShell: PS C:\__PPMM\ConfigureStoreFront&gt; Import-Module Microsoft.PowerShell.Security After loading the module in Windows PowerShell, the error should be gone and the Terraform snippet run through. Before running the snippet, the StoreFront server has no StoreFront site configured. Example Terraform snippet to create a StoreFront site: # Terraform Deployment of Citrix CVAD on XenServer 8 - 25.11 
## Definition of XenServer Provider Variables - not all available variables are defined, only these we use
### Reference https://registry.terraform.io/providers/xenserver/xenserver/latest/docs
#### Configuring StoreFront 
##### Reference https://github.com/citrix/terraform-provider-citrix/blob/main/StoreFront.md
###### Create an initial StoreFront Deployment
resource "citrix_stf_deployment" "CreateInitialSFDeployment" {
  site_id       = var.TACG-TMM-SF-SiteID
  host_base_url = var.TACG-TMM-SF-SiteURL
  roaming_gateway = [
    {
      name                         = var.TACG-TMM-SF-Gateway-Name
      logon_type                   = var.TACG-TMM-SF-Gateway-LogonType
      gateway_url                  = var.TACG-TMM-SF-Gateway-URL
      callback_url                 = var.TACG-TMM-SF-Gateway-CallbackURL
      subnet_ip_address            = var.TACG-TMM-SF-Gateway-SNIP
      secure_ticket_authority_urls = var.TACG-TMM-SF-Gateway-STAs
    }
  ]
  roaming_beacon = {
    internal_address   = var.TACG-TMM-SF-Beacon-Internal
    external_addresses = var.TACG-TMM-SF-Beacon-External
  }
}

##### Create an Authentication service
resource "citrix_stf_authentication_service" "CreateSFAuthenticationService" {
  depends_on    = [citrix_stf_deployment.CreateInitialSFDeployment]
  site_id       = citrix_stf_deployment.CreateInitialSFDeployment.site_id
  friendly_name = var.TACG-TMM-SF-AuthService-Name
  virtual_path  = var.TACG-TMM-SF-AuthService-Path
}

##### Create a Store service
resource "citrix_stf_store_service" "CreateSFStoreService" {
  depends_on                          = [citrix_stf_authentication_service.CreateSFAuthenticationService]
  site_id                             = citrix_stf_deployment.CreateInitialSFDeployment.site_id
  virtual_path                        = var.TACG-TMM-SF-StoreService-Path
  friendly_name                       = var.TACG-TMM-SF-StoreService-Name
  authentication_service_virtual_path = citrix_stf_authentication_service.CreateSFAuthenticationService.virtual_path
  pna = {
    enable = false
  }
  farms = var.TACG-TMM-SF-StoreService-Farms
}

##### Create a WebReceiver service
resource "citrix_stf_webreceiver_service" "CreateSFWebReceiverService" {
  depends_on         = [citrix_stf_deployment.CreateInitialSFDeployment, citrix_stf_store_service.CreateSFStoreService]
  site_id            = citrix_stf_deployment.CreateInitialSFDeployment.site_id
  friendly_name      = var.TACG-TMM-SF-WebService-Name
  virtual_path       = var.TACG-TMM-SF-WebService-Path
  store_virtual_path = citrix_stf_store_service.CreateSFStoreService.virtual_path
  authentication_methods = [
    "ExplicitForms",
    "CitrixAGBasic"
  ]
  plugin_assistant = {
    enabled                 = var.TACG-TMM-SF-WebService-PA-Enabled
    html5_single_tab_launch = var.TACG-TMM-SF-WebService-PA-HTML5SingleTab
    upgrade_at_login        = var.TACG-TMM-SF-WebService-PA-UpgradeAtLogin
    html5_enabled           = var.TACG-TMM-SF-WebService-PA-HTML5Enabled
  }
  application_shortcuts = {
    prompt_for_untrusted_shortcuts = var.TACG-TMM-SF-WebService-AppSC-Prompt
    trusted_urls                   = var.TACG-TMM-SF-WebService-AppSC-TrustedURLs
  }
  user_interface = {
    auto_launch_desktop     = var.TACG-TMM-SF-WebService-UI-AutoLaunchDesktop
    multi_click_timeout     = var.TACG-TMM-SF-WebService-UI-Timeout
    enable_apps_folder_view = var.TACG-TMM-SF-WebService-UI-AppFolderView
    workspace_control = {
      enabled                 = var.TACG-TMM-SF-WebService-UI-WC-Enabled
      auto_reconnect_at_logon = var.TACG-TMM-SF-WebService-UI-WC-AutoReconnect
      logoff_action           = var.TACG-TMM-SF-WebService-UI-LogoffAction
      show_reconnect_button   = var.TACG-TMM-SF-WebService-UI-WC-ShowReconnect
      show_disconnect_button  = var.TACG-TMM-SF-WebService-UI-WC-ShowDisconnect
    }
    receiver_configuration = {
      enabled = var.TACG-TMM-SF-WebService-RC-Enabled
    }
    app_shortcuts = {
      enabled               = var.TACG-TMM-SF-WebService-ASC-Enabled
      show_desktop_shortcut = var.TACG-TMM-SF-WebService-ASC-ShowDesktopShortcut
    }
    ui_views = {
      show_apps_view     = var.TACG-TMM-SF-WebService-UIViews-Apps
      show_desktops_view = var.TACG-TMM-SF-WebService-UIViews-Desktops
      default_view       = var.TACG-TMM-SF-WebService-UIViews-Default
    }
    category_view_collapsed   = var.TACG-TMM-SF-WebService-CategoryView
    move_app_to_uncategorized = var.TACG-TMM-SF-WebService-Uncategorized
    progressive_web_app = {
      enabled             = var.TACG-TMM-SF-WebService-PWA-Enabled
      show_install_prompt = var.TACG-TMM-SF-WebService-PWA-Install
    }
    show_activity_manager = var.TACG-TMM-SF-WebService-ActivityManager
    show_first_time_use   = var.TACG-TMM-SF-WebService-FirstTimeUse
    prevent_ica_downloads = var.TACG-TMM-SF-WebService-PreventICADownloads
  }
  resources_service = {
    ica_file_cache_expiry         = var.TACG-TMM-SF-WebService-RS-IcaFileExpiry
    persistent_icon_cache_enabled = var.TACG-TMM-SF-WebService-RS-IconCacheEnabled
  }
}

##### A PNA site is not installed due to no effective needs
#resource "citrix_stf_xenapp_default_store" "CreateSFXenAppPath" {
#  depends_on         = [citrix_stf_store_service.CreateSFStoreService]
#  store_virtual_path = citrix_stf_store_service.CreateSFStoreService.virtual_path
#  store_site_id      = citrix_stf_store_service.CreateSFStoreService.site_id
#}
Running the snippet should complete all required steps – during the run, the StoreFront server installs any missing roles and features. PS C:\_ppmm\__TF\CVADOnXS-2511\_ConfigureStoreFront&gt; terraform apply

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_stf_authentication_service.CreateSFAuthenticationService will be created
  + resource "citrix_stf_authentication_service" "CreateSFAuthenticationService" {
      + claims_factory_name = "standardClaimsFactory"
      + friendly_name       = "TACG-SF-AuthService"
      + site_id             = "1"
      + virtual_path        = "/Citrix/Authentication"
    }

  # citrix_stf_deployment.CreateInitialSFDeployment will be created
  + resource "citrix_stf_deployment" "CreateInitialSFDeployment" {
      + host_base_url   = "https://storefront.the-austrian-citrix-guy.at"
      + roaming_beacon  = {
          + external_addresses = [
              + "https://www.orf.at/",
              + "https://www.microsoft.com/",
            ]
          + internal_address   = "http://10.10.100.1/"
        }
      + roaming_gateway = [
          + {
              + callback_url                   = "https://access.the-austrian-citrix-guy.at/CitrixAuthService/AuthService.asmx"
              + gateway_url                    = "https://access.the-austrian-citrix-guy.at/"
              + is_cloud_gateway               = false
              + logon_type                     = "Domain"
              + name                           = "TACG-NS"
              + request_ticket_from_two_stas   = false
              + secure_ticket_authority_urls   = [
                  + {
                      + sta_url                = "http://tf-cvad-ddc1.the-austrian-citrix-guy.at/scripts/ctxsta.dll"
                      + sta_validation_enabled = false
                    },
                  + {
                      + sta_url                = "http://tf-cvad-ddc2.the-austrian-citrix-guy.at/scripts/ctxsta.dll"
                      + sta_validation_enabled = false
                    },
                ]
              + session_reliability            = false
              + smart_card_fallback_logon_type = "None"
              + stas_bypass_duration           = "0.1:0:0"
              + stas_use_load_balancing        = false
              + subnet_ip_address              = "10.10.119.112"
              + version                        = "Version10_0_69_4"
            },
        ]
      + site_id         = "1"
    }

  # citrix_stf_store_service.CreateSFStoreService will be created
  + resource "citrix_stf_store_service" "CreateSFStoreService" {
      + authentication_service_virtual_path = "/Citrix/Authentication"
      + farms                               = [
          + {
              + all_failed_bypass_duration     = 0
              + bypass_duration                = 60
              + farm_name                      = "TACG80"
              + farm_type                      = "XenDesktop"
              + load_balance                   = true
              + max_failed_servers_per_request = 0
              + port                           = 80
              + rade_ticket_time_to_live       = 100
              + server_urls                    = []
              + servers                        = [
                  + "tf-cvad-sf.the-austrian-citrix-guy.at",
                ]
              + ssl_relay_port                 = 443
              + ticket_time_to_live            = 200
              + transport_type                 = "HTTPS"
              + xml_validation_enabled         = false
              + zones                          = []
                # (4 unchanged attributes hidden)
            },
          + {
              + all_failed_bypass_duration     = 0
              + bypass_duration                = 60
              + farm_name                      = "TACG443"
              + farm_type                      = "XenDesktop"
              + load_balance                   = true
              + max_failed_servers_per_request = 0
              + port                           = 443
              + rade_ticket_time_to_live       = 100
              + server_urls                    = []
              + servers                        = [
                  + "tf-cvad-sf.the-austrian-citrix-guy.at",
                ]
              + ssl_relay_port                 = 443
              + ticket_time_to_live            = 200
              + transport_type                 = "HTTPS"
              + xml_validation_enabled         = false
              + zones                          = []
                # (4 unchanged attributes hidden)
            },
        ]
      + friendly_name                       = "TACG-SF-StoreService"
      + pna                                 = {
          + enable = false
        }
      + site_id                             = "1"
      + virtual_path                        = "/Citrix/Store"
    }

  # citrix_stf_webreceiver_service.CreateSFWebReceiverService will be created
  + resource "citrix_stf_webreceiver_service" "CreateSFWebReceiverService" {
      + application_shortcuts  = {
          + gateway_urls                   = []
          + prompt_for_untrusted_shortcuts = true
          + trusted_urls                   = [
              + "https://storefront.the-austrian-citrix-guy.at/",
            ]
        }
      + authentication_methods = [
          + "CitrixAGBasic",
          + "ExplicitForms",
        ]
      + friendly_name          = "TACG-SF-WebReceiverService"
      + plugin_assistant       = {
          + enabled                 = true
          + html5_enabled           = "Fallback"
          + html5_single_tab_launch = true
          + show_after_login        = false
          + upgrade_at_login        = true
        }
      + resources_service      = {
          + ica_file_cache_expiry         = 80
          + icon_size                     = 128
          + persistent_icon_cache_enabled = true
          + show_desktop_viewer           = true
        }

      + site_id                = "1"
      + store_virtual_path     = "/Citrix/Store"
      + user_interface         = {
          + app_shortcuts             = {
              + allow_session_reconnect = false
            }
          + auto_launch_desktop       = true
          + category_view_collapsed   = false
          + enable_apps_folder_view   = true
          + move_app_to_uncategorized = true
          + multi_click_timeout       = 3
          + prevent_ica_downloads     = false
          + progressive_web_app       = {
              + enabled             = false
              + show_install_prompt = false
            }
          + receiver_configuration    = {
              + download_url = (known after apply)
              + enabled      = true
            }
          + show_activity_manager     = true
          + show_first_time_use       = true
          + ui_views                  = {
              + default_view       = "Auto"
              + show_apps_view     = true
              + show_desktops_view = true
            }
          + workspace_control         = {
              + auto_reconnect_at_logon = true
              + enabled                 = true
              + logoff_action           = "Disconnect"
              + show_disconnect_button  = false
              + show_reconnect_button   = false
            }
        }
      + virtual_path           = "/Citrix/StoreWeb"
    }

Plan: 4 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_stf_deployment.CreateInitialSFDeployment: Creating...
citrix_stf_deployment.CreateInitialSFDeployment: Still creating... [00m10s elapsed]
citrix_stf_deployment.CreateInitialSFDeployment: Still creating... [00m20s elapsed]
citrix_stf_deployment.CreateInitialSFDeployment: Creation complete after 25s
citrix_stf_authentication_service.CreateSFAuthenticationService: Creating...
citrix_stf_authentication_service.CreateSFAuthenticationService: Creation complete after 6s
citrix_stf_store_service.CreateSFStoreService: Creating...
citrix_stf_store_service.CreateSFStoreService: Still creating... [00m10s elapsed]
citrix_stf_store_service.CreateSFStoreService: Creation complete after 16s
citrix_stf_webreceiver_service.CreateSFWebReceiverService: Creating...
citrix_stf_webreceiver_service.CreateSFWebReceiverService: Still creating... [00m10s elapsed]
citrix_stf_webreceiver_service.CreateSFWebReceiverService: Creation complete after 15s

Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
PS C:\_ppmm\__TF\CVADOnXS-2511\_ConfigureStoreFront&gt;The StoreFront server now has a valid StoreFront site deployed:  With the StoreFront site ready, we can proceed to the final step: deploying all CVAD-related entities.  Important You can now switch back to the Ubuntu IaC machine or stay on the Windows machine to run the last Terraform snippet.  Part 10: Using Terraform to create all CVAD entities and all their necessitiesCreating the final steps is a simple, straightforward task. Terraform can create the CVAD entities site on its own – no Ansible interaction is needed. Create a StoreFront Server object Create a Hypervisor connection Create a Hypervisor connection resource pool Create a Machine Catalog Create a Delivery Group Create a Policy Set and its policies Before running the snippet, no CVAD-related entity is configured: No Hypervisor Connection and Hypervisor Connection pool exist, no Machine Catalog exists, no Delivery Group exists, and no dedicated Policy Set exists: Example Terraform snippet to create all needed CVAD entities as mentioned above: # Terraform Deployment of Citrix CVAD on XenServer 8 - 25.11 
## Definition of XenCVADServer Provider Variables - not all available variables are defined, only these we use
### Reference https://registry.terraform.io/providers/citrix/citrix/latest/docs
#### Definition of all required local variables or data
##### Get XenServer-related Data
###### Get the VM SR object
data "xenserver_sr" "vmsr" {
  name_label = var.TACG-TMM-XS-CL-VMSR
}

/* output "name" {
  value = data.xenserver_sr.vmsr.data_items[0].name_label
} */

###### Get the Network object
data "xenserver_network" "network" {
}

##### Get CVAD-related Data
###### Get the Zone in CVAD
data "citrix_zone" "TACG-TMM-CVAD-Zone" {
  name = var.TACG-TMM-CVAD-Zone-Name
}

#### Create a StoreFront Server object - we assume a load-balanced server
resource "citrix_storefront_server" "CreateSFServer" {
  name        = var.TACG-TMM-SF-Server-Name
  description = var.TACG-TMM-SF-Server-Description
  url         = var.TACG-TMM-SF-Server-URL
  enabled     = true
}

#### Create a Hypervisor Connection
resource "citrix_xenserver_hypervisor" "TACG-TMM-CVAD-HypConn" {
  depends_on      = [data.citrix_zone.TACG-TMM-CVAD-Zone]
  name            = var.TACG-TMM-CVAD-HypConn-Name
  zone            = data.citrix_zone.TACG-TMM-CVAD-Zone.id
  username        = var.XS-CL-UN
  password        = var.XS-CL-PW
  password_format = "PlainText"
  addresses       = var.XS-CL-IPs
  ssl_thumbprints = var.XS-CL-SSLTP
}

resource "null_resource" "WriteProgress1" {
  depends_on = [citrix_xenserver_hypervisor.TACG-TMM-CVAD-HypConn]
  provisioner "local-exec" {
    command = "echo The Hypervisor Connection was successfully created..."
  }
}

#### Create a Hypervisor Connection Resource Pool
resource "citrix_xenserver_hypervisor_resource_pool" "TACG-TMM-CVAD-HypConnPool" {
  depends_on = [citrix_xenserver_hypervisor.TACG-TMM-CVAD-HypConn]
  name       = var.TACG-TMM-CVAD-HypConnPool-Name
  hypervisor = citrix_xenserver_hypervisor.TACG-TMM-CVAD-HypConn.id
  networks   = [data.xenserver_network.network.data_items[0].name_label]

  storage = [
    {
      storage_name = data.xenserver_sr.vmsr.data_items[0].name_label
    }
  ]
  temporary_storage = [
    {
      storage_name = data.xenserver_sr.vmsr.data_items[0].name_label
    }
  ]
  use_local_storage_caching = false
}

resource "null_resource" "WriteProgress2" {
  depends_on = [citrix_xenserver_hypervisor_resource_pool.TACG-TMM-CVAD-HypConnPool]
  provisioner "local-exec" {
    command = "echo The Hypervisor Connection Pool was successfully created..."
  }
}

#### Create the Machine Catalog
##### Important: Using CVAD Image Definitions and Image Versions is currently not supported on a XenServer-based deployment.
##### Important: You need to use standard Master Image VMs/Snapshots for creation of a Machine Catalog!
resource "citrix_machine_catalog" "TACG-TMM-CVAD-MC" {
  depends_on        = [citrix_xenserver_hypervisor_resource_pool.TACG-TMM-CVAD-HypConnPool]
  name              = var.TACG-TMM-CVAD-MC-Name
  description       = var.TACG-TMM-CVAD-MC-Description
  provisioning_type = var.TACG-TMM-CVAD-MC-ProvisioningType
  allocation_type   = var.TACG-TMM-CVAD-MC-AllocationType
  session_support   = var.TACG-TMM-CVAD-MC-SessionType
  zone              = data.citrix_zone.TACG-TMM-CVAD-Zone.id
  provisioning_scheme = {
    hypervisor               = citrix_xenserver_hypervisor.TACG-TMM-CVAD-HypConn.id
    hypervisor_resource_pool = citrix_xenserver_hypervisor_resource_pool.TACG-TMM-CVAD-HypConnPool.id
    identity_type            = var.TACG-TMM-CVAD-MC-IdentityType
    machine_domain_identity = {
      domain                   = var.TACG-TMM-CVAD-MC-VM-DomainFQDN
      domain_ou                = var.TACG-TMM-CVAD-MC-VM-DomainOU
      service_account          = var.TACG-TMM-CVAD-MC-VM-ServiceUserName
      service_account_password = var.TACG-TMM-CVAD-MC-VM-ServiceUserPassword
    }
    xenserver_machine_config = {
      master_image_vm                  = var.TACG-TMM-CVAD-MC-MasterImageVMName
      cpu_count                        = var.TACG-TMM-CVAD-MC-VM-CPUCount
      memory_mb                        = var.TACG-TMM-CVAD-MC-VM-MemorySize
      image_snapshot                   = var.TACG-TMM-CVAD-MC-MasterImageVMName-SnapshotName
      use_full_disk_clone_provisioning = var.TACG-TMM-CVAD-MC-MasterImageVMName-UseFullDiskClone
    }
    number_of_total_machines = var.TACG-TMM-CVAD-MC-VM-NumberOfVMs
    machine_account_creation_rules = {
      naming_scheme      = var.TACG-TMM-CVAD-MC-VM-NamingScheme
      naming_scheme_type = var.TACG-TMM-CVAD-MC-VM-NamingSchemeType
    }
  }
}

resource "null_resource" "WriteProgress3" {
  depends_on = [citrix_machine_catalog.TACG-TMM-CVAD-MC]
  provisioner "local-exec" {
    command = "echo The Machine Catalog was successfully created..."
  }
}

/* data "citrix_machine_catalog" "TACG-TMM-CVAD-MC" {
  name = var.TACG-TMM-CVAD-MC-Name
} */

#### Create the Delivery Group
resource "citrix_delivery_group" "TACG-TMM-CVAD-DG" {
  depends_on = [citrix_machine_catalog.TACG-TMM-CVAD-MC, citrix_storefront_server.CreateSFServer]
  #depends_on               = [data.citrix_machine_catalog.TACG-TMM-CVAD-MC]
  name                     = var.TACG-TMM-CVAD-DG-Name
  description              = var.TACG-TMM-CVAD-DG-Description
  minimum_functional_level = var.TACG-TMM-CVAD-DG-FunctionalLevel
  storefront_servers       = [citrix_storefront_server.CreateSFServer.id]
  associated_machine_catalogs = [
    {
      machine_catalog = citrix_machine_catalog.TACG-TMM-CVAD-MC.id
      #machine_catalog = data.citrix_machine_catalog.TACG-TMM-CVAD-MC.id
      machine_count = var.TACG-TMM-CVAD-MC-VM-NumberOfVMs
    }
  ]
  desktops = [
    {
      published_name = var.TACG-TMM-CVAD-DG-Desktops-Name
      description    = var.TACG-TMM-CVAD-DG-Desktops-Description
      restricted_access_users = {
        allow_list = var.TACG-TMM-CVAD-DG-Desktops-AllowList
      }
      enabled = var.TACG-TMM-CVAD-DG-Desktops-Enabled

      ###### Not needed due to Static assignment type-MC
      # enable_session_roaming = var.TACG-TMM-CVAD-DG-Desktops-SessionRoaming
    }

  ]
  autoscale_settings = {
    autoscale_enabled                   = var.TACG-TMM-CVAD-DG-AS-Enabled
    peak_disconnect_timeout_minutes     = var.TACG-TMM-CVAD-DG-AS-PeakDisconnectTime
    off_peak_disconnect_timeout_minutes = var.TACG-TMM-CVAD-DG-AS-OffPeakDisconnectTime
    peak_disconnect_action              = var.TACG-TMM-CVAD-DG-AS-PeakDisconnectAction
    off_peak_disconnect_action          = var.TACG-TMM-CVAD-DG-AS-OffPeakDisconnectAction
    off_peak_log_off_timeout_minutes    = var.TACG-TMM-CVAD-DG-AS-PeakLogoffTime
    peak_log_off_timeout_minutes        = var.TACG-TMM-CVAD-DG-AS-OffPeakLogoffTime
    peak_log_off_action                 = var.TACG-TMM-CVAD-DG-AS-PeakDisconnectAction
    off_peak_log_off_action             = var.TACG-TMM-CVAD-DG-AS-OffPeakDisconnectAction

    ###### Not needed due to Static assignment type-MC
    #log_off_off_peak_disconnected_session_after_seconds = var.TACG-TMM-CVAD-DG-AS-PeakLogoffTime
    #log_off_peak_disconnected_session_after_seconds     = var.TACG-TMM-CVAD-DG-AS-OffPeakLogoffTime
    #peak_log_off_action                                 = var.TACG-TMM-CVAD-DG-AS-PeakDisconnectAction
    #off_peak_log_off_action                             = var.TACG-TMM-CVAD-DG-AS-OffPeakDisconnectAction
    #log_off_reminder_enabled                            = var.TACG-TMM-CVAD-DG-AS-Enabled
    #log_off_reminder_message                            = var.TACG-TMM-CVAD-DG-RS-Notification-MessageBody
    #log_off_reminder_title                              = var.TACG-TMM-CVAD-DG-AS-Notification-MessageTitle
    #log_off_warning_message                             = var.TACG-TMM-CVAD-DG-RS-Notification-MessageBody
    #log_off_warning_title                               = var.TACG-TMM-CVAD-DG-AS-Notification-MessageTitle
    timezone = var.TACG-TMM-CVAD-DG-AS-TimeZone

    power_time_schemes = [
      {
        days_of_week          = var.TACG-TMM-CVAD-DG-AS-Days
        display_name          = var.TACG-TMM-CVAD-DG-AS-Name
        peak_time_ranges      = var.TACG-TMM-CVAD-DG-AS-PeakTime
        pool_using_percentage = false
      },
    ]
  }
  restricted_access_users = {
    allow_list = var.TACG-TMM-CVAD-DG-Desktops-AllowList
  }
  reboot_schedules = [
    {
      name                    = var.TACG-TMM-CVAD-DG-RS-Name
      reboot_schedule_enabled = var.TACG-TMM-CVAD-DG-RS-Enabled
      frequency               = var.TACG-TMM-CVAD-DG-RS-Frequency
      frequency_factor        = 1
      days_in_week            = var.TACG-TMM-CVAD-DG-RS-RebootDays
      start_time              = var.TACG-TMM-CVAD-DG-RS-RebootStartTime
      start_date              = var.TACG-TMM-CVAD-DG-RS-RebootStartDate
      reboot_duration_minutes = var.TACG-TMM-CVAD-DG-RS-RebootDuration
      ignore_maintenance_mode = var.TACG-TMM-CVAD-DG-RS-IgnoreMaintenanceMode
      natural_reboot_schedule = var.TACG-TMM-CVAD-DG-RS-NormalRebootSchedule

      ###### Not needed due to SingleSession type-MC
      #reboot_notification_to_users = {
      #notification_duration_minutes       = var.TACG-TMM-CVAD-DG-RS-Notification-Duration
      #notification_message                = var.TACG-TMM-CVAD-DG-RS-Notification-MessageBody
      #notification_title                  = var.TACG-TMM-CVAD-DG-RS-Notification-MessageTitle
      #notification_repeat_every_5_minutes = var.TACG-TMM-CVAD-DG-RS-Notification-MessageRepeat
      #} 
    }
  ]
}

resource "null_resource" "WriteProgress4" {
  depends_on = [citrix_delivery_group.TACG-TMM-CVAD-DG]
  provisioner "local-exec" {
    command = "echo The Delivery Group was successfully created..."
  }
}

#### Create a default Policy Set
resource "citrix_policy_set_v2" "TACG-TMM-CVAD-DefaultPolicySet" {
  depends_on      = [citrix_delivery_group.TACG-TMM-CVAD-DG]
  name            = var.TACG-TMM-CVAD-PS-Name
  description     = var.TACG-TMM-CVAD-PS-Description
  delivery_groups = [citrix_delivery_group.TACG-TMM-CVAD-DG.id]
}

#### Create a Policy in the Policy Set
resource "citrix_policy" "TACG-TMM-CVAD-DefaultPolicySet-Printing" {
  depends_on    = [citrix_policy_set_v2.TACG-TMM-CVAD-DefaultPolicySet]
  policy_set_id = citrix_policy_set_v2.TACG-TMM-CVAD-DefaultPolicySet.id
  name          = "Printing"
  description   = "Example of Printer-related policy in default Policy Set"
  enabled       = true
}

#### Create a Policy in the Policy Set
resource "citrix_policy" "TACG-TMM-CVAD-DefaultPolicySet-HDX" {
  depends_on    = [citrix_policy_set_v2.TACG-TMM-CVAD-DefaultPolicySet]
  policy_set_id = citrix_policy_set_v2.TACG-TMM-CVAD-DefaultPolicySet.id
  name          = "HDX™ Graphics"
  description   = "Example of HDX Graphics-related policy in default Policy Set"
  enabled       = true
}

#### Create a Policy in the Policy Set
resource "citrix_policy" "TACG-TMM-CVAD-DefaultPolicySet-ClientDrives" {
  depends_on    = [citrix_policy_set_v2.TACG-TMM-CVAD-DefaultPolicySet]
  policy_set_id = citrix_policy_set_v2.TACG-TMM-CVAD-DefaultPolicySet.id
  name          = "Client Drives"
  description   = "Example of Client Drive-related policy in default Policy Set"
  enabled       = true
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-Printing1" {
  policy_id   = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-Printing.id
  name        = "ClientPrinterAutoCreation"
  value       = "DefaultPrinterOnly"
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-Printing2" {
  policy_id   = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-Printing.id
  name        = "UniversalPrintDriverUsage"
  value       = "FallbackToSpecific"
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-HDX1" {
  policy_id   = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-HDX.id
  name        = "AllowVisuallyLosslessCompression"
  enabled     = true
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-HDX2" {
  policy_id   = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-HDX.id
  name        = "UseVideoCodecForCompression"
  value       = "UseVideoCodecIfPreferred"
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-HDX3" {
  policy_id   = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-HDX.id
  name        = "UseHardwareEncodingForVideoCodec"
  enabled     = true
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-CD1" {
  policy_id   = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-ClientDrives.id
  name        = "AutoConnectDrives"
  enabled     = true
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-CD2" {
  policy_id   = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-ClientDrives.id
  name        = "ClientDriveRedirection"
  enabled     = true
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-CD3" {
  policy_id   = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-ClientDrives.id
  name        = "ClientFixedDrives"
  enabled     = true
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-CD4" {
  policy_id   = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-ClientDrives.id
  name        = "ClientFloppyDrives"
  enabled     = false
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-CD5" {
  policy_id   = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-ClientDrives.id
  name        = "ClientOpticalDrives"
  enabled     = false
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-CD6" {
  policy_id   = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-ClientDrives.id
  name        = "ClientNetworkDrives"
  enabled     = false
  use_default = false
}

#### Create the corresponding Policy Filters
resource "citrix_delivery_group_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-DGFilter-1" {
  depends_on        = [citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-Printing]
  policy_id         = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-Printing.id
  enabled           = true
  allowed           = true
  delivery_group_id = citrix_delivery_group.TACG-TMM-CVAD-DG.id
}

#### Create the corresponding Policy Filters
resource "citrix_delivery_group_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-DGFilter-2" {
  depends_on        = [citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-HDX]
  policy_id         = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-HDX.id
  enabled           = true
  allowed           = true
  delivery_group_id = citrix_delivery_group.TACG-TMM-CVAD-DG.id
}

#### Create the corresponding Policy Filters
resource "citrix_delivery_group_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-DGFilter-3" {
  depends_on        = [citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-ClientDrives]
  policy_id         = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-ClientDrives.id
  enabled           = true
  allowed           = true
  delivery_group_id = citrix_delivery_group.TACG-TMM-CVAD-DG.id
}

#### Create the corresponding Policy Filters
resource "citrix_user_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-SIDFilter-1" {
  depends_on = [citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-Printing]
  policy_id  = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-Printing.id
  enabled    = true
  allowed    = true
  sid        = var.TACG-TMM-CVAD-PS-UserSID
}

#### Create the corresponding Policy Filters
resource "citrix_user_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-SIDFilter-2" {
  depends_on = [citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-HDX]
  policy_id  = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-HDX.id
  enabled    = true
  allowed    = true
  sid        = var.TACG-TMM-CVAD-PS-UserSID
}

#### Create the corresponding Policy Filters
resource "citrix_user_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-SIDFilter-3" {
  depends_on = [citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-ClientDrives]
  policy_id  = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-ClientDrives.id
  enabled    = true
  allowed    = true
  sid        = var.TACG-TMM-CVAD-PS-UserSID
}

#### Create the corresponding Policy Filters
resource "citrix_client_ip_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-ClientIPFilter-1" {
  depends_on = [citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-Printing]
  policy_id  = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-Printing.id
  enabled    = true
  allowed    = true
  ip_address = var.TACG-TMM-CVAD-PS-IPRange
}

#### Create the corresponding Policy Filters
resource "citrix_client_ip_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-ClientIPFilter-2" {
  depends_on = [citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-HDX]
  policy_id  = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-HDX.id
  enabled    = true
  allowed    = true
  ip_address = var.TACG-TMM-CVAD-PS-IPRange
}

#### Create the corresponding Policy Filters
resource "citrix_client_ip_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-ClientIPFilter-3" {
  depends_on = [citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-ClientDrives]
  policy_id  = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-ClientDrives.id
  enabled    = true
  allowed    = true
  ip_address = var.TACG-TMM-CVAD-PS-IPRange
}

resource "null_resource" "WriteProgress5" {
  depends_on = [citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD1, citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD2, citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD3, citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD4, citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD5, citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD6, citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-HDX1, citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-HDX2, citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-HDX3, citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-Printing1, citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-Printing2]
  provisioner "local-exec" {
    command = "echo The default Policy Set and its policies and filters were successfully created..."
  }
}Running the snippet should fulfill all needed steps: azadmin@tacg-cicd:/etc/terraform-tmm/CVADXS/_CreateCitrixEntities$ terraform apply
data.xenserver_network.network: Reading...
data.xenserver_sr.vmsr: Reading...
data.xenserver_network.network: Read complete after 0s
data.xenserver_sr.vmsr: Read complete after 0s
data.citrix_zone.TACG-TMM-CVAD-Zone: Reading...
data.citrix_zone.TACG-TMM-CVAD-Zone: Read complete after 0s [id=47010f19-5c99-4414-ab73-01a31aacff9a]

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_machine_catalog.TACG-TMM-CVAD-MC will be created
  + resource "citrix_machine_catalog" "TACG-TMM-CVAD-MC" {
      + allocation_type          = "Static"
      + built_in_scopes          = (known after apply)
      + delete_machine_accounts  = "None"
      + description              = "Terraform-created Machine Catalog for TACG running on XS8-Cluster"
      + id                       = (known after apply)
      + inherited_scopes         = (known after apply)
      + minimum_functional_level = "L7_20"
      + name                     = "TACG-TMM-CVAD-XS8-MC"
      + persist_user_changes     = (known after apply)
      + provisioning_scheme      = {
          + hypervisor                     = (known after apply)
          + hypervisor_resource_pool       = (known after apply)
          + identity_type                  = "ActiveDirectory"
          + machine_account_creation_rules = {
              + naming_scheme      = "tf-mcs-w11-#"
              + naming_scheme_type = "Numeric"
            }
          + machine_domain_identity        = {
              + domain                   = "the-austrian-citrix-guy.at"
              + domain_ou                = "OU=_COMPUTER,OU=TACG-CVAD,DC=the-austrian-citrix-guy,DC=at"
              + service_account          = (sensitive value)
              + service_account_password = (sensitive value)
            }
          + number_of_total_machines       = 2
          + xenserver_machine_config       = {
              + cpu_count                        = 2
              + image_snapshot                   = ""
              + master_image_note                = ""
              + master_image_vm                  = "W1125H2"
              + memory_mb                        = 4096
              + use_full_disk_clone_provisioning = false
            }
        }
      + provisioning_type        = "MCS"
      + scopes                   = []
      + session_support          = "SingleSession"
      + tenants                  = (known after apply)
      + zone                     = "47010f19-5c99-4414-ab73-01a31aacff9a"
    }

  # citrix_xenserver_hypervisor.TACG-TMM-CVAD-HypConn will be created
  + resource "citrix_xenserver_hypervisor" "TACG-TMM-CVAD-HypConn" {
      + addresses                                = [
          + "http://10.10.119.1",
        ]
      + id                                       = (known after apply)
      + max_absolute_active_actions              = 40
      + max_absolute_new_actions_per_minute      = 10
      + max_power_actions_percentage_of_machines = 20
      + name                                     = "TACG-TMM-CVAD-XS8-Cluster"
      + password                                 = (sensitive value)
      + password_format                          = "PlainText"
      + scopes                                   = []
      + ssl_thumbprints                          = (sensitive value)
      + tenants                                  = (known after apply)
      + username                                 = (sensitive value)
      + zone                                     = "47010f19-5c99-4414-ab73-01a31aacff9a"
    }

  # citrix_xenserver_hypervisor_resource_pool.TACG-TMM-CVAD-HypConnPool will be created
  + resource "citrix_xenserver_hypervisor_resource_pool" "TACG-TMM-CVAD-HypConnPool" {
      + hypervisor                = (known after apply)
      + id                        = (known after apply)
      + name                      = "TACG-TMM-CVAD-XS8-Cluster"
      + networks                  = [
          + "Network0",
        ]
      + storage                   = [
          + {
              + storage_name = "local storage"
              + superseded   = false
            },
        ]
      + temporary_storage         = [
          + {
              + storage_name = "local storage"
              + superseded   = false
            },
        ]
      + use_local_storage_caching = false
      + vm_tagging                = true
    }

  # null_resource.WriteProgress1 will be created
  + resource "null_resource" "WriteProgress1" {
      + id = (known after apply)
    }

  # null_resource.WriteProgress2 will be created
  + resource "null_resource" "WriteProgress2" {
      + id = (known after apply)
    }

  # null_resource.WriteProgress3 will be created
  + resource "null_resource" "WriteProgress3" {
      + id = (known after apply)
    }

# citrix_client_ip_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-ClientIPFilter-1 will be created
  + resource "citrix_client_ip_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-ClientIPFilter-1" {
      + allowed    = true
      + enabled    = true
      + id         = (known after apply)
      + ip_address = "10.10.0.0"
      + policy_id  = (known after apply)
    }

  # citrix_client_ip_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-ClientIPFilter-2 will be created
  + resource "citrix_client_ip_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-ClientIPFilter-2" {
      + allowed    = true
      + enabled    = true
      + id         = (known after apply)
      + ip_address = "10.10.0.0"
      + policy_id  = (known after apply)
    }

  # citrix_client_ip_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-ClientIPFilter-3 will be created
  + resource "citrix_client_ip_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-ClientIPFilter-3" {
      + allowed    = true
      + enabled    = true
      + id         = (known after apply)
      + ip_address = "10.10.0.0"
      + policy_id  = (known after apply)
    }

  # citrix_delivery_group.TACG-TMM-CVAD-DG will be created
  + resource "citrix_delivery_group" "TACG-TMM-CVAD-DG" {
      + associated_machine_catalogs = [
          + {
              + machine_catalog = "c052bbc4-d5a7-407a-baa3-d0faf901a0b1"
              + machine_count   = 2
            },
        ]
      + autoscale_settings          = {
          + autoscale_enabled                                     = true
          + disconnect_off_peak_idle_session_after_seconds        = 0
          + disconnect_peak_idle_session_after_seconds            = 0
          + log_off_off_peak_disconnected_session_after_seconds   = 0
          + log_off_peak_disconnected_session_after_seconds       = 0
          + log_off_reminder_enabled                              = false
          + log_off_reminder_message                              = ""
          + log_off_reminder_title                                = ""
          + log_off_warning_message                               = ""
          + log_off_warning_title                                 = ""
          + off_peak_buffer_size_percent                          = 0
          + off_peak_disconnect_action                            = "Suspend"
          + off_peak_disconnect_timeout_minutes                   = 5
          + off_peak_extended_disconnect_action                   = "Nothing"
          + off_peak_extended_disconnect_timeout_minutes          = 0
          + off_peak_limit_seconds_to_force_log_off_user          = 0
          + off_peak_log_off_action                               = "Suspend"
          + off_peak_log_off_reminder_interval                    = 0
          + off_peak_log_off_timeout_minutes                      = 5
          + peak_autoscale_assigned_power_on_idle_action          = "Nothing"
          + peak_autoscale_assigned_power_on_idle_timeout_minutes = 0
          + peak_buffer_size_percent                              = 0
          + peak_disconnect_action                                = "Suspend"
          + peak_disconnect_timeout_minutes                       = 5
          + peak_extended_disconnect_action                       = "Nothing"
          + peak_extended_disconnect_timeout_minutes              = 0
          + peak_limit_seconds_to_force_log_off_user              = 0
          + peak_log_off_action                                   = "Suspend"
          + peak_log_off_reminder_interval                        = 0
          + peak_log_off_timeout_minutes                          = 5
          + power_off_delay_minutes                               = 30
          + power_time_schemes                                    = [
              + {
                  + days_of_week          = [
                      + "Friday",
                      + "Monday",
                      + "Thursday",
                      + "Tuesday",
                      + "Wednesday",
                    ]
                  + display_name          = "TACG-TMM-CVAD-XS8-AS"
                  + peak_time_ranges      = [
                      + "09:00-17:00",
                    ]
                  + pool_using_percentage = false
                },
            ]
          + timezone                                              = "UTC"
        }
      + built_in_scopes             = (known after apply)
      + color_depth                 = "TwentyFourBit"
      + default_desktop_icon        = "1"
      + description                 = "Terraform-created Delivery Group for TACG running on XS8-Cluster"
      + desktops                    = [
          + {
              + description             = "Terraform-created W11 Desktops running on XS8-Cluster"
              + enabled                 = true
              + id                      = (known after apply)
              + published_name          = "TACG-XS8-W11"
              + restricted_access_users = {
                  + allow_list = [
                      + "TACG\\vdaallowed",
                    ]
                  + block_list = []
                }
            },
        ]
      + enabled                     = true
      + force_delete                = false
      + id                          = (known after apply)
      + in_maintenance_mode         = false
      + inherited_scopes            = (known after apply)
      + load_balancing_type         = "None"
      + minimum_functional_level    = "L7_20"
      + name                        = "TACG-TMM-CVAD-XS8-DG"
      + reboot_schedules            = [
          + {
              + days_in_week            = [
                  + "Sunday",
                ]
              + frequency               = "Weekly"
              + frequency_factor        = 1
              + ignore_maintenance_mode = true
              + name                    = "TACG-TMM-CVAD-XS8-RS"
              + natural_reboot_schedule = false
              + reboot_duration_minutes = 0
              + reboot_schedule_enabled = true
              + start_date              = "2025-01-01"
              + start_time              = "02:00"
                # (1 unchanged attribute hidden)
            },
        ]
      + restricted_access_users     = {
          + allow_list = [
              + "TACG\\vdaallowed",
            ]
          + block_list = []
        }
      + scopes                      = []
      + secure_ica_required         = false
      + tenants                     = (known after apply)
      + total_machines              = (known after apply)
    }

  # citrix_delivery_group_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-DGFilter-1 will be created
  + resource "citrix_delivery_group_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-DGFilter-1" {
      + allowed           = true
      + delivery_group_id = (known after apply)
      + enabled           = true
      + id                = (known after apply)
      + policy_id         = (known after apply)
    }

  # citrix_delivery_group_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-DGFilter-2 will be created
  + resource "citrix_delivery_group_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-DGFilter-2" {
      + allowed           = true
      + delivery_group_id = (known after apply)
      + enabled           = true
      + id                = (known after apply)
      + policy_id         = (known after apply)
    }

  # citrix_delivery_group_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-DGFilter-3 will be created
  + resource "citrix_delivery_group_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-DGFilter-3" {
      + allowed           = true
      + delivery_group_id = (known after apply)
      + enabled           = true
      + id                = (known after apply)
      + policy_id         = (known after apply)
    }

  # citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-ClientDrives will be created
  + resource "citrix_policy" "TACG-TMM-CVAD-DefaultPolicySet-ClientDrives" {
      + description   = "Example of Client Drive-related policy in default Policy Set"
      + enabled       = true
      + id            = (known after apply)
      + name          = "Client Drives"
      + policy_set_id = (known after apply)
    }

  # citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-HDX will be created
  + resource "citrix_policy" "TACG-TMM-CVAD-DefaultPolicySet-HDX" {
      + description   = "Example of HDX Graphics-related policy in default Policy Set"
      + enabled       = true
      + id            = (known after apply)
      + name          = "HDX Graphics"
      + policy_set_id = (known after apply)
    }

  # citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-Printing will be created
  + resource "citrix_policy" "TACG-TMM-CVAD-DefaultPolicySet-Printing" {
      + description   = "Example of Printer-related policy in default Policy Set"
      + enabled       = true
      + id            = (known after apply)
      + name          = "Printing"
      + policy_set_id = (known after apply)
    }

  # citrix_policy_set_v2.TACG-TMM-CVAD-DefaultPolicySet will be created
  + resource "citrix_policy_set_v2" "TACG-TMM-CVAD-DefaultPolicySet" {
      + assigned        = (known after apply)
      + delivery_groups = [
          + (known after apply),
        ]
      + description     = "Terraform-created default Policy Set for TACG running on XS8-Cluster"
      + id              = (known after apply)
      + name            = "TACG-TMM-CVAD-XS8-PS-Default"
      + scopes          = []
    }

  # citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD1 will be created
  + resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-CD1" {
      + enabled     = true
      + id          = (known after apply)
      + name        = "AutoConnectDrives"
      + policy_id   = (known after apply)
      + use_default = false
    }

  # citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD2 will be created
  + resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-CD2" {
      + enabled     = true
      + id          = (known after apply)
      + name        = "ClientDriveRedirection"
      + policy_id   = (known after apply)
      + use_default = false
    }

  # citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD3 will be created
  + resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-CD3" {
      + enabled     = true
      + id          = (known after apply)
      + name        = "ClientFixedDrives"
      + policy_id   = (known after apply)
      + use_default = false
    }

  # citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD4 will be created
  + resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-CD4" {
      + enabled     = false
      + id          = (known after apply)
      + name        = "ClientFloppyDrives"
      + policy_id   = (known after apply)
      + use_default = false
    }

  # citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD5 will be created
  + resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-CD5" {
      + enabled     = false
      + id          = (known after apply)
      + name        = "ClientOpticalDrives"
      + policy_id   = (known after apply)
      + use_default = false
    }

  # citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD6 will be created
  + resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-CD6" {
      + enabled     = false
      + id          = (known after apply)
      + name        = "ClientNetworkDrives"
      + policy_id   = (known after apply)
      + use_default = false
    }

  # citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-HDX1 will be created
  + resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-HDX1" {
      + enabled     = true
      + id          = (known after apply)
      + name        = "AllowVisuallyLosslessCompression"
      + policy_id   = (known after apply)
      + use_default = false
    }

  # citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-HDX2 will be created
  + resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-HDX2" {
      + id          = (known after apply)
      + name        = "UseVideoCodecForCompression"
      + policy_id   = (known after apply)
      + use_default = false
      + value       = "UseVideoCodecIfPreferred"
    }

  # citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-HDX3 will be created
  + resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-HDX3" {
      + enabled     = true
      + id          = (known after apply)
      + name        = "UseHardwareEncodingForVideoCodec"
      + policy_id   = (known after apply)
      + use_default = false
    }

  # citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-Printing1 will be created
  + resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-Printing1" {
      + id          = (known after apply)
      + name        = "ClientPrinterAutoCreation"
      + policy_id   = (known after apply)
      + use_default = false
      + value       = "DefaultPrinterOnly"
    }

  # citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-Printing2 will be created
  + resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-Printing2" {
      + id          = (known after apply)
      + name        = "UniversalPrintDriverUsage"
      + policy_id   = (known after apply)
      + use_default = false
      + value       = "FallbackToSpecific"
    }

  # citrix_user_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-SIDFilter-1 will be created
  + resource "citrix_user_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-SIDFilter-1" {
      + allowed   = true
      + enabled   = true
      + id        = (known after apply)
      + policy_id = (known after apply)
      + sid       = "S-1-5-21-3919795815-764310115-1491410927-2116"
    }

  # citrix_user_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-SIDFilter-2 will be created
  + resource "citrix_user_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-SIDFilter-2" {
      + allowed   = true
      + enabled   = true
      + id        = (known after apply)
      + policy_id = (known after apply)
      + sid       = "S-1-5-21-3919795815-764310115-1491410927-2116"
    }

  # citrix_user_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-SIDFilter-3 will be created
  + resource "citrix_user_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-SIDFilter-3" {
      + allowed   = true
      + enabled   = true
      + id        = (known after apply)
      + policy_id = (known after apply)
      + sid       = "S-1-5-21-3919795815-764310115-1491410927-2116"
    }

  # null_resource.WriteProgress4 will be created
  + resource "null_resource" "WriteProgress4" {
      + id = (known after apply)
    }

  # null_resource.WriteProgress5 will be created
  + resource "null_resource" "WriteProgress5" {
      + id = (known after apply)
    }

Plan: 33 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_xenserver_hypervisor.TACG-TMM-CVAD-HypConn: Creating...
citrix_xenserver_hypervisor.TACG-TMM-CVAD-HypConn: Still creating... [00m10s elapsed]
citrix_xenserver_hypervisor.TACG-TMM-CVAD-HypConn: Still creating... [00m20s elapsed]
citrix_xenserver_hypervisor.TACG-TMM-CVAD-HypConn: Still creating... [00m30s elapsed]
citrix_xenserver_hypervisor.TACG-TMM-CVAD-HypConn: Still creating... [00m40s elapsed]
citrix_xenserver_hypervisor.TACG-TMM-CVAD-HypConn: Creation complete after 41s [id=f6f129aa-c9c6-4b9f-84c3-cb0834342fa6]
null_resource.WriteProgress1: Creating...
null_resource.WriteProgress1: Provisioning with 'local-exec'...
null_resource.WriteProgress1 (local-exec): Executing: ["/bin/sh" "-c" "echo The Hypervisor Connection was successfully created..."]
null_resource.WriteProgress1 (local-exec): The Hypervisor Connection was successfully created...
null_resource.WriteProgress1: Creation complete after 0s [id=7102860708396185821]
citrix_xenserver_hypervisor_resource_pool.TACG-TMM-CVAD-HypConnPool: Creating...
citrix_xenserver_hypervisor_resource_pool.TACG-TMM-CVAD-HypConnPool: Still creating... [00m10s elapsed]
citrix_xenserver_hypervisor_resource_pool.TACG-TMM-CVAD-HypConnPool: Still creating... [00m20s elapsed]
citrix_xenserver_hypervisor_resource_pool.TACG-TMM-CVAD-HypConnPool: Still creating... [00m30s elapsed]
citrix_xenserver_hypervisor_resource_pool.TACG-TMM-CVAD-HypConnPool: Still creating... [00m40s elapsed]
citrix_xenserver_hypervisor_resource_pool.TACG-TMM-CVAD-HypConnPool: Creation complete after 40s [id=6d2214be-5d23-44bb-8f90-90acc6151e97]
null_resource.WriteProgress2: Creating...
null_resource.WriteProgress2: Provisioning with 'local-exec'...
null_resource.WriteProgress2 (local-exec): Executing: ["/bin/sh" "-c" "echo The Hypervisor Connection Pool was successfully created..."]
null_resource.WriteProgress2 (local-exec): The Hypervisor Connection Pool was successfully created...
null_resource.WriteProgress2: Creation complete after 0s [id=4353748660640660049]
citrix_machine_catalog.TACG-TMM-CVAD-MC: Creating...
citrix_machine_catalog.TACG-TMM-CVAD-MC: Still creating... [00m10s elapsed]
citrix_machine_catalog.TACG-TMM-CVAD-MC: Still creating... [00m20s elapsed]
citrix_machine_catalog.TACG-TMM-CVAD-MC: Still creating... [00m30s elapsed]
citrix_machine_catalog.TACG-TMM-CVAD-MC: Still creating... [00m40s elapsed]
...

citrix_machine_catalog.TACG-TMM-CVAD-MC: Still creating... [09m20s elapsed]
citrix_machine_catalog.TACG-TMM-CVAD-MC: Creation complete after 41s [id=47df73ea-fde2-3431-33f13-ae8909328dd1]
...
citrix_delivery_group.TACG-TMM-CVAD-DG: Creating...
citrix_delivery_group.TACG-TMM-CVAD-DG: Still creating... [00m10s elapsed]
citrix_delivery_group.TACG-TMM-CVAD-DG: Still creating... [00m20s elapsed]
citrix_delivery_group.TACG-TMM-CVAD-DG: Creation complete after 21s [id=e57c8f65-c8a2-4788-bdda-dac19816192b]
null_resource.WriteProgress4: Creating...
citrix_policy_set_v2.TACG-TMM-CVAD-DefaultPolicySet: Creating...
null_resource.WriteProgress4: Provisioning with 'local-exec'...
null_resource.WriteProgress4 (local-exec): Executing: ["/bin/sh" "-c" "echo The Delivery Group was successfully created..."]
null_resource.WriteProgress4 (local-exec): The Delivery Group was successfully created...
null_resource.WriteProgress4: Creation complete after 0s [id=1603009483001819548]
citrix_policy_set_v2.TACG-TMM-CVAD-DefaultPolicySet: Still creating... [00m10s elapsed]
citrix_policy_set_v2.TACG-TMM-CVAD-DefaultPolicySet: Still creating... [00m20s elapsed]
citrix_policy_set_v2.TACG-TMM-CVAD-DefaultPolicySet: Still creating... [00m30s elapsed]
citrix_policy_set_v2.TACG-TMM-CVAD-DefaultPolicySet: Creation complete after 30s [id=f06d0abd-bfba-4da2-853a-22f1d0f29c94]
citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-ClientDrives: Creating...
citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-HDX: Creating...
citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-Printing: Creating...
citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-HDX: Creation complete after 1s [id=7d2605d3-8e15-4358-81c0-3e6fecefe31c]
citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-Printing: Creation complete after 1s [id=ccc01f86-0120-4293-85e6-92ca430553c6]
citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-ClientDrives: Creation complete after 1s [id=51ee45df-68c8-4221-a9f4-b65fa37c0299]
citrix_user_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-SIDFilter-2: Creating...
citrix_delivery_group_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-DGFilter-2: Creating...
citrix_client_ip_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-ClientIPFilter-2: Creating...
citrix_delivery_group_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-DGFilter-1: Creating...
citrix_client_ip_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-ClientIPFilter-1: Creating...
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-HDX3: Creating...
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-HDX1: Creating...
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-Printing1: Creating...
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-HDX2: Creating...
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-Printing2: Creating...
citrix_user_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-SIDFilter-2: Creation complete after 1s [id=f4e7a4ba-b9af-4808-86bf-451f2747729d]
citrix_user_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-SIDFilter-1: Creating...
citrix_delivery_group_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-DGFilter-2: Creation complete after 1s [id=30c858f9-437c-42af-b917-2938284aae76]
citrix_client_ip_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-ClientIPFilter-3: Creating...
citrix_client_ip_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-ClientIPFilter-2: Creation complete after 1s [id=cc621016-8e1e-48e8-bdcd-244d6bcdaf6e]
citrix_delivery_group_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-DGFilter-3: Creating...
citrix_delivery_group_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-DGFilter-1: Creation complete after 1s [id=1b24d545-130a-429e-8c7e-a8340d38d18c]
citrix_client_ip_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-ClientIPFilter-1: Creation complete after 1s [id=b6a0b752-fcc1-4bcc-87a1-34bc132bddb1]
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD3: Creating...
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD6: Creating...
citrix_user_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-SIDFilter-1: Creation complete after 0s [id=64f9bffa-bd49-44f8-953a-ae15f7137838]
citrix_client_ip_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-ClientIPFilter-3: Creation complete after 0s [id=0c1174c4-cc2c-45cb-9183-aca6857b46f1]
citrix_delivery_group_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-DGFilter-3: Creation complete after 0s [id=dc53bebb-9ddf-409c-9df3-a29abf995bd0]
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD2: Creating...
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD5: Creating...
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD4: Creating...
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD2: Creation complete after 1s [id=99327137-cee4-4ad9-81c8-cdc489bf9bf5]
citrix_user_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-SIDFilter-3: Creating...
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-HDX3: Creation complete after 3s [id=d80f6146-6a4f-4950-8f0f-1bf157f64824]
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-Printing1: Creation complete after 3s [id=41fe293e-0f1a-48a9-bc13-83b44f7d1fb9]
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD1: Creating...
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-HDX1: Creation complete after 3s [id=89866698-9c9e-4c9a-946b-a0e6cca6b856]
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-HDX2: Creation complete after 3s [id=e40d7c92-2b67-4d85-94c6-9d648cc4cd16]
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD5: Creation complete after 1s [id=32b89ef9-d542-4a09-8863-391a7132c80d]
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD6: Creation complete after 2s [id=0d7e6233-946a-442b-96ab-004a62e72088]
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD4: Creation complete after 1s [id=45d0697b-b39f-4e70-9c8d-bfb50aef7db2]
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD3: Creation complete after 2s [id=09d39467-ba5a-4057-ad3c-427789900339]
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-Printing2: Creation complete after 3s [id=2297952e-611f-4d08-b9c2-a9587d119838]
citrix_user_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-SIDFilter-3: Creation complete after 0s [id=a9332544-c5d4-495b-b800-12f976d4cdef]
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD1: Creation complete after 0s [id=dde0f25c-7a9e-4429-b1c7-7d5b978a180e]
null_resource.WriteProgress5: Creating...
null_resource.WriteProgress5: Provisioning with 'local-exec'...
null_resource.WriteProgress5 (local-exec): Executing: ["/bin/sh" "-c" "echo The default Policy Set and its policies and filters were successfully created..."]
null_resource.WriteProgress5 (local-exec): The default Policy Set and its policies and filters were successfully created...
null_resource.WriteProgress5: Creation complete after 0s [id=3134505013310827667]

Apply complete! Resources: 33 added, 0 changed, 0 destroyed.
azadmin@tacg-cicd:/etc/terraform-tmm/CVADXS/_CreateCitrixEntities$Terraform has successfully installed all required Citrix entities: a Hypervisor Connection and a Hypervisor Connection Pool were created, a Machine Catalog was created, a Delivery Group was created, and a dedicated Policy Set and its policies were created. WebStudio shows a complete, error-free deployment of Citrix Virtual Apps and Desktops 2507 LTSR:  As the deployment is complete, we can now log on to StoreFront and start our assigned desktop.  Part 10: Using Terraform to create all CVAD entities and all their necessitiesRemoving Citrix DaaS entities using TerraformWe have discussed mainly aspects of the CREATION of entities using Terraform. Finally, it is also most important to have an instrument to REMOVE entities created by Terraform. In Infrastructure‑as‑Code (IaC) environments, terraform destroy plays a critical role in ensuring clean deprovisioning, operational discipline, and compliance with governance.  By design, Terraform manages infrastructure declaratively—meaning everything it creates is recorded in its state. Using terraform destroy ensures that all Terraform‑managed resources are removed consistently, predictably, and traceably, eliminating the risk of leaving unmanaged or “orphaned” cloud assets behind, and supports security hygiene by ensuring no unused compute, networking, or identity components remain. It also reduces cost leakage. Terraform logs, state history, and pipeline execution metadata provide a verifiable chain of custody for resource lifecycle events. Using terraform destroy creates explicit evidence showing: What was removed, When it was removed, and Who initiated the removal This is essential for audit frameworks such as ISO 27001, SOC 2, and internal IT controls. Example: Removing a Delivery Group You can simply remove the Delivery Group by uncommenting the respective code in your snippet - place the uncomment markers /* &lt;------ place the marker here
#### Create the Delivery Group
resource "citrix_delivery_group" "TACG-TMM-CVAD-DG" {
  depends_on = [citrix_machine_catalog.TACG-TMM-CVAD-MC, citrix_storefront_server.CreateSFServer]
  #depends_on               = [data.citrix_machine_catalog.TACG-TMM-CVAD-MC]
  name                     = var.TACG-TMM-CVAD-DG-Name
  description              = var.TACG-TMM-CVAD-DG-Description
  minimum_functional_level = var.TACG-TMM-CVAD-DG-FunctionalLevel
  storefront_servers       = [citrix_storefront_server.CreateSFServer.id]
  associated_machine_catalogs = [
    {
      machine_catalog = citrix_machine_catalog.TACG-TMM-CVAD-MC.id
      #machine_catalog = data.citrix_machine_catalog.TACG-TMM-CVAD-MC.id
      machine_count = var.TACG-TMM-CVAD-MC-VM-NumberOfVMs
    }
  ]
  desktops = [
    {
      published_name = var.TACG-TMM-CVAD-DG-Desktops-Name
      description    = var.TACG-TMM-CVAD-DG-Desktops-Description
      restricted_access_users = {
        allow_list = var.TACG-TMM-CVAD-DG-Desktops-AllowList
      }
      enabled = var.TACG-TMM-CVAD-DG-Desktops-Enabled

      ###### Not needed due to Static assignment type-MC
      # enable_session_roaming = var.TACG-TMM-CVAD-DG-Desktops-SessionRoaming
    }

  ]
  autoscale_settings = {
    autoscale_enabled                   = var.TACG-TMM-CVAD-DG-AS-Enabled
    peak_disconnect_timeout_minutes     = var.TACG-TMM-CVAD-DG-AS-PeakDisconnectTime
    off_peak_disconnect_timeout_minutes = var.TACG-TMM-CVAD-DG-AS-OffPeakDisconnectTime
    peak_disconnect_action              = var.TACG-TMM-CVAD-DG-AS-PeakDisconnectAction
    off_peak_disconnect_action          = var.TACG-TMM-CVAD-DG-AS-OffPeakDisconnectAction
    off_peak_log_off_timeout_minutes    = var.TACG-TMM-CVAD-DG-AS-PeakLogoffTime
    peak_log_off_timeout_minutes        = var.TACG-TMM-CVAD-DG-AS-OffPeakLogoffTime
    peak_log_off_action                 = var.TACG-TMM-CVAD-DG-AS-PeakDisconnectAction
    off_peak_log_off_action             = var.TACG-TMM-CVAD-DG-AS-OffPeakDisconnectAction

    ###### Not needed due to Static assignment type-MC
    #log_off_off_peak_disconnected_session_after_seconds = var.TACG-TMM-CVAD-DG-AS-PeakLogoffTime
    #log_off_peak_disconnected_session_after_seconds     = var.TACG-TMM-CVAD-DG-AS-OffPeakLogoffTime
    #peak_log_off_action                                 = var.TACG-TMM-CVAD-DG-AS-PeakDisconnectAction
    #off_peak_log_off_action                             = var.TACG-TMM-CVAD-DG-AS-OffPeakDisconnectAction
    #log_off_reminder_enabled                            = var.TACG-TMM-CVAD-DG-AS-Enabled
    #log_off_reminder_message                            = var.TACG-TMM-CVAD-DG-RS-Notification-MessageBody
    #log_off_reminder_title                              = var.TACG-TMM-CVAD-DG-AS-Notification-MessageTitle
    #log_off_warning_message                             = var.TACG-TMM-CVAD-DG-RS-Notification-MessageBody
    #log_off_warning_title                               = var.TACG-TMM-CVAD-DG-AS-Notification-MessageTitle
    timezone = var.TACG-TMM-CVAD-DG-AS-TimeZone

    power_time_schemes = [
      {
        days_of_week          = var.TACG-TMM-CVAD-DG-AS-Days
        display_name          = var.TACG-TMM-CVAD-DG-AS-Name
        peak_time_ranges      = var.TACG-TMM-CVAD-DG-AS-PeakTime
        pool_using_percentage = false
      },
    ]
  }
  restricted_access_users = {
    allow_list = var.TACG-TMM-CVAD-DG-Desktops-AllowList
  }
  reboot_schedules = [
    {
      name                    = var.TACG-TMM-CVAD-DG-RS-Name
      reboot_schedule_enabled = var.TACG-TMM-CVAD-DG-RS-Enabled
      frequency               = var.TACG-TMM-CVAD-DG-RS-Frequency
      frequency_factor        = 1
      days_in_week            = var.TACG-TMM-CVAD-DG-RS-RebootDays
      start_time              = var.TACG-TMM-CVAD-DG-RS-RebootStartTime
      start_date              = var.TACG-TMM-CVAD-DG-RS-RebootStartDate
      reboot_duration_minutes = var.TACG-TMM-CVAD-DG-RS-RebootDuration
      ignore_maintenance_mode = var.TACG-TMM-CVAD-DG-RS-IgnoreMaintenanceMode
      natural_reboot_schedule = var.TACG-TMM-CVAD-DG-RS-NormalRebootSchedule

      ###### Not needed due to SingleSession type-MC
      #reboot_notification_to_users = {
      #notification_duration_minutes       = var.TACG-TMM-CVAD-DG-RS-Notification-Duration
      #notification_message                = var.TACG-TMM-CVAD-DG-RS-Notification-MessageBody
      #notification_title                  = var.TACG-TMM-CVAD-DG-RS-Notification-MessageTitle
      #notification_repeat_every_5_minutes = var.TACG-TMM-CVAD-DG-RS-Notification-MessageRepeat
      #} 
    }
  ]
}
*/ &lt;------ place the marker hereRunning the snippet would remove the Delivery Group: XxXxXxXxX@devops-automation:/etc/terraform-tmm/DeployDG$ terraform apply

...

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  - destroy

Terraform will perform the following actions:

  # citrix_delivery_group.CreateDG will be destroyed
  - resource "citrix_delivery_group" "TACG-TMM-CVAD-DG" {
      - associated_machine_catalogs = [
          - {
              - machine_catalog = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" -&gt; null
              - machine_count   = 1 -&gt; null
            },
        ]
      + autoscale_settings          = {
         ...

Plan: 0 to add, 0 to change, 1 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_delivery_group.TACG-TMM-CVAD-DG: Destroying...
citrix_delivery_group.TACG-TMM-CVAD-DG: Destruction complete after 24s [id=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX]

Apply complete! Resources: 0 added, 0 changed, 1 destroyed.

XxXxXxXxX@devops-automation:/etc/terraform-tmm/DeployDG$Terraform has decommissioned the Delivery Group and its adjacent entities. That completes our example of using Infrastructure-as-Code for creating a Citrix DaaS Deployment on Azure.  Using the DeploymentAs all prerequisites are met, we can now log on to our CVAD site and start our assigned VDI desktop. Logging on through Citrix Workspace App or Storefront is possible:      SummaryUsing Terraform can significantly streamline your workflow and improve efficiency: Automation: IaC automates repetitive tasks, such as provisioning and managing infrastructure. This reduces the need for manual intervention and minimizes the risk of human error. Consistency: Defining IaC ensures that your environments are consistent and reproducible. This is particularly useful for maintaining multiple environments, such as development, staging, and production. Scalability: IaC makes it easy to scale your infrastructure up or down based on demand. You can define your desired state, and Terraform will handle the necessary changes to achieve that state. Collaboration: IaC configurations can be stored in version control systems like Git, enabling team collaboration. Team members can review, comment on, and approve changes before they are applied. Visibility: IaC provides a clear and auditable record of changes to your infrastructure. This makes it easier to track changes, troubleshoot issues, and ensure compliance with organizational policies. Integration: IaCintegrates with various tools and services, such as CI/CD pipelines, monitoring systems, and configuration management tools. This allows you to create a seamless, automated workflow for managing your infrastructure. Cost Management: By automating resource provisioning and de-provisioning, Terraform helps you optimize resource usage and control costs. You can easily identify and eliminate unused or underutilized resources.  That completes Part 4 of the Citrix Automation Handbook. In the first part of the Citrix Automation Handbook, we discuss the Citrix Automation story and the basics of Infrastructure-as-Code, of Continuous Integration/Continuous Deployment (CI/CD) strategies, and our main IaC tools: Packer, Terraform, and Ansible. In the second part of the Citrix Automation Handbook, we focus on: Applying automation and infrastructure-as-code in the Citrix universe. We discuss all relevant components and provide code examples from real environments Best practises for designing an environment for automation and IaC use, and discuss deployment tips In the third part of the Citrix Automation Handbook, we discuss common and special use cases, as well as complete deployment examples from the field. In this part, we focused on: Creating a Citrix Virtual Apps and Desktops 2507 LTSR deployment on XenServer 8.4. In the fifth part of the Citrix Automation Handbook, we cover creating a Citrix DaaS deployment on Azure. In the sixth part of the Citrix Automation Handbook, we cover some final topics.  DisclaimerMost important All code snippets mentioned in this handbook were thoroughly tested and run in a sandbox environment. All shown Packer, Terraform, Ansible Playbooks, and PowerShell scripts were tailored exclusively for the environment used for this handbook. You need to adjust all settings, values, snippets, etc., in the most suitable way tailored to your requirements, regulations, and existing environments. All settings, scripts, and code examples listed in this document are intended only to illustrate the possibilities and serve as a basis for your own deployment. We tried to show different ways, even if they were not always in line with best-practice guidelines, such as unencrypted WinRM communication for demonstration purposes. Using all the provided code snippets is at your own risk – please read the disclaimer below for further information.   EXCEPT WHERE EXPRESSLY PROVIDED OTHERWISE BY CLOUD SOFTWARE GROUP, THE CONTENT IN THIS DOCUMENT IS PROVIDED “AS IS” AND CLOUD SOFTWARE GROUP HEREBY DISCLAIMS ALL EXPRESS OR IMPLIED REPRESENTATIONS, WARRANTIES, GUARANTIES, AND CONDITIONS, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES OR CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD TO BE LEGALLY INVALID. CLOUD SOFTWARE GROUP MAKES NO REPRESENTATIONS, WARRANTIES, GUARANTIES, OR CONDITIONS AS TO THE QUALITY, SUITABILITY, TRUTH, ACCURACY, OR COMPLETENESS OF ANY OF THE CONTENT CONTAINED ON THE WEBSITE. The above disclaimer applies to any damages, liability, or injuries caused by any failure of performance, error, omission, interruption, deletion, defect, delay in operation or transmission, computer virus, communication line failure, theft or destruction of or unauthorized access to, alteration of, or use, whether for breach of contract, tort, negligence or any other cause of action.]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2025_11/PackerCreateMasterVMReadyTemplate.png.6bfc940f310c41181444280b2be3357a.png" length="88377" type="image/png"/><pubDate>Wed, 21 Jan 2026 09:13:09 +0000</pubDate></item><item><title>The Citrix Automation Handbook 2601 - Part 3</title><link>https://community.stage.citrix.com/tech-zone/automation/automation-handbook-2601-part3/</link><description><![CDATA[The Citrix Automation Handbook - Part 3In the first part of the Citrix Automation Handbook, we discuss the Citrix Automation story and the basics of Infrastructure-as-Code, of Continuous Integration/Continuous Deployment (CI/CD) strategies, and our main IaC tools: Packer, Terraform, and Ansible. In the second part of the Citrix Automation Handbook, we focus on: Applying automation and infrastructure-as-code in the Citrix universe. We discuss all relevant components and provide code examples from real environments Best practises for designing an environment for automation and IaC use, and discuss deployment tips In this part, we focus on: Common and special use cases, as well as complete deployment examples from the field. In the fourth part of the Citrix Automation Handbook, we cover creating a Citrix Virtual Apps and Desktops 2507 LTSR deployment on XenServer 8.4. In the fifth part of the Citrix Automation Handbook, we cover creating a Citrix DaaS deployment on Azure. In the sixth part of the Citrix Automation Handbook, we cover some final topics.  Part 3 Common Use Cases, Special Use Cases, and Examples from the FieldIn this section of the Handbook, we would like to showcase use cases we saw in the field. At first, we discuss the usage of the triad in real-world scenarios based on Customer projects. Common Use Cases of the TriadThese use cases are examples from Customer Projects. Theory: Deploying and Configuring Master Images with Hashicorp Packer and RedHat AnsibleOverviewHashiCorp Packer automates the creation of machine images—across clouds and virtualization platforms—from a single, declarative template, enabling a consistent, repeatable, and fast way to deliver 'golden images' that underpin immutable infrastructure. By combining core Packer capabilities (builders, provisioners, post‑processors) with the HCP Packer registry (channels, metadata, and revocation) and Terraform consumption, platform teams can standardize base images, reduce drift, and accelerate delivery while maintaining compliance. Modern delivery teams operate across multiple platforms (AWS, Azure, GCP, VMware, Docker), and the cost of scripting per platform is high. Packer solves this by building identical images for multiple targets from a single configuration, thereby reducing variability and build time. Let´s look at two ways to automate the creation of Master Images using Packer: Creating a Master Image on vSphere Creating a Master Image on Azure The HCL-based Packer templates are primarily built using variables for maximum flexibility and reusability. There are different ways of modifying the Master Image during the creation process – in this guide, we look at the following steps: Packer initially creates the base images and sets the initial Software configurations on the Master Image using PowerShell scripts embedded in the autounattend.xml file and using different provisioners. After creating the Master Image, we use Ansible and/or Chocolatey to automate additional software installations and configurations. Using this approach, you can maximize flexibility when creating your Master Images automatically. Modifying the Master Image using Autounattend.xmlThe autounattend.xml file is an unattended answer file used during Windows setup. It contains configuration settings and instructions that Windows Setup uses to perform an automated installation without requiring user interaction. Important The autounattend.xml file is only needed for Hypervisor-based Images. It is not required for all Hyperscaler- or Cloud-based deployments. Of course, you could convert an upload  Hypervisor-based Image to the Hyperscaler… Some benefits of using autounattend.xml files for modifying the Windows Master Image are: Fully Automated Installation: It eliminates the need for manual input during installation (e.g., language selection, disk partitioning, product key entry)  Consistency Across Deployments: Ensures every machine is set up precisely the same way — ideal for enterprise environments or mass deployments  Time-Saving: Speeds up deployment, especially when installing Windows on many machines  Customization: You can predefine: Computer name User accounts Network settings Domain-join settings Application installations Post-install scripts  Hands-Free Setup: Useful for remote or unattended installations where no technician is present  Note You can decide when to use the autounattend.xml method for initial configuration and when to use Ansible afterward. For demonstration purposes, we use both ways sequentially in this guide. Example of an autounattend.xml file : ...
&lt;settings pass="oobeSystem"&gt;
        &lt;component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;
            &lt;AutoLogon&gt;
                &lt;Enabled&gt;true&lt;/Enabled&gt;
                &lt;Username&gt;administrator&lt;/Username&gt;
                &lt;Password&gt;
                    &lt;Value&gt;XxXxXxXxXxXxXxX&lt;/Value&gt;
                    &lt;PlainText&gt;true&lt;/PlainText&gt;
                &lt;/Password&gt;
                &lt;LogonCount&gt;5&lt;/LogonCount&gt;
            &lt;/AutoLogon&gt;
            &lt;FirstLogonCommands&gt;
                &lt;SynchronousCommand wcm:action="add"&gt;
                    &lt;Order&gt;1&lt;/Order&gt;
                    &lt;CommandLine&gt;powershell -ExecutionPolicy Bypass -File a:\install-vmwtools.ps1&lt;/CommandLine&gt;
                    &lt;Description&gt;VMware Tools Installation&lt;/Description&gt;
                &lt;/SynchronousCommand&gt;
                &lt;SynchronousCommand wcm:action="add"&gt;
                    &lt;Order&gt;2&lt;/Order&gt;
                    &lt;CommandLine&gt;powershell -ExecutionPolicy Bypass -File a:\enable-ssh.ps1&lt;/CommandLine&gt;
                    &lt;Description&gt;Enable SSH service&lt;/Description&gt;
                    &lt;RequiresUserInput&gt;true&lt;/RequiresUserInput&gt;
                &lt;/SynchronousCommand&gt;
                &lt;SynchronousCommand wcm:action="add"&gt;
                    &lt;Order&gt;3&lt;/Order&gt;
                    &lt;CommandLine&gt;cmd.exe /c a:\enable-rdp.cmd&lt;/CommandLine&gt;
                    &lt;Description&gt;Enable RDP&lt;/Description&gt;
                &lt;/SynchronousCommand&gt;
                &lt;SynchronousCommand wcm:action="add"&gt;
                    &lt;Order&gt;4&lt;/Order&gt;
                    &lt;CommandLine&gt;powershell -ExecutionPolicy Bypass -File a:\configure-firewall.ps1&lt;/CommandLine&gt;
                    &lt;Description&gt;Configure Windows Firewall&lt;/Description&gt;
                &lt;/SynchronousCommand&gt;
                &lt;SynchronousCommand wcm:action="add"&gt;
                    &lt;Order&gt;5&lt;/Order&gt;
                    &lt;CommandLine&gt;powershell -ExecutionPolicy Bypass -File a:\enable-winrm.ps1&lt;/CommandLine&gt;
                    &lt;Description&gt;Enable WinRM service&lt;/Description&gt;
                    &lt;RequiresUserInput&gt;true&lt;/RequiresUserInput&gt;
                &lt;/SynchronousCommand&gt;
            &lt;/FirstLogonCommands&gt;
            &lt;OOBE&gt;
                &lt;HideEULAPage&gt;true&lt;/HideEULAPage&gt;
                &lt;HideLocalAccountScreen&gt;true&lt;/HideLocalAccountScreen&gt;
                &lt;HideOEMRegistrationScreen&gt;true&lt;/HideOEMRegistrationScreen&gt;
                &lt;HideOnlineAccountScreens&gt;true&lt;/HideOnlineAccountScreens&gt;
                &lt;HideWirelessSetupInOOBE&gt;true&lt;/HideWirelessSetupInOOBE&gt;
                &lt;NetworkLocation&gt;Home&lt;/NetworkLocation&gt;
                &lt;ProtectYourPC&gt;1&lt;/ProtectYourPC&gt;
            &lt;/OOBE&gt;
            &lt;UserAccounts&gt;
                &lt;AdministratorPassword&gt;
                    &lt;Value&gt;XxXxXxXxXxXxXValue&gt;
                    &lt;PlainText&gt;true&lt;/PlainText&gt;
                &lt;/AdministratorPassword&gt;
            &lt;/UserAccounts&gt;
        &lt;/component&gt;
    &lt;/settings&gt; Note The autounattend.xml file can trigger further scripts. In this example, five scripts are started in sequence for further configuration.  Caution The syntax and structure of the autounattend.xml file are complex and differ between operating systems. Please ensure the correct structure and syntax, as an erroneous autounattend.xml file can prevent the Windows Master Image from deploying. Therefore, we created a web-based application and framework with a graphical interface to configure and structure a corresponding autounattend.xml file. It simplifies creating a valid autounattend.xml file. A valid and well-structured autounattend.xml file lays the foundation for successfully deploying a Windows-based Master Image.  Modifying the Master Image using AnsibleAnsible is an open-source automation tool used primarily for: Configuration management Application deployment Task automation IT orchestration Ansible describes automation jobs using YAML (in the form of Ansible Playbooks), a simple, human-readable language. It connects to your nodes (Servers, Devices, etc.) over SSH (or WinRM for Windows) and doesn’t require Agent software installed on the target machines. Some benefits of using Ansible Playbooks for modifying the Windows Master Image, especially when you're aiming for consistency, scalability, and automation in your infrastructure, are: Consistency and Repeatability: Ansible Playbooks ensure that every Master Image is built the same way, every time. Reduces human error and configuration drift  Modular and Reusable: Ansible roles and tasks can be reused across different images or environments. Makes it easy to maintain and update configurations  Testable and Version-Controlled: Ansible Playbooks can be stored in Git, enabling version control, collaboration, and code reviews. You can test changes in a staging environment before applying them to production images  Agentless and Cross-Platform: No need to install agents on the Image. Works across Linux, Windows, and Cloud platforms (AWS, Azure, GCP, etc.) Using Ansible to modify your Windows Master Images is a powerful way to ensure consistency and repeatability in your infrastructure. Caution The Ansible Playbooks depend entirely on the correct syntax and structure of the corresponding YAML file. Please be aware that writing YAML using the correct structure and syntax is crucial, as an erroneous Playbook can break the deployment of the Windows Master Image.  Therefore, we also created a web-based application and framework with a graphical interface to configure and structure the corresponding Ansible Playbooks. Example of an Ansible Playbook – shortened due to the original length of the code/decision logic: ---
# Install WWCO Golden Master
# Version 100625v2GK
# Windows 11 23H2
- name: Install WWCO Master Image 100625v2GK
  hosts: all
  gather_facts: true

  vars:
    vm_join_domain: true
    vm_enable_rdp: true
    vm_install_windows_updates: false
    vm_join_domain_config: "wwco.net.json"
    vm_optimize_image: true
    vm_install_vda: true
    vm_install_vda_mcsio: false
    vm_install_vda_firewall: false
    vm_install_vda_rtt: false
    vm_install_vda_pm: false
    vm_install_vda_pm_wmi: true
    vm_install_vda_upgr: true
    vm_install_wem: false
    vm_install_pvs: false
    vm_install_uber: false
    vm_install_wapp: false
    vm_install_o365: true
    vm_install_python: false
    vm_install_ps7: false
    vm_install_crm: false
    vm_install_chrome: true
    vm_install_firefox: true
    vm_install_slack: false
    
  roles:
    - role: join_domain
      when: vm_join_domain == true
    - role: enable_rdp
      when: vm_enable_rdp == true  
    - role: install_vda_std
      when: vm_install_vda == true and vm_install_vda_mcsio == false and vm_install_vda_firewall == false and vm_install_vda_rtt == false and vm_install_vda_pm == false and vm_install_vda_pm_wmi == false and vm_install_vda_upgr == false
    - role: install_vda_std_mcsio
      when: vm_install_vda == true and vm_install_vda_mcsio == true and vm_install_vda_firewall == false and vm_install_vda_rtt == false and vm_install_vda_pm == false and vm_install_vda_pm_wmi == false and vm_install_vda_upgr == false
    - role: install_vda_std_fw
      when: vm_install_vda == true and vm_install_vda_mcsio == false and vm_install_vda_firewall == true and vm_install_vda_rtt == false and vm_install_vda_pm == false and vm_install_vda_pm_wmi == false and vm_install_vda_upgr == false
    - role: install_vda_std_rtt
      when: vm_install_vda == true and vm_install_vda_mcsio == false and vm_install_vda_firewall == false and vm_install_vda_rtt == true and vm_install_vda_pm == false and vm_install_vda_pm_wmi == false and vm_install_vda_upgr == false
    - role: install_vda_std_pm
      when: vm_install_vda == true and vm_install_vda_mcsio == false and vm_install_vda_firewall == false and vm_install_vda_rtt == false and vm_install_vda_pm == true and vm_install_vda_upgr == false
    - role: install_vda_std_pm_wmi
      when: vm_install_vda == true and vm_install_vda_mcsio == false and vm_install_vda_firewall == false and vm_install_vda_rtt == false and vm_install_vda_pm_wmi == true and vm_install_vda_upgr == false
    - role: install_vda_std_upgr
      when: vm_install_vda == true and vm_install_vda_mcsio == false and vm_install_vda_firewall == false and vm_install_vda_rtt == false and vm_install_vda_pm == false and vm_install_vda_pm_wmi == true and vm_install_vda_upgr == true
...
    - role: install_wem
      when: vm_install_wem == true
    - role: install_pvs
      when: vm_install_pvs == true
    - role: install_uber
      when: vm_install_uber == true
    - role: install_wapp
      when: vm_install_wapp == true
    - role: install_o365
      when: vm_install_o365 == true
    - role: install_python
      when: vm_install_python == true    
    - role: install_ps7
      when: vm_install_ps7 == true   
    - role: install_crm
      when: vm_install_crm == true   
    - role: install_chrome
      when: vm_install_chrome == true
    - role: install_firefox
      when: vm_install_firefox == true
    - role: install_slack
      when: vm_install_slack == true
    - role: install_windows_updates
      when: vm_install_windows_updates == true
    - role: optimize_image
      when: vm_optimize_image == true
...
By setting a variable in the vars: section to true, Ansible will install the corresponding Software package. The Ansible roles contain all the needed Tasks for deploying the corresponding functionality – for example, these are the Tasks for the Ansible role “Join-Domain”: ---
# Ansible Role Join-Domain
## Generic Tasks file for Join-Domain Role
- name: Create the DNS Server List
  ansible.builtin.set_fact:
    dns_servers_list: "{{ dns_servers  | split(',') }}"
  when: dns_servers is search(",")

- name: Set DNS to DCs
  ansible.windows.win_dns_client:
    adapter_names: "*"
    dns_servers: "{{ dns_servers_list }}"
  when: dns_servers and  dns_servers is search(",")

- name: Change Computer Name
  ansible.windows.win_hostname:
    name: "{{ vm_hostname }}"
  register: hostname_state

- name: Reboot after Computer Name was changed
  ansible.windows.win_reboot:
    post_reboot_delay: 30
  when: hostname_state.reboot_required

- name: Join to Domain - Variables are set by PACKERGEN-Tool according to chosen Domain-Join configuration
  microsoft.ad.membership:
    dns_domain_name: "{{ dns_domain_name }}"
    domain_admin_user: "{{ domain_join_user }}"
    domain_admin_password: "{{ domain_join_pw }}"
    domain_ou_path: "{{ domain_join_ou_path }}"
    hostname: "{{ vm_hostname }}"
    state: domain
  ignore_unreachable: true
  register: domain_state

- name: Reboot after Domain Join
  ansible.windows.win_reboot:
...Most Ansible Tasks are built on Variables that the Web application modifies and reads. The Domain configuration is stored in a JSON file in an Azure Vault and loaded by the Web application. It is then written in the corresponding Ansible YAML file: ---
# Variable files for Domain-Join Role
## DNS
dns_servers: "10.53.XXX.XXX,10.53.XXX.XXX"
dns_domain_name: "WWCO.NET"

# Domain
domain_join_user: "XxXxXxX@wwco.net"
domain_join_pw: "SWFtU29ycnlCdXRUaGF0SXNPZkNvdXJzZU5vdFRoZVJpZ2h0UGFzc3dvcmQ="
domain_join_ou_path: "OU=VDA,OU=XxXxXxXxX,OU=XxXxXxXxX,OU=XxXxXxXxX,OU=XxXxXxXxX,DC=wwco,DC=net"
vm_hostname: "GENMI100625V2"Due to the complexity of the syntax in autounattend.xml and the YAML playbooks, we created a web application that lets you configure all settings via a GUI. You might follow this approach to ease Master Image creation.  If you want to further ease software package deployment, we will present a way using Chocolatey as a package manager later in this guide - Installing Software Packages using Packer, Ansible, and Chocolatey Example Workflow for Packer creating a Master Image on AzureThe workflow below demonstrates how to automate the creation of a Windows Server 2025-based Master Image using all the steps outlined in this guide. For a more detailed guide and deeper information, please consult our Packer deployment guides on Citrix TechZone.  The Terraform code for creation of the prerequisites is out of scope in this guide, you can find it on Citrix TechZone. We defined the Software packages and needed configurations in Ansible using the Web application. Packer will create the Master Image on Azure from a custom Base Image, and then use Ansible for further configuration and Software deployment. Example Packer HCL code: # Packer File for Creation of a Windows Server 2025-based Master Image based on an existing custom Base Image using Packer and Ansible
## Version 06.2025
### Modified by PACKERGEN
packer {
  required_plugins {
    azure = {
      source  = "github.com/hashicorp/azure"
      version = "&gt;= 2.3.3"
    }
    ansible = {
      source  = "github.com/hashicorp/ansible"
      version = "&gt;= 1.1.3"
    }
  }
}
...
source "azure-arm" "windows" {
  async_resourcegroup_delete             = true
  client_id                              = var.client_id
  client_secret                          = var.client_secret
  communicator                           = "winrm"
  location                               = var.location
  build_resource_group_name              = var.build_resource_group_name

  # Security settings
  security_type                          = var.security_type
  vtpm_enabled                           = var.vtpm
  secure_boot_enabled                    = var.secure_boot

  # Temp Image Storage Type
  managed_image_storage_account_type     = var.managed_image_storage_account_type
  os_type                                = var.os_type

  shared_image_gallery {
    subscription                         = var.subscription_id
    resource_group                       = var.resource_group_name
    gallery_name                         = var.shared_gallery_name
    image_name                           = var.image_name
    image_version                        = var.image_version
  }
  shared_image_gallery_destination {
    gallery_name                         = var.shared_gallery_name
    image_name                           = var.dest_image_name
    image_version                        = var.dest_image_version
    replication_regions                  = ["${var.image_replication_regions}"]
    resource_group                       = var.resource_group_name
    storage_account_type                 = var.sig_storage_type
  }
  azure_tags = {
    build_number                         = var.build_number
    build_id                             = var.build_id
  }

  private_virtual_network_with_public_ip = "false"
  subscription_id                        = var.subscription_id
  tenant_id                              = var.tenant_id
  vm_size                                = var.vm_size
  virtual_network_name                   = var.virtual_network_name
  virtual_network_resource_group_name    = var.virtual_network_resource_group_name
  virtual_network_subnet_name            = var.virtual_network_subnet_name
  winrm_insecure                         = "true"
  winrm_timeout                          = var.winrm_timeout
  winrm_use_ssl                          = "true"
  winrm_username                         = var.winrm_username
}

build {
  name = "WWCOW2K25MIFromARMTemplate"
  sources = ["source.azure-arm.windows"]

  # Enable WinRM
  provisioner "powershell" {
    script = "/scripts/enable-winrm.ps1"
  }

  # Provisioning of Configuration and Packages using Ansible 
  ## Modified by PACKERGEN
  provisioner "ansible" {
    playbook_file = "${var.ansible_playbook_generic}"
    user          = "packer"
    use_proxy     = false
      extra_arguments = [
      "--extra-vars", "@${var.ansible_config}",
      "-e",
      "ansible_winrm_server_cert_validation=ignore",
      "-e",
      "vm_hostname=${var.vm_name}",
      "${var.ansible_debug}"
    ]
  }

   ## Modified by PACKERGEN
   provisioner "ansible" {
    playbook_file = "${var.ansible_playbook_CTX}"
    user          = "packer"
    use_proxy     = false
      extra_arguments = [
      "--extra-vars", "@${var.ansible_config}",
      "-e",
      "ansible_winrm_server_cert_validation=ignore",
      "-e",
      "vm_hostname=${var.vm_name}",
      "${var.ansible_debug}"
    ]
  }

  provisioner "powershell" {
  script = "/scripts/sysprep.ps1"
  }

  post-processor "manifest" {
   output = "manifest.json"
   strip_path = true
   custom_data = {
        source_image_name = "${build.SourceImageName}"
        tenant_id = "${build.TenantID}"
        subscription_id = "${build.SubscriptionID}"
        temp_deployment_name = "${build.TempDeploymentName}"
        temp_compute_name = "${build.TempComputeName}"
        temp_nic_name = "${build.TempNicName}"
        temp_os_disk_name = "${build.TempOSDiskName}"
        temp_data_disk_name = "${build.TempDataDiskName}"
        temp_resource_group_name = "${build.TempResourceGroupName}"
        temp_nsg_name = "${build.TempNsgName}"
        temp_key_vault_name = "${build.TempKeyVaultName}"
        temp_subnet_name = "${build.TempSubnetName}"
        temp_virtual_network_name = "${build.TempVirtualNetworkName}"
        temp_public_ip_address_name = "${build.TempPublicIPAddressName}"
  }
 }
}
...
Important Please do not forget to follow the correct sequence of the packer commands – e.g.: packer init -upgrade XxXxXxXxX.pkr.hcl packer validate -var-file W2K25ARMWithAnsible.variables.pkr.hcl W2K25ARMWithAnsible.pkr.hcl packer build -on-error=abort -force -var-file=W2K25ARMWithAnsible.variables.pkr.hcl W2K25ARMWithAnsible.pkr.hcl  Example Workflow for Packer creating a Master Image on vSpherePacker will create the Master Image on vSphere from an official Windows Server 2025 ISO file, and then use Ansible for further configuration and Software deployment. We defined the Software packages and required configurations in Ansible using our Web application, including installing the Citrix Virtual Delivery Agent (VDA) on the Master Image. Example Packer HCL code: packer {
  required_version = "&gt;= 1.9.0"
  required_plugins {
    vsphere = {
      version = "&gt;= v1.4.2"
      source  = "github.com/hashicorp/vsphere"
    }
  }
  required_plugins {
    windows-update = {
      version = "&gt;= 0.16.8"
      source  = "github.com/rgl/windows-update"
    }
  }
}
...
source "vsphere-iso" "windows" {
    vcenter_server              = var.vcenter_server
    username                    = var.vcenter_username
    password                    = var.vcenter_password
    insecure_connection         = true

    cluster                     = var.vsphere_cluster
    host                        = var.vsphere_host
    datacenter                  = var.vsphere_datacenter
    datastore                   = var.vsphere_datastore
    folder                      = var.vsphere_folder

    vm_name                     = var.vm_name
    notes                       = "Created by Packer and PACKERGEN."
    guest_os_type               = "windows9Server64Guest"
    firmware                    = var.firmware
    vTPM                        = var.vtpm
    CPUs                        = var.cpu
    cpu_cores                   = var.cpu
    RAM                         = var.memory
    RAM_reserve_all             = true
    remove_cdrom                = true
    cdrom_type                  = "sata"
    
    vm_version                  = var.vm_version

    iso_paths                   = [
      "${var.os_iso_path}"
    ]

    cd_files                  = [
      "/autounattend/Autounattend.xml",
      "/scripts/enable-winrm.ps1",
      "/scripts/install-vmwaretools.ps1",
      "/drivers/"
    ]

    disk_controller_type        = ["pvscsi"]
    storage {
        disk_size                 = var.disksize
        disk_thin_provisioned     = true
    }

    network_adapters {
        network                   = var.vsphere_network
        network_card              = "vmxnet3"
    }

    boot_order              = "disk,cdrom"
    boot_command = [
        "&lt;spacebar&gt;&lt;wait2&gt;&lt;spacebar&gt;"
    ]
    boot_wait                   = "1s"

    communicator                = "winrm"
    winrm_insecure              = true
    winrm_password              = var.connection_password
    winrm_timeout               = "20m"
    winrm_use_ssl               = true
    winrm_username              = var.connection_username
}

build {
    name = "WWCOW2K25MIFromISOvSphere"
    source "vsphere-iso.windows" {
        convert_to_template         = false
        create_snapshot             = var.create_snapshot
        snapshot_name               = "Created by Packer and PACKERGEN"
    }

  # Provisioning of Configuration and Packages using Ansible 
  ## Modified by PACKERGEN
  provisioner "ansible" {
    playbook_file = "${var.ansible_playbook_generic}"
    user          = "packer"
    use_proxy     = false
      extra_arguments = [
      "--extra-vars", "@${var.ansible_config}",
      "-e",
      "ansible_winrm_server_cert_validation=ignore",
      "-e",
      "vm_hostname=${var.vm_name}",
      "${var.ansible_debug}"
    ]
  }

  ## Modified by PACKERGEN
  provisioner "ansible" {
    playbook_file = "${var.ansible_playbook_CTX}"
    user          = "packer"
    use_proxy     = false
      extra_arguments = [
      "--extra-vars", "@${var.ansible_config}",
      "-e",
      "ansible_winrm_server_cert_validation=ignore",
      "-e",
      "vm_hostname=${var.vm_name}",
      "${var.ansible_debug}"
    ]
  }

  post-processor "manifest" {
        output = "manifest.json"
        strip_path = true
        custom_data = {
          source_image_name = "${build.SourceImageName}"
          tenant_id = "${build.TenantID}"
          subscription_id = "${build.SubscriptionID}"
          temp_deployment_name = "${build.TempDeploymentName}"
          temp_compute_name = "${build.TempComputeName}"
          temp_nic_name = "${build.TempNicName}"
          temp_os_disk_name = "${build.TempOSDiskName}"
          temp_data_disk_name = "${build.TempDataDiskName}"
          temp_resource_group_name = "${build.TempResourceGroupName}"
          temp_nsg_name = "${build.TempNsgName}"
          temp_key_vault_name = "${build.TempKeyVaultName}"
          temp_subnet_name = "${build.TempSubnetName}"
          temp_virtual_network_name = "${build.TempVirtualNetworkName}"
          temp_public_ip_address_name = "${build.TempPublicIPAddressName}"
        }
  }  
} Example: Packer Image Management Module for Citrix® Virtual Apps and DesktopsAs mentioned above, there are different ways to deploy the Citrix® Virtual Delivery Agent (VDA) on the Master Image: using the autounattend.xml file, a Packer Provisioner, or Ansible. The VDA is a crucial component for a successful Citrix infrastructure. The Packer Image Management Module for Citrix® Virtual Apps and Desktops aims to streamline image management by creating a custom Golden Image with the VDA. It runs the Citrix® Optimizer to improve image efficiency by removing unnecessary bloatware, resulting in optimized performance and resource utilization. A single command can deploy an Image by creating it using Packer.  Note Please note that this module is still in Tech Preview. You can find the actual version here: https://github.com/citrix/citrix-packer-tools Example HCL code for creating a Windows 11-based Image: packer {
  required_plugins {
    azure = {
      source  = "github.com/hashicorp/azure"
      version = "~&gt; 1"
    }
  }
}

variable "client_id" {
  type    = string
  default = "ff1bXXXX-XXXX-XXXX-XXXX-XXXXXXXX53b6"
}

variable "client_object_id" {
  type    = string
  default = "69bdXXXX-XXXX-XXXX-XXXX-XXXXXXXX4244"
}

variable "client_secret" {
  type    = string
  default = "XxXxXxXxXxXxXxXxX"
}

variable "client_subscription_id" {
  type    = string
  default = "de13XXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13"
}

variable "client_tenant_id" {
  type    = string
  default = "1974XXXX-XXXX-XXXX-XXXX-XXXXXXXX474b"
}

variable "image_name" {
  type    = string
  default = "win_office_11_22h2"
}

variable "image_resource_group" {
  type    = string
  default = "TMM-XxXxXxXxX-WEUR"
}

variable "install_browser_chrome_flag" {
  type    = string
  default = "true"
}

variable "install_browser_firefox_flag" {
  type    = string
  default = "true"
}

variable "location_setup" {
  type    = string
  default = "c:\\setup"
}

variable "optimizer_location" {
  type    = string
  default = "https://XxXxXxX.blob.core.windows.net/packer/VDAWorkstationSetup_2503.exe"
}

variable "optimizer_template" {
  type    = string
  default = "Citrix_Windows_11_2009.xml"
}

variable "script_cleanup" {
  type    = string
  default = "run-cleanup.ps1"
}

variable "script_installchrome" {
  type    = string
  default = "install-browser-chrome.ps1"
}

variable "script_installfirefox" {
  type    = string
  default = "install-browser-firefox.ps1"
}

variable "script_installvda" {
  type    = string
  default = "install-vda.ps1"
}

variable "script_optimizer" {
  type    = string
  default = "run-optimizer.ps1"
}

variable "username" {
  type    = string
  default = "XxXxXxXxXxXxXxXxX"
}

variable "userpass" {
  type    = string
  default = "XxXxXxXxXxXxXxXxX"
}

variable "vda_location" {
  type    = string
  default = "https://XxXxXxX.blob.core.windows.net/packer/CitrixOptimizerTool.zip"
}

variable "winrm_insecure" {
  type    = string
  default = "true"
}
# The "legacy_isotime" function has been provided for backwards compatability, but we recommend switching to the timestamp and formatdate functions.

source "azure-arm" "autogenerated_1" {
  azure_tags = {
    Image = "Packer Generated"
  }
  client_id                           = "${var.client_id}"
  client_secret                       = "${var.client_secret}"
  communicator                        = "winrm"
  image_offer                         = "office-365"
  image_publisher                     = "MicrosoftWindowsDesktop"
  image_sku                           = "WIN11-22H2-AVD-M365"
  image_version                       = "latest"
  location                            = "East US"
  managed_image_name                  = "${var.image_name}_${legacy_isotime("200601020304")}"
  managed_image_os_disk_snapshot_name = "${var.image_name}_${legacy_isotime("200601020304")}"
  managed_image_resource_group_name   = "${var.image_resource_group}"
  object_id                           = "${var.client_object_id}"
  os_type                             = "Windows"
  subscription_id                     = "${var.client_subscription_id}"
  tenant_id                           = "${var.client_tenant_id}"
  vm_size                             = "Standard_D2s_v3"
  winrm_insecure                      = "${var.winrm_insecure}"
  winrm_password                      = "${var.userpass}"
  winrm_timeout                       = "20m"
  winrm_use_ssl                       = false
  winrm_username                      = "${var.username}"
}

build {
  sources = ["source.azure-arm.autogenerated_1"]
...
  provisioner "powershell" {
    inline = ["New-Item -Path ${var.location_setup} -ItemType \"directory\" | Out-Null"]
  }

  provisioner "file" {
    destination = "${var.location_setup}\\${var.script_installvda}"
    source      = ".\\scripts\\${var.script_installvda}"
  }

  provisioner "file" {
    destination = "${var.location_setup}\\${var.script_optimizer}"
    source      = ".\\scripts\\${var.script_optimizer}"
  }

  provisioner "file" {
    destination = "${var.location_setup}\\${var.script_installchrome}"
    source      = ".\\scripts\\${var.script_installchrome}"
  }

  provisioner "file" {
    destination = "${var.location_setup}\\${var.script_installfirefox}"
    source      = ".\\scripts\\${var.script_installfirefox}"
  }

  provisioner "file" {
    destination = "${var.location_setup}\\${var.script_cleanup}"
    source      = ".\\scripts\\${var.script_cleanup}"
  }

  provisioner "powershell" {
    inline = ["${var.location_setup}\\${var.script_installvda} -VdaSetupDownloadUri ${var.vda_location}"]
  }

  provisioner "windows-restart" {
    restart_check_command = "powershell -command \"&amp; {Write-Host 'Script: computer restarted.'}\""
    restart_timeout       = "30m"
  }

  provisioner "powershell" {
    inline = ["${var.location_setup}\\${var.script_installvda} -VdaSetupDownloadUri ${var.vda_location}"]
  }

  provisioner "powershell" {
    inline = ["${var.location_setup}\\${var.script_optimizer} -OptimizerDownloadUri ${var.optimizer_location} -Template ${var.optimizer_template}"]
  }

  provisioner "powershell" {
    inline = ["${var.location_setup}\\${var.script_installchrome} -InstallSoftware ${var.install_browser_chrome_flag}"]
  }

  provisioner "powershell" {
    inline = ["${var.location_setup}\\${var.script_installfirefox} -InstallSoftware ${var.install_browser_firefox_flag}"]
  }

  provisioner "powershell" {
    inline = ["${var.location_setup}\\${var.script_cleanup} -Username ${var.username} -LocationPath ${var.location_setup}"]
  }
...
}
Examples from the Field - Creating Master Images with PackerLet´s have a look at real-world examples from the field - we cover using Packer to create Master Images on XenServer, vSphere, and Azure. All these examples have more detailed guides available in our Automation Landing Zone on Citrix TechZone at https://community.citrix.com/tech-zone/automation Please be aware that each Hypervisor and Hyperscaler has different support in Packer – for example, some Packer plugins can automatically add Windows VMs to an AD domain, while others do not. That is why we try to keep as Hypervisor-/Hyperscaler-agnostic as possible – all further configurations, such as changing IP addresses, DNS names, or adding the VM to the AD domain, are handled by Ansible. Ansible does not care about the underlying infrastructure; it talks directly to the target machine to run the desired configuration steps.   Important It is imperative to configure the Master Image for WinRM before proceeding with all further steps. In these examples, we use Packer and/or the autounattend.xml method to configure WinRM. More sophisticated WinRM configurations, such as securing the WinRM transport, are applied after the AD domain join via GPOs. Creating Master Images using Packer on XenServer 8.4Caution XenServer currently lacks an official Packer plugin. In this guide, we use a forked plugin from my Citrite colleague Rody Kossen to showcase the feasibility of end-to-end IaC. https://github.com/xenserver/packer-plugin-xenserver/tree/feature-rodyk-revamp Use the plugin at your own risk – please read the disclaimer at the end of the document for further information. There is NO warranty that it will work as intended – we cannot provide any support. You need to compile the plugin before you can use it. Compiling the Packer plugin for XenServerHere´s a guide for compiling it – we use our Ubuntu IaC machine for compilation: Before compilation, we need to ensure that these prerequisites are installed: Packer – is installed. Golang 1.24.1 – is not installed, need to install Step 1: Install Golang 1.24.1 azadmin@tacg-cicd:~$ wget https://go.dev/dl/go1.24.1.linux-amd64.tar.gz
--2025-11-21 09:32:35--  https://go.dev/dl/go1.24.1.linux-amd64.tar.gz
Resolving go.dev (go.dev)... 216.239.36.21, 216.239.32.21, 216.239.34.21, ...
Connecting to go.dev (go.dev)|216.239.36.21|:443... connected.
HTTP request sent, awaiting response... 302 Found
Location: https://dl.google.com/go/go1.24.1.linux-amd64.tar.gz [following]
--2025-11-21 09:32:36--  https://dl.google.com/go/go1.24.1.linux-amd64.tar.gz
Resolving dl.google.com (dl.google.com)... 142.251.39.78, 2a00:1450:400d:80e::200e
Connecting to dl.google.com (dl.google.com)|142.251.39.78|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 78503123 (75M) [application/x-gzip]
Saving to: ‘go1.24.1.linux-amd64.tar.gz’

go1.24.1.linux-amd64.tar.gz   100%[=================================================&gt;]  74.87M  29.5MB/s    in 2.5s

2025-11-21 09:32:38 (29.5 MB/s) - ‘go1.24.1.linux-amd64.tar.gz’ saved [78503123/78503123]

azadmin@tacg-cicd:~$ 
azadmin@tacg-cicd:~$ sudo tar -C /usr/local -xzf go1.24.1.linux-amd64.tar.gz
azadmin@tacg-cicd:~$ 
azadmin@tacg-cicd:~$ ls -l /usr/local/go*
total 76
drwxr-xr-x  2 root root  4096 Feb 27  2025 api
drwxr-xr-x  2 root root  4096 Feb 27  2025 bin
-rw-r--r--  1 root root    52 Feb 27  2025 codereview.cfg
-rw-r--r--  1 root root  1337 Feb 27  2025 CONTRIBUTING.md
drwxr-xr-x  3 root root  4096 Feb 27  2025 doc
-rw-r--r--  1 root root   505 Feb 27  2025 go.env
drwxr-xr-x  5 root root  4096 Feb 27  2025 lib
-rw-r--r--  1 root root  1453 Feb 27  2025 LICENSE
drwxr-xr-x  8 root root  4096 Feb 27  2025 misc
-rw-r--r--  1 root root  1303 Feb 27  2025 PATENTS
drwxr-xr-x  4 root root  4096 Feb 27  2025 pkg
-rw-r--r--  1 root root  1454 Feb 27  2025 README.md
-rw-r--r--  1 root root   426 Feb 27  2025 SECURITY.md
drwxr-xr-x 56 root root  4096 Feb 27  2025 src
drwxr-xr-x 28 root root 16384 Feb 27  2025 test
-rw-r--r--  1 root root    35 Feb 27  2025 VERSION
azadmin@tacg-cicd:~$
azadmin@tacg-cicd:~$ sudo nano ~/.profile
azadmin@tacg-cicd:~$
azadmin@tacg-cicd:~$ source ~/.profile
azadmin@tacg-cicd:~$
azadmin@tacg-cicd:~$ go version
go version go1.24.1 linux/amd64
azadmin@tacg-cicd:~$Go is successfully installed. Step 2: Download the plugin source from GitHub and place it into a feasible directory We downloaded the source and put it into “packer-plugin-xenserver-feature-rodyk-revamp” azadmin@tacg-cicd:~$ ls
cert_key.pem  Documents                    Music                                         packer-templates  snap
cert.pem      Downloads                    packer-examples-for-vsphere                   Pictures          Templates
Desktop       go1.24.1.linux-amd64.tar.gz  packer-plugin-xenserver-feature-rodyk-revamp  Public            Videos
azadmin@tacg-cicd:~$Step 3: Compile the plugin azadmin@tacg-cicd:~/packer-plugin-xenserver-feature-rodyk-revamp$ make
builder/xenserver/common/client.go:9:2: xenapi@v0.0.0-00010101000000-000000000000: replacement directory ./goSDK does not exist
make: *** [GNUmakefile:11: build] Error 1

azadmin@tacg-cicd:~/packer-plugin-xenserver-feature-rodyk-revamp$ This error indicates that the XenServer SDK-GO files are missing. We need to download them into a subdirectory to make them accessible for compilation: azadmin@tacg-cicd:~/packer-plugin-xenserver-feature-rodyk-revamp$ wget -P /home/azadmin/Downloads https://downloads.xenserver.com/sdk/25.30.0/XenServer-SDK-25.30.0.zip
--2025-11-21 10:39:31--  https://downloads.xenserver.com/sdk/25.30.0/XenServer-SDK-25.30.0.zip
Resolving downloads.xenserver.com (downloads.xenserver.com)... 13.107.226.44, 13.107.253.44, 2620:1ec:48:1::44, ...
Connecting to downloads.xenserver.com (downloads.xenserver.com)|13.107.226.44|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 19568538 (19M) [application/zip]
Saving to: ‘/home/azadmin/Downloads/XenServer-SDK-25.30.0.zip’

XenServer-SDK-25.30.0.zip                          100%[================================================================================================================&gt;]  18.66M  21.7MB/s    in 0.9s

2025-11-21 10:39:32 (21.7 MB/s) - ‘/home/azadmin/Downloads/XenServer-SDK-25.30.0.zip’ saved [19568538/19568538]

azadmin@tacg-cicd:~/packer-plugin-xenserver-feature-rodyk-revamp$
azadmin@tacg-cicd:~/packer-plugin-xenserver-feature-rodyk-revamp$ mkdir goSDK
azadmin@tacg-cicd:~/packer-plugin-xenserver-feature-rodyk-revamp$ 
azadmin@tacg-cicd:~/packer-plugin-xenserver-feature-rodyk-revamp/goSDK$ unzip /home/azadmin/Downloads/XenServer-SDK-25.30.0.zip -d /home/azadmin/Downloads/XenServer-SDK-25.30.0
...
azadmin@tacg-cicd:~/packer-plugin-xenserver-feature-rodyk-revamp$ 
azadmin@tacg-cicd:~/packer-plugin-xenserver-feature-rodyk-revamp$ cp -r /home/azadmin/Downloads/XenServer-SDK-25.30.0/XenServer-SDK/XenServerGo/src/. /home/azadmin/packer-plugin-xenserver-feature-rodyk-re
vamp/goSDK
azadmin@tacg-cicd:~/packer-plugin-xenserver-feature-rodyk-revamp$
azadmin@tacg-cicd:~/packer-plugin-xenserver-feature-rodyk-revamp$ cd goSDK
azadmin@tacg-cicd:~/packer-plugin-xenserver-feature-rodyk-revamp/goSDK$ ls
api_errors.go    cluster_host.go    enums.go           host_driver.go     network_sriov.go  pool_patch.go         repository.go      subject.go      vdi_nbd_server_info.go  vm_group.go
api_messages.go  console.go         event.go           host.go            observer.go       pool_update.go        role.go            task.go         vgpu.go                 vm_guest_metrics.go
api_versions.go  convert.go         export_test.go     host_metrics.go    pbd.go            probe_result.go       sdn_controller.go  tunnel.go       vgpu_type.go            vm_metrics.go
auth.go          convert_test.go    feature.go         host_patch.go      pci.go            pusb.go               secret.go          usb_group.go    vif.go                  vmpp.go
blob.go          crashdump.go       go.mod             jsonrpc_client.go  pgpu.go           pvs_cache_storage.go  session.go         user.go         vif_metrics.go          vmss.go
bond.go          data_source.go     gpu_group.go       lvhd.go            pif.go            pvs_proxy.go          sm.go              vbd.go          vlan.go                 vtpm.go
certificate.go   driver_variant.go  host_cpu.go        message.go         pif_metrics.go    pvs_server.go         sr.go              vbd_metrics.go  vm_appliance.go         vusb.go
cluster.go       dr_task.go         host_crashdump.go  network.go         pool.go           pvs_site.go           sr_stat.go         vdi.go          vm.go
azadmin@tacg-cicd:~/packer-plugin-xenserver-feature-rodyk-revamp/goSDK$As the required SDK files are now in place, we can start the compilation again: azadmin@tacg-cicd:~/packer-plugin-xenserver-feature-rodyk-revamp$ make
go: downloading github.com/hashicorp/packer-plugin-sdk v0.6.2
go: downloading github.com/hashicorp/hcl/v2 v2.24.0
go: downloading github.com/hashicorp/hcl v1.0.0
...
go: downloading github.com/golang/protobuf v1.5.3
go: downloading github.com/mattn/go-colorable v0.1.13
go: downloading github.com/hashicorp/go-immutable-radix v1.3.1
go: downloading golang.org/x/sync v0.14.0
go: downloading github.com/hashicorp/golang-lru v0.5.4
azadmin@tacg-cicd:~/packer-plugin-xenserver-feature-rodyk-revamp$
azadmin@tacg-cicd:~/packer-plugin-xenserver-feature-rodyk-revamp$ ls
builder  docs  examples  GNUmakefile  go.mod  goSDK  go.sum  LICENSE  main.go  packer-plugin-xenserver  README.md  version
azadmin@tacg-cicd:~/packer-plugin-xenserver-feature-rodyk-revamp$The compilation was successful, now we need to install the plugin in Packer. Step 4: Install the plugin azadmin@tacg-cicd:~/packer-plugin-xenserver-feature-rodyk-revamp$ sudo packer plugins install --path packer-plugin-xenserver github.com/xenserver/xenserver
Successfully installed plugin github.com/xenserver/xenserver from /home/azadmin/packer-plugin-xenserver-feature-rodyk-revamp/packer-plugin-xenserver to /root/.config/packer/plugins/github.com/xenserver/xenserver/packer-plugin-xenserver_v0.9.1_x5.0_linux_amd64
azadmin@tacg-cicd:~/packer-plugin-xenserver-feature-rodyk-revamp$ 
azadmin@tacg-cicd:~/packer-plugin-xenserver-feature-rodyk-revamp$ sudo packer plugins installed
/root/.config/packer/plugins/github.com/xenserver/xenserver/packer-plugin-xenserver_v0.9.1_x5.0_linux_amd64
azadmin@tacg-cicd:~/packer-plugin-xenserver-feature-rodyk-revamp$Installation is complete, let´s create some VMs now. For a detailed overview of the builder, please look at the plugin´s documentation. Example Packer HCL file for deploying a Windows Server 2025-VM on XenServer 8: packer {
  required_plugins {
   xenserver= {
      version = "&gt;= v0.1.0"
      source = "github.com/xenserver/xenserver"
    }
  }
}

source "xenserver-iso" "XSTEST" {

    iso_checksum_type           = "none"
	iso_name					= var.os_iso_path
    sr_iso_name                 = var.sr_iso_name
    sr_name                     = var.sr_name
    tools_iso_name              = var.tools_iso_name

    remote_host                 = var.remote_host
    remote_password             = var.remote_password
    remote_username             = var.remote_username

    vm_name                     = var.vm_name
    vm_description              = var.vm_description
    vm_memory                   = var.memory
    disk_size                   = var.disksize
	vcpus				        = var.cpu
	cores_per_socket			= var.cpu
	network_names				= ["${var.virtual_network_name}"]
	firmware                    = var.firmware
    secure_boot 				= var.secure_boot
    vTPM                        = var.vtpm
    vgpu_profile                = var.vgpu_profile

	clone_template				= var.clone_template

    cd_files                	= ["W2K25AutoUnattendInclWinRM.xml"]

	ssh_username            	= var.ssh_username
	ssh_password            	= var.ssh_password
	ssh_wait_timeout        	= "60000s"
	
    boot_command = [
        "&lt;spacebar&gt;&lt;wait2&gt;&lt;spacebar&gt;"
    ]
    boot_wait                   = "1s"
	
    #Export Options
	format 						= "xva"
    keep_vm 					= "on_success"

    communicator                = "winrm"
    winrm_insecure              = true
    winrm_password              = var.winrm_password
    winrm_timeout               = "5m"
    winrm_use_ssl               = false
    winrm_username              = var.winrm_username
}

build {
   sources = ["xenserver-iso.windows"]
  
   provisioner "powershell" {
    inline = ["Write-Host 'Installing updates and XenServer Tools...'", "Start-Process msiexec.exe -ArgumentList '/i D:\\xensetup\\managementagentx64.msi /qn' -Wait”]
  }
}In this example, we use a modified autounattend.xml file to configure the Windows VM during creation: &lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;unattend xmlns="urn:schemas-microsoft-com:unattend"&gt;
  &lt;settings pass="windowsPE"&gt;
    &lt;component name="Microsoft-Windows-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"&gt;
      &lt;DiskConfiguration&gt;
        &lt;Disk wcm:action="add"&gt;
          &lt;DiskID&gt;0&lt;/DiskID&gt;
          &lt;WillWipeDisk&gt;true&lt;/WillWipeDisk&gt;
          &lt;CreatePartitions&gt;
            &lt;CreatePartition wcm:action="add"&gt;
              &lt;Order&gt;1&lt;/Order&gt;
              &lt;Type&gt;Primary&lt;/Type&gt;
              &lt;Size&gt;100&lt;/Size&gt;
            &lt;/CreatePartition&gt;
            &lt;CreatePartition wcm:action="add"&gt;
              &lt;Order&gt;2&lt;/Order&gt;
              &lt;Type&gt;Primary&lt;/Type&gt;
              &lt;Extend&gt;true&lt;/Extend&gt;
            &lt;/CreatePartition&gt;
          &lt;/CreatePartitions&gt;
          &lt;ModifyPartitions&gt;
            &lt;ModifyPartition wcm:action="add"&gt;
              &lt;Order&gt;1&lt;/Order&gt;
              &lt;PartitionID&gt;1&lt;/PartitionID&gt;
              &lt;Active&gt;true&lt;/Active&gt;
              &lt;Label&gt;System&lt;/Label&gt;
              &lt;Format&gt;NTFS&lt;/Format&gt;
            &lt;/ModifyPartition&gt;
            &lt;ModifyPartition wcm:action="add"&gt;
              &lt;Order&gt;2&lt;/Order&gt;
              &lt;PartitionID&gt;2&lt;/PartitionID&gt;
              &lt;Label&gt;Windows&lt;/Label&gt;
              &lt;Format&gt;NTFS&lt;/Format&gt;
              &lt;Letter&gt;C&lt;/Letter&gt;
            &lt;/ModifyPartition&gt;
          &lt;/ModifyPartitions&gt;
        &lt;/Disk&gt;
        &lt;WillShowUI&gt;OnError&lt;/WillShowUI&gt;
      &lt;/DiskConfiguration&gt;

      &lt;ImageInstall&gt;
        &lt;OSImage&gt;
          &lt;InstallFrom&gt;
            &lt;MetaData wcm:action="add"&gt;
              &lt;Key&gt;/IMAGE/INDEX&lt;/Key&gt;
              &lt;Value&gt;1&lt;/Value&gt;
            &lt;/MetaData&gt;
          &lt;/InstallFrom&gt;
          &lt;InstallTo&gt;
            &lt;DiskID&gt;0&lt;/DiskID&gt;
            &lt;PartitionID&gt;2&lt;/PartitionID&gt;
          &lt;/InstallTo&gt;
        &lt;/OSImage&gt;
      &lt;/ImageInstall&gt;

      &lt;UserData&gt;
        &lt;ProductKey&gt;
          &lt;Key&gt;&lt;/Key&gt;
          &lt;WillShowUI&gt;Never&lt;/WillShowUI&gt;
        &lt;/ProductKey&gt;
        &lt;AcceptEula&gt;true&lt;/AcceptEula&gt;
        &lt;FullName&gt;Packer Builder&lt;/FullName&gt;
        &lt;Organization&gt;ExampleCorp&lt;/Organization&gt;
      &lt;/UserData&gt;
    &lt;/component&gt;
  &lt;/settings&gt;

  &lt;settings pass="specialize"&gt;
    &lt;component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"&gt;
      &lt;ComputerName&gt;WIN2025BASE&lt;/ComputerName&gt;
      &lt;TimeZone&gt;UTC&lt;/TimeZone&gt;
    &lt;/component&gt;

    &lt;component name="Microsoft-Windows-Deployment" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"&gt;
      &lt;RunSynchronous&gt;
        &lt;RunSynchronousCommand&gt;
          &lt;Order&gt;1&lt;/Order&gt;
          &lt;Path&gt;cmd /c winrm quickconfig -quiet&lt;/Path&gt;
          &lt;Description&gt;Enable WinRM&lt;/Description&gt;
        &lt;/RunSynchronousCommand&gt;
        &lt;RunSynchronousCommand&gt;
          &lt;Order&gt;2&lt;/Order&gt;
          &lt;Path&gt;cmd /c winrm set winrm/config/service/auth @{Basic="true"}&lt;/Path&gt;
          &lt;Description&gt;Enable Basic Auth&lt;/Description&gt;
        &lt;/RunSynchronousCommand&gt;
        &lt;RunSynchronousCommand&gt;
          &lt;Order&gt;3&lt;/Order&gt;
          &lt;Path&gt;cmd /c winrm set winrm/config/service @{AllowUnencrypted="true"}&lt;/Path&gt;
          &lt;Description&gt;Allow Unencrypted&lt;/Description&gt;
        &lt;/RunSynchronousCommand&gt;
        &lt;RunSynchronousCommand&gt;
          &lt;Order&gt;4&lt;/Order&gt;
          &lt;Path&gt;cmd /c netsh advfirewall set allprofiles state off&lt;/Path&gt;
          &lt;Description&gt;Disable Firewall&lt;/Description&gt;
        &lt;/RunSynchronousCommand&gt;
        &lt;RunSynchronousCommand&gt;
          &lt;Order&gt;5&lt;/Order&gt;
          &lt;Path&gt;cmd /c winrm set winrm/config/client @{TrustedHosts="*"}&lt;/Path&gt;
          &lt;Description&gt;Disable Firewall&lt;/Description&gt;
        &lt;/RunSynchronousCommand&gt;
      &lt;/RunSynchronous&gt;
    &lt;/component&gt;
  &lt;/settings&gt;

   &lt;settings pass="oobeSystem"&gt;
    &lt;component name="Microsoft-Windows-International-Core" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"&gt;
      &lt;InputLocale&gt;de-AT&lt;/InputLocale&gt;
      &lt;SystemLocale&gt;en-US&lt;/SystemLocale&gt;
      &lt;UILanguage&gt;en-US&lt;/UILanguage&gt;
      &lt;UserLocale&gt;de-AT&lt;/UserLocale&gt;
    &lt;/component&gt;
    &lt;component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"&gt;
      &lt;OOBE&gt;
        &lt;HideEULAPage&gt;true&lt;/HideEULAPage&gt;
        &lt;NetworkLocation&gt;Work&lt;/NetworkLocation&gt;
        &lt;ProtectYourPC&gt;3&lt;/ProtectYourPC&gt;
      &lt;/OOBE&gt;
      &lt;UserAccounts&gt;
        &lt;AdministratorPassword&gt;
          &lt;Value&gt;!XxXxXxXxXxXxXxXxX!&lt;/Value&gt;
          &lt;PlainText&gt;true&lt;/PlainText&gt;
        &lt;/AdministratorPassword&gt;
      &lt;/UserAccounts&gt;
      &lt;AutoLogon&gt;
        &lt;Username&gt;Administrator&lt;/Username&gt;
        &lt;Password&gt;
          &lt;Value&gt;!XxXxXxXxXxXxXxXxX!&lt;/Value&gt;
          &lt;PlainText&gt;true&lt;/PlainText&gt;
        &lt;/Password&gt;
        &lt;Enabled&gt;true&lt;/Enabled&gt;
        &lt;LogonCount&gt;1&lt;/LogonCount&gt;
      &lt;/AutoLogon&gt;

      &lt;FirstLogonCommands&gt;
      &lt;/FirstLogonCommands&gt;
      
    &lt;/component&gt;
  &lt;/settings&gt;

  &lt;cpi:offlineImage cpi:source="wim://wimfile/install.wim#Windows Server 2025 SERVERSTANDARD" xmlns:cpi="urn:schemas-microsoft-com:cpi" /&gt;
&lt;/unattend&gt;Running the Packer IaC snippet should create a XenServer XVA that can then be used as a Master Image: azadmin@tacg-cicd:~$ packer build packer-tmpl-w2k25-xs.pkr.hcl
xenserver-iso.XSTEST: output will be in this color.

==&gt; xenserver-iso.XSTEST: XAPI client session established
==&gt; xenserver-iso.XSTEST: Retrieving ISO
==&gt; xenserver-iso.XSTEST: Trying http://10.10.100.1/iso/windows_server_2025.iso
==&gt; xenserver-iso.XSTEST: Trying http://10.10.100.1/iso/windows_server_2025.iso
==&gt; xenserver-iso.XSTEST: http://10.10.100.1/iso/windows_server_2025.iso =&gt; C:\__PPMM\_PACKER\_XENSERVER\packer_cache\4282f11098333441b4a8c39028041a36490797cb
==&gt; xenserver-iso.XSTEST: Creating CD disk...
    xenserver-iso.XSTEST: OSCDIMG 2.56 CD-ROM and DVD-ROM Premastering Utility
    xenserver-iso.XSTEST: Copyright (C) Microsoft, 1993-2012. All rights reserved.
    xenserver-iso.XSTEST: Licensed only for producing Microsoft authorized content.
    xenserver-iso.XSTEST: Scanning source tree
    xenserver-iso.XSTEST: Scanning source tree complete (1 files in 1 directories)
    xenserver-iso.XSTEST: Computing directory information complete
    xenserver-iso.XSTEST: Image file is 57344 bytes (before optimization)
    xenserver-iso.XSTEST: Writing 1 files in 1 directories to tmp/packer1625305315.iso
    xenserver-iso.XSTEST: Storage optimization saved 0 files, 0 bytes (0% of image)
    xenserver-iso.XSTEST: After optimization, image file is 57344 bytes
    xenserver-iso.XSTEST: Done.
    xenserver-iso.XSTEST: 100% complete
    xenserver-iso.XSTEST: Done copying paths from CD_dirs
==&gt; xenserver-iso.XSTEST: Step: Upload VDI 'Packer-CD'
==&gt; xenserver-iso.XSTEST: Step: Found SR for upload 'OpaqueRef:ed24af24-cd3e-4ed0-2303-d885e72a691a'
==&gt; xenserver-iso.XSTEST: PUT 'https://10.10.119.1/import_raw_vdi?session_id=OpaqueRef%3A93b232ae-a9bf-8b83-bb54-9aaf7412b6ba&amp;task_id=OpaqueRef%3A99d66110-5044-37b2-7b21-baa23c54d2e1&amp;vdi=OpaqueRef%3A65a7da41-3aba-e5b1-d1f1-0e5f006286f4'
==&gt; xenserver-iso.XSTEST: Attemping to find VDI 'windows_server_2025.iso'
==&gt; xenserver-iso.XSTEST: Step: Upload VDI 'windows_server_2025.iso'
==&gt; xenserver-iso.XSTEST: Step: Found SR for upload 'OpaqueRef:ed24af24-cd3e-4ed0-2303-d885e72a691a'
==&gt; xenserver-iso.XSTEST: PUT 'https://10.10.119.1/import_raw_vdi?session_id=OpaqueRef%3A93b232ae-a9bf-8b83-bb54-9aaf7412b6ba&amp;task_id=OpaqueRef%3Ac8d26e51-2289-49b5-5363-220848bb4b6c&amp;vdi=OpaqueRef%3A4893a784-2514-12b3-20bf-9880fc5bf3be'
==&gt; xenserver-iso.XSTEST: Step: Create Instance
==&gt; xenserver-iso.XSTEST: Using the following SR for the VM: OpaqueRef:67690a5f-2856-f785-7cac-fb59728d7ef2
==&gt; xenserver-iso.XSTEST: Created instance '2d4f4efc-5a3d-d915-380c-f86b0108eabe'
==&gt; xenserver-iso.XSTEST: Step: Start VM Paused
==&gt; xenserver-iso.XSTEST: Step: Set SSH address to VM host IP
==&gt; xenserver-iso.XSTEST: Set host SSH address to '10.10.119.1'.
==&gt; xenserver-iso.XSTEST: Set host SSH port to 22.
==&gt; xenserver-iso.XSTEST: Unpausing VM 2d4f4efc-5a3d-d915-380c-f86b0108eabe
==&gt; xenserver-iso.XSTEST: Waiting 10s for boot...
==&gt; xenserver-iso.XSTEST: Connecting to VNC over XAPI...
    xenserver-iso.XSTEST: Making HTTP request to initiate VNC connection: CONNECT /console?uuid=6e861319-fb00-a8c8-d607-615b6c4561fe HTTP/1.0
    xenserver-iso.XSTEST: Host: 10.10.119.1
    xenserver-iso.XSTEST: Cookie: session_id=OpaqueRef:93b232ae-a9bf-8b83-bb54-9aaf7412b6ba
    xenserver-iso.XSTEST:
    xenserver-iso.XSTEST:
    xenserver-iso.XSTEST: Received response: HTTP/1.1 200 OK
    xenserver-iso.XSTEST: Connection: keep-alive
    xenserver-iso.XSTEST: Cache-Control: no-cache, no-store
    xenserver-iso.XSTEST:
    xenserver-iso.XSTEST:
    xenserver-iso.XSTEST: Found local IP: 10.10.11.99
==&gt; xenserver-iso.XSTEST: Typing boot commands over VNC...
==&gt; xenserver-iso.XSTEST: Step: Wait for VMs IP to become known to us.
==&gt; xenserver-iso.XSTEST: Successfully installed
==&gt; xenserver-iso.XSTEST: Destroying VDI
==&gt; xenserver-iso.XSTEST: Destroyed VDI 'windows_server_2025.iso'
==&gt; xenserver-iso.XSTEST: Destroyed VDI 'Packer-CD'
==&gt; xenserver-iso.XSTEST: Deleting output directory...
==&gt; xenserver-iso.XSTEST: Closing sessions ....
Build 'xenserver-iso.XSTEST' finished after 43 minutes 52 seconds.

==&gt; Wait completed after 43 minutes 52 seconds

==&gt; Builds finished. The artifacts of successful builds are:
--&gt; ==&gt; xenserver-iso.XSTEST: W2K25.2511

azadmin@tacg-cicd:~$After a successful run, XenServer should reflect the created VM, and after conversion, the corresponding template. Creating Master Images using Packer on vSphere 8This example demonstrates creating a Windows 11-based Master Image on vSphere 8. After the VM is successfully created, it is converted by a post-processor into a vSphere template for further use. For a detailed overview of the builder, please look at the plugin´s documentation. Example Packer HCL file for deploying a Windows 11-VM on vSphere 8: locals {
  WinRMUser        = "administrator"
  WinRMPass        = "!XxXxXxXxXxXxXxXxX!"
  vcenter_admin    = "administrator@vsphere.local"
  vcenter_password = "!XxXxXxXxXxXxXxXxX!"
  timestamp        = regex_replace(timestamp(), "[- TZ:01]", "")
  buildtime        = formatdate("DD-MM-YYYY hh:mm ZZZ", timestamp())
  manifest_date    = formatdate("DD-MM-YYYY hh:mm:ss", timestamp())
}

packer {
  required_version = "&gt;= 1.9.0"
  required_plugins {
    vsphere = {
      version = "&gt;= v1.4.2"
      source  = "github.com/vmware/vsphere"
    }
  }
  required_plugins {
    windows-update = {
      version = "&gt;= 0.16.8"
      source  = "github.com/rgl/windows-update"
    }
  }
}

variable "host" {
  type        = string
  description = "VMware ESXi host"
}

variable "name" {
  type        = string
  description = "Golden Image name"
}

variable "operating_system_vm" {
  type        = string
  description = "OS Guest OS"
}

variable "vcenter_cluster" {
  type        = string
  description = "vCenter cluster name"
}

variable "vcenter_datastore" {
  type        = string
  description = "vSphere datastore"
}

variable "vcenter_host" {
  type        = string
  description = "vCenter Server"
}

variable "vcenter_network" {
  type        = string
  description = "Portgroup name"
}

variable "vm_cores" {
  type        = string
  description = "Amount of cores"
}

variable "vm_cpus" {
  type        = string
  description = "amount of vCPUs"
}

variable "vm_disk_controller_type" {
  type        = list(string)
  description = "Controller type"
}

variable "vm_disk_size" {
  type        = string
  description = "Harddisk size"
}

variable "vm_disk_thin" {
  type        = string
  description = "Enable/Disable thin provisioning"
}

variable "vm_hardwareversion" {
  type        = string
  description = "VM hardware version"
}

variable "vm_firmware" {
  type        = string
  description = "The virtual machine firmware. (e.g. 'efi-secure'. 'efi', or 'bios')"
  default     = "efi-secure"
}

variable "vm_cdrom_type" {
  type        = string
  description = "The virtual machine CD-ROM type. (e.g. 'sata', or 'ide')"
  default     = "sata"
}

variable "common_remove_cdrom" {
  type        = bool
  description = "Remove the virtual CD-ROM(s)."
  default     = true
}

variable "vm_hotplug" {
  type        = string
  description = "Enable/Disable hotplug?"
}

variable "vm_logging" {
  type        = string
  description = "Enable/Disable VM Logging"
}

variable "vm_memory" {
  type        = string
  description = "VM Memory"
}

variable "vm_memory_reserve_all" {
  type        = string
  description = "Reserve all memory?"
}

variable "vm_network_card" {
  type        = string
  description = "Networkcard type"
}

variable "winsrv_iso" {
  type        = string
  description = "Windows Server ISO location"
}

variable "vm_vgpu" {
  type        = string
  description = "VM vGPU size"
}

variable "vm_boot_order" {
  type        = string
  description = "The boot order for virtual machines devices. (e.g. 'disk,cdrom')"
  default     = "disk,cdrom"
}

variable "vm_boot_wait" {
  type        = string
  description = "The time to wait before boot."
}

variable "vm_boot_command" {
  type        = list(string)
  description = "The virtual machine boot command."
  default     = []
}

variable "vm_shutdown_command" {
  type        = string
  description = "Command(s) for guest operating system shutdown."
}

variable "vm_tpm" {
  type    = string
  default = "true"
}

variable "create_snapshot" {
  type = string
}

variable "remove_cdrom" {
  type = string
}

variable "vbs_enabled" {
  type = string
}

variable "vvtd_enabled" {
  type = string
}

source "vsphere-iso" "win11_iso" {
  CPUs     = var.vm_cpus
  notes    = "Built by HashiCorp Packer on ${local.buildtime}."
  RAM      = var.vm_memory
  firmware = var.vm_firmware
  NestedHV        = true
  RAM_reserve_all = var.vm_memory_reserve_all
  cluster         = var.vcenter_cluster
  configuration_parameters = {
    "devices.hotplug"  = var.vm_hotplug
    "logging"          = var.vm_logging
    "svga.autodetect"  = "FALSE"
    "svga.numDisplays" = "2"
  }
  create_snapshot      = var.create_snapshot
  remove_cdrom         = var.remove_cdrom
  cpu_cores            = var.vm_cores
  datastore            = var.vcenter_datastore
  disk_controller_type = var.vm_disk_controller_type
  floppy_files         = ["${path.root}/setup/"]
  guest_os_type        = var.operating_system_vm
  host                 = var.host
  iso_paths            = [var.winsrv_iso]
  network_adapters {
    network      = var.vcenter_network
    network_card = var.vm_network_card
  }
  storage {
    disk_size             = var.vm_disk_size
    disk_thin_provisioned = var.vm_disk_thin
  }
  username       = local.vcenter_admin
  password       = local.vcenter_password
  vcenter_server = var.vcenter_host
  vm_name        = var.name
  vm_version     = var.vm_hardwareversion
  vTPM = var.vm_tpm
  vbs_enabled  = var.vbs_enabled
  vvtd_enabled = var.vvtd_enabled
  insecure_connection = "true"
  communicator        = "winrm"
  winrm_port          = "5985"
  winrm_username      = local.WinRMUser
  winrm_password      = local.WinRMPass
  winrm_timeout       = "12h"
  boot_order       = var.vm_boot_order
  boot_wait        = var.vm_boot_wait
  boot_command     = var.vm_boot_command
  shutdown_command = var.vm_shutdown_command
}

build {
  sources = ["source.vsphere-iso.win11_iso"]

  provisioner "windows-restart" {
    restart_check_command = "powershell -command \"&amp; {Write-Output 'restarted.'}\""
    restart_timeout       = "20m"
  }

  provisioner "powershell" {
    elevated_user     = local.WinRMUser
    elevated_password = local.WinRMPass
    valid_exit_codes  = [0]
    scripts = [
      "./setup/install-vda.ps1",
    ]
  }

  provisioner "powershell" {
    elevated_user     = local.WinRMUser
    elevated_password = local.WinRMPass
    valid_exit_codes  = [0, 267014]
    scripts = [
      "./setup/windows-init.ps1",
    ]
  }

  post-processors {
      post-processor "vsphere-template"{
          host                = var.vcenter_host
          insecure            = true
          username            = local.vcenter_admin
          password            = local.vcenter_password
          datacenter          = var.vcenter_datacenter
          folder              = "/vmfs/volumes/XxXxXxXxXxXxXxX"
      }
    }
}In this example, we use a modified autounattend.xml file to configure the Windows VM during creation: &lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;unattend xmlns="urn:schemas-microsoft-com:unattend"&gt;
    &lt;settings pass="windowsPE"&gt;
        &lt;component name="Microsoft-Windows-PnpCustomizationsWinPE" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;
            &lt;DriverPaths&gt;
                &lt;PathAndCredentials wcm:action="add" wcm:keyValue="1"&gt;
                    &lt;Path&gt;E:\&lt;/Path&gt;
                &lt;/PathAndCredentials&gt;
            &lt;/DriverPaths&gt;
        &lt;/component&gt;
        &lt;component name="Microsoft-Windows-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;
            &lt;DiskConfiguration&gt;
                &lt;Disk wcm:action="add"&gt;
                    &lt;CreatePartitions&gt;
                        &lt;CreatePartition wcm:action="add"&gt;
                            &lt;Order&gt;1&lt;/Order&gt;
                            &lt;Type&gt;EFI&lt;/Type&gt;
                            &lt;Size&gt;100&lt;/Size&gt;
                        &lt;/CreatePartition&gt;
                        &lt;CreatePartition wcm:action="add"&gt;
                            &lt;Order&gt;2&lt;/Order&gt;
                            &lt;Type&gt;MSR&lt;/Type&gt;
                            &lt;Size&gt;16&lt;/Size&gt;
                        &lt;/CreatePartition&gt;
                        &lt;CreatePartition wcm:action="add"&gt;
                            &lt;Order&gt;3&lt;/Order&gt;
                            &lt;Type&gt;Primary&lt;/Type&gt;
                            &lt;Extend&gt;true&lt;/Extend&gt;
                        &lt;/CreatePartition&gt;
                    &lt;/CreatePartitions&gt;
                    &lt;ModifyPartitions&gt;
                        &lt;ModifyPartition wcm:action="add"&gt;
                            &lt;Order&gt;1&lt;/Order&gt;
                            &lt;Format&gt;FAT32&lt;/Format&gt;
                            &lt;Label&gt;System&lt;/Label&gt;
                            &lt;PartitionID&gt;1&lt;/PartitionID&gt;
                        &lt;/ModifyPartition&gt;
                        &lt;ModifyPartition wcm:action="add"&gt;
                            &lt;Order&gt;2&lt;/Order&gt;
                            &lt;PartitionID&gt;2&lt;/PartitionID&gt;
                        &lt;/ModifyPartition&gt;
                        &lt;ModifyPartition wcm:action="add"&gt;
                            &lt;Order&gt;3&lt;/Order&gt;
                            &lt;Format&gt;NTFS&lt;/Format&gt;
                            &lt;Label&gt;Windows&lt;/Label&gt;
                            &lt;Letter&gt;C&lt;/Letter&gt;
                            &lt;PartitionID&gt;3&lt;/PartitionID&gt;
                        &lt;/ModifyPartition&gt;
                    &lt;/ModifyPartitions&gt;
                    &lt;DiskID&gt;0&lt;/DiskID&gt;
                    &lt;WillWipeDisk&gt;true&lt;/WillWipeDisk&gt;
                &lt;/Disk&gt;
            &lt;/DiskConfiguration&gt;
            &lt;ImageInstall&gt;
                &lt;OSImage&gt;
                    &lt;InstallTo&gt;
                        &lt;DiskID&gt;0&lt;/DiskID&gt;
                        &lt;PartitionID&gt;3&lt;/PartitionID&gt;
                    &lt;/InstallTo&gt;
                    &lt;InstallFrom&gt;
                        &lt;MetaData wcm:action="add"&gt;
                            &lt;Key&gt;/IMAGE/INDEX&lt;/Key&gt;
                            &lt;Value&gt;1&lt;/Value&gt;
                        &lt;/MetaData&gt;
                    &lt;/InstallFrom&gt;
                &lt;/OSImage&gt;
            &lt;/ImageInstall&gt;
            &lt;UserData&gt;
                &lt;ProductKey&gt;
                    &lt;Key&gt;VK7JG-NPHTM-C97JM-9MPGT-3V66T&lt;/Key&gt;
                    &lt;WillShowUI&gt;OnError&lt;/WillShowUI&gt;
                &lt;/ProductKey&gt;
                &lt;AcceptEula&gt;true&lt;/AcceptEula&gt;
                &lt;FullName&gt;TACG&lt;/FullName&gt;
                &lt;Organization&gt;TACG&lt;/Organization&gt;
            &lt;/UserData&gt;
            &lt;RunSynchronous&gt;
                &lt;RunSynchronousCommand wcm:action="add"&gt;
                    &lt;Order&gt;1&lt;/Order&gt;
                    &lt;Path&gt;reg add HKLM\SYSTEM\Setup\LabConfig /v BypassTPMCheck /t REG_DWORD /d 1&lt;/Path&gt;
                &lt;/RunSynchronousCommand&gt;
                &lt;RunSynchronousCommand wcm:action="add"&gt;
                    &lt;Order&gt;2&lt;/Order&gt;
                    &lt;Path&gt;reg add HKLM\SYSTEM\Setup\LabConfig /v BypassSecureBootCheck /t REG_DWORD /d 1&lt;/Path&gt;
                &lt;/RunSynchronousCommand&gt;
                &lt;RunSynchronousCommand wcm:action="add"&gt;
                    &lt;Order&gt;3&lt;/Order&gt;
                    &lt;Path&gt;reg add HKLM\SYSTEM\Setup\LabConfig /v BypassRAMCheck /t REG_DWORD /d 1&lt;/Path&gt;
                &lt;/RunSynchronousCommand&gt;
            &lt;/RunSynchronous&gt;
        &lt;/component&gt;
        &lt;component name="Microsoft-Windows-International-Core-WinPE" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;
            &lt;SetupUILanguage&gt;
                &lt;UILanguage&gt;en-US&lt;/UILanguage&gt;
            &lt;/SetupUILanguage&gt;
            &lt;InputLocale&gt;en-US&lt;/InputLocale&gt;
            &lt;SystemLocale&gt;en-US&lt;/SystemLocale&gt;
            &lt;UILanguage&gt;en-US&lt;/UILanguage&gt;
            &lt;UILanguageFallback&gt;en-US&lt;/UILanguageFallback&gt;
            &lt;UserLocale&gt;en-US&lt;/UserLocale&gt;
        &lt;/component&gt;
    &lt;/settings&gt;
    &lt;settings pass="oobeSystem"&gt;
        &lt;component name="Microsoft-Windows-International-Core" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;
            &lt;InputLocale&gt;en-US&lt;/InputLocale&gt;
            &lt;SystemLocale&gt;en-US&lt;/SystemLocale&gt;
            &lt;UILanguage&gt;en-US&lt;/UILanguage&gt;
            &lt;UILanguageFallback&gt;en-US&lt;/UILanguageFallback&gt;
            &lt;UserLocale&gt;en-US&lt;/UserLocale&gt;
        &lt;/component&gt;
        &lt;component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;
            &lt;OOBE&gt;
                &lt;HideEULAPage&gt;true&lt;/HideEULAPage&gt;
                &lt;HideLocalAccountScreen&gt;true&lt;/HideLocalAccountScreen&gt;
                &lt;HideOEMRegistrationScreen&gt;true&lt;/HideOEMRegistrationScreen&gt;
                &lt;HideOnlineAccountScreens&gt;true&lt;/HideOnlineAccountScreens&gt;
                &lt;HideWirelessSetupInOOBE&gt;true&lt;/HideWirelessSetupInOOBE&gt;
                &lt;ProtectYourPC&gt;1&lt;/ProtectYourPC&gt;
            &lt;/OOBE&gt;
            &lt;TimeZone&gt;UTC&lt;/TimeZone&gt;
            &lt;UserAccounts&gt;
                &lt;AdministratorPassword&gt;
                    &lt;Value&gt;!XxXxXxXxXxXxXxXxX!&lt;/Value&gt;
                    &lt;PlainText&gt;true&lt;/PlainText&gt;
                &lt;/AdministratorPassword&gt;
                &lt;LocalAccounts&gt;
                    &lt;LocalAccount wcm:action="add"&gt;
                        &lt;Name&gt;azadmin&lt;/Name&gt;
                        &lt;DisplayName&gt;&lt;/DisplayName&gt;
                        &lt;Group&gt;Administrators&lt;/Group&gt;
                        &lt;Password&gt;
                            &lt;Value&gt;!XxXxXxXxXxXxXxXxX!&lt;/Value&gt;
                            &lt;PlainText&gt;true&lt;/PlainText&gt;
                        &lt;/Password&gt;
                    &lt;/LocalAccount&gt;
                    &lt;LocalAccount wcm:action="add"&gt;
                        &lt;Name&gt;User&lt;/Name&gt;
                        &lt;DisplayName&gt;&lt;/DisplayName&gt;
                        &lt;Group&gt;Users&lt;/Group&gt;
                        &lt;Password&gt;
                            &lt;Value&gt;&lt;/Value&gt;
                            &lt;PlainText&gt;true&lt;/PlainText&gt;
                        &lt;/Password&gt;
                    &lt;/LocalAccount&gt;
                &lt;/LocalAccounts&gt;
            &lt;/UserAccounts&gt;
            &lt;AutoLogon&gt;
                &lt;Username&gt;Administrator&lt;/Username&gt;
                &lt;Enabled&gt;true&lt;/Enabled&gt;
                &lt;LogonCount&gt;1&lt;/LogonCount&gt;
                &lt;Password&gt;
                    &lt;Value&gt;!XxXxXxXxXxXxXxXxX!&lt;/Value&gt;
                    &lt;PlainText&gt;true&lt;/PlainText&gt;
                &lt;/Password&gt;
            &lt;/AutoLogon&gt;
            &lt;FirstLogonCommands&gt;
                &lt;SynchronousCommand wcm:action="add"&gt;
                    &lt;Order&gt;1&lt;/Order&gt;
                    &lt;CommandLine&gt;PowerShell "Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Force"&lt;/CommandLine&gt;
                    &lt;Description&gt;Change the default PowerShell Execution Policy from Restricted to RemoteSigned&lt;/Description&gt;
                &lt;/SynchronousCommand&gt;
                &lt;SynchronousCommand wcm:action="add"&gt;
                    &lt;Order&gt;2&lt;/Order&gt;
                    &lt;CommandLine&gt;C:\Windows\SysWOW64\cmd /c PowerShell -Command "Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Force"&lt;/CommandLine&gt;
                    &lt;Description&gt;Change the default PowerShell (32-bit) Execution Policy from Restricted to RemoteSigned&lt;/Description&gt;
                &lt;/SynchronousCommand&gt;
                &lt;SynchronousCommand wcm:action="add"&gt;
                    &lt;Order&gt;3&lt;/Order&gt;
                    &lt;CommandLine&gt;PowerShell -WindowStyle Maximized -Command "Get-PSDrive -PSProvider FileSystem | ForEach-Object {$p = Join-Path $_.Root provision-autounattend.ps1; if (Test-Path $p) {&amp;amp;$p}}"&lt;/CommandLine&gt;
                &lt;/SynchronousCommand&gt;
                &lt;SynchronousCommand wcm:action="add"&gt;
               &lt;CommandLine&gt;powershell -ExecutionPolicy Bypass -File a:\install-vmwtools.ps1&lt;/CommandLine&gt;
               &lt;Order&gt;4&lt;/Order&gt;
               &lt;Description&gt;Install VMware Tools&lt;/Description&gt;
            &lt;/SynchronousCommand&gt;
            &lt;SynchronousCommand wcm:action="add"&gt;
               &lt;CommandLine&gt;%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -File a:\windows-init.ps1&lt;/CommandLine&gt;
               &lt;Order&gt;5&lt;/Order&gt;
               &lt;Description&gt;Initial Configuration&lt;/Description&gt;
            &lt;/SynchronousCommand&gt;
            &lt;SynchronousCommand wcm:action="add"&gt;
               &lt;Description&gt;Sysprep Generalize&lt;/Description&gt;
               &lt;CommandLine&gt;cmd /c C:\Windows\System32\Sysprep\Sysprep.exe /generalize /oobe /shutdown&lt;/CommandLine&gt;
               &lt;Order&gt;5&lt;/Order&gt;
            &lt;/SynchronousCommand&gt;
            &lt;/FirstLogonCommands&gt;
        &lt;/component&gt;
    &lt;/settings&gt;
    &lt;settings pass="offlineServicing"&gt;
        &lt;component name="Microsoft-Windows-LUA-Settings" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;
            &lt;EnableLUA&gt;false&lt;/EnableLUA&gt;
        &lt;/component&gt;
    &lt;/settings&gt;
&lt;/unattend&gt;Running the Windows 11-related Packer IaC snippet should create a vSphere VM that is converted to a template: azadmin@tacg-cicd:~$ packer build -force -var-file=".\win11-std.auto.pkrvars.hcl" win11-std-config.pkr.hcl
vsphere-iso.win11_iso: output will be in this color.

==&gt; vsphere-iso.win11_iso: Creating virtual machine...
==&gt; vsphere-iso.win11_iso: Customizing hardware...
==&gt; vsphere-iso.win11_iso: Adding virtual machine flags...
==&gt; vsphere-iso.win11_iso: Mounting ISO images...
==&gt; vsphere-iso.win11_iso: Adding configuration parameters...
==&gt; vsphere-iso.win11_iso: Creating floppy disk...
==&gt; vsphere-iso.win11_iso: Copying files flatly from floppy_files
==&gt; vsphere-iso.win11_iso: Copying directory: ./setup/
==&gt; vsphere-iso.win11_iso: Adding file: setup\add-domain.ps1
==&gt; vsphere-iso.win11_iso: Adding file: setup\autounattend.xml
==&gt; vsphere-iso.win11_iso: Adding file: setup\autounattend.xml-orig
==&gt; vsphere-iso.win11_iso: Adding file: setup\disable-autolog.ps1
==&gt; vsphere-iso.win11_iso: Adding file: setup\disable-network-discovery.cmd
==&gt; vsphere-iso.win11_iso: Adding file: setup\disable-ssh.ps1
==&gt; vsphere-iso.win11_iso: Adding file: setup\enable-rdp.cmd
==&gt; vsphere-iso.win11_iso: Adding file: setup\enable-ssh.ps1
==&gt; vsphere-iso.win11_iso: Adding file: setup\enable-winrm.ps1
==&gt; vsphere-iso.win11_iso: Adding file: setup\install-vmwtools.ps1
==&gt; vsphere-iso.win11_iso: Adding file: setup\install_windowsfeatures.ps1
==&gt; vsphere-iso.win11_iso: Adding file: setup\windows-init.ps1
==&gt; vsphere-iso.win11_iso: Done copying files from floppy_files
==&gt; vsphere-iso.win11_iso: Collecting paths from floppy_dirs
==&gt; vsphere-iso.win11_iso: Resulting paths from floppy_dirs : []
==&gt; vsphere-iso.win11_iso: Done copying paths from floppy_dirs
==&gt; vsphere-iso.win11_iso: Copying files from floppy_content
==&gt; vsphere-iso.win11_iso: Done copying files from floppy_content
==&gt; vsphere-iso.win11_iso: Uploading floppy image...
==&gt; vsphere-iso.win11_iso: Adding generated floppy image...
==&gt; vsphere-iso.win11_iso: Setting boot order...
==&gt; vsphere-iso.win11_iso: Powering on virtual machine...
==&gt; vsphere-iso.win11_iso: Waiting 3s for boot...
==&gt; vsphere-iso.win11_iso: Typing boot command...
==&gt; vsphere-iso.win11_iso: Waiting for IP...
==&gt; vsphere-iso.win11_iso: IP address: 10.10.11.186
==&gt; vsphere-iso.win11_iso: Using WinRM communicator to connect: 10.10.11.186
==&gt; vsphere-iso.win11_iso: Waiting for WinRM to become available...
==&gt; vsphere-iso.win11_iso: WinRM connected.
==&gt; vsphere-iso.win11_iso: Connected to WinRM!
==&gt; vsphere-iso.win11_iso: Restarting Machine
==&gt; vsphere-iso.win11_iso: Waiting for machine to restart...
==&gt; vsphere-iso.win11_iso: A system shutdown is in progress.(1115)
==&gt; vsphere-iso.win11_iso: A system shutdown is in progress.(1115)
==&gt; vsphere-iso.win11_iso: restarted.
==&gt; vsphere-iso.win11_iso: TACGTAC-8KAGCAS restarted.
==&gt; vsphere-iso.win11_iso: Machine successfully restarted, moving on
==&gt; vsphere-iso.win11_iso: Provisioning with Powershell...
==&gt; vsphere-iso.win11_iso: Provisioning with powershell script: ./setup/windows-init.ps1
==&gt; vsphere-iso.win11_iso: Configuring important Windows Features...
==&gt; vsphere-iso.win11_iso: Setting the network connection profiles to Private...
==&gt; vsphere-iso.win11_iso: Setting the Windows Remote Management configuration...
==&gt; vsphere-iso.win11_iso: WinRM service is already running on this machine.
==&gt; vsphere-iso.win11_iso: WinRM is already set up for remote management on this computer.
==&gt; vsphere-iso.win11_iso: Service
==&gt; vsphere-iso.win11_iso:     RootSDDL = O:NSG:BAD:P(A;;GA;;;BA)(A;;GR;;;IU)S:P(AU;FA;GA;;;WD)(AU;SA;GXGW;;;WD)
==&gt; vsphere-iso.win11_iso:     MaxConcurrentOperations = 4294967295
==&gt; vsphere-iso.win11_iso:     MaxConcurrentOperationsPerUser = 1500
==&gt; vsphere-iso.win11_iso:     EnumerationTimeoutms = 240000
==&gt; vsphere-iso.win11_iso:     MaxConnections = 300
==&gt; vsphere-iso.win11_iso:     MaxPacketRetrievalTimeSeconds = 120
==&gt; vsphere-iso.win11_iso:     AllowUnencrypted = true
==&gt; vsphere-iso.win11_iso:     Auth
==&gt; vsphere-iso.win11_iso:         Basic = true
==&gt; vsphere-iso.win11_iso:         Kerberos = true
==&gt; vsphere-iso.win11_iso:         Negotiate = true
==&gt; vsphere-iso.win11_iso:         Certificate = false
==&gt; vsphere-iso.win11_iso:         CredSSP = false
==&gt; vsphere-iso.win11_iso:         CbtHardeningLevel = Relaxed
==&gt; vsphere-iso.win11_iso:     DefaultPorts
==&gt; vsphere-iso.win11_iso:         HTTP = 5985
==&gt; vsphere-iso.win11_iso:         HTTPS = 5986
==&gt; vsphere-iso.win11_iso:     IPv4Filter = *
==&gt; vsphere-iso.win11_iso:     IPv6Filter = *
==&gt; vsphere-iso.win11_iso:     EnableCompatibilityHttpListener = false
==&gt; vsphere-iso.win11_iso:     EnableCompatibilityHttpsListener = false
==&gt; vsphere-iso.win11_iso:     CertificateThumbprint
==&gt; vsphere-iso.win11_iso:     AllowRemoteAccess = true
==&gt; vsphere-iso.win11_iso:
==&gt; vsphere-iso.win11_iso: Auth
==&gt; vsphere-iso.win11_iso:     Basic = true
==&gt; vsphere-iso.win11_iso:     Kerberos = true
==&gt; vsphere-iso.win11_iso:     Negotiate = true
==&gt; vsphere-iso.win11_iso:     Certificate = false
==&gt; vsphere-iso.win11_iso:     CredSSP = false
==&gt; vsphere-iso.win11_iso:     CbtHardeningLevel = Relaxed
==&gt; vsphere-iso.win11_iso:
==&gt; vsphere-iso.win11_iso: Allowing Windows Remote Management in the Windows Firewall...
==&gt; vsphere-iso.win11_iso:
==&gt; vsphere-iso.win11_iso: No rules match the specified criteria.
==&gt; vsphere-iso.win11_iso:
==&gt; vsphere-iso.win11_iso:
==&gt; vsphere-iso.win11_iso: Updated 2 rule(s).
==&gt; vsphere-iso.win11_iso: Ok.
==&gt; vsphere-iso.win11_iso:
==&gt; vsphere-iso.win11_iso: Running shutdown command...
==&gt; vsphere-iso.win11_iso: Deleting floppy drives...
==&gt; vsphere-iso.win11_iso: Deleting floppy image...
==&gt; vsphere-iso.win11_iso: Ejecting CD-ROM media...
==&gt; vsphere-iso.win11_iso: Removing CD-ROM devices...
==&gt; vsphere-iso.win11_iso: Closing sessions...
Build 'vsphere-iso.win11_iso' finished after 20 minutes 40 seconds.
...
==&gt; Wait completed after 20 minutes 40 seconds

==&gt; Builds finished. The artifacts of successful builds are:
--&gt; vsphere-iso.win11_iso: win11-p01
azadmin@tacg-cicd:~$  Creating Master Images using Packer on Azure with Packer, Ansible, and ChocolateyUsing Packer with a modern package manager like Chocolatey makes it easier to create master images that include all necessary software components. With Packer, you define the basic settings of the master image based on your requirements for the underlying operating system. Next, with Chocolatey, you can then install all the necessary software components after configuring the operating system. Chocolatey´s repository currently lists over 10,000 software packages. Finally, Ansible enables installing software components that are not available in Chocolatey or that are not supported for deployment via Chocolatey. Since we are referring to DaaS and CVAD, Packer can also perform the appropriate configuration and installation of the Virtual Delivery Agent (VDA) and other Citrix components, resulting in a fully configured master image ready for further use.      Note You can find the full guide on Citrix TechZone: https://community.citrix.com/tech-zone/automation/masterimage-iac-packer-ansible-chocolatey-github This example demonstrates creating a much more sophisticated Windows 11-based Master Image with Packer, Ansible, and Chocolatey on Azure. For a detailed overview of the builder, please look at the plugin´s documentation.  PrerequisitesBefore starting Packer, we need to determine some Azure-related information, like the SKUs to be used: # Source Image
  os_type         = "Windows"
  image_publisher = var.azure_imgpublisher
  image_offer     = var.azure_imgoffer
  image_sku       = var.azure_imgsku
  image_version   = var.azure_imgversion ### Get available Windows Desktop Publishers
PS C:\Users\packer&gt; Get-AzVMImagePublisher -Location "austriaeast" | Where-Object { $_.PublisherName -eq "MicrosoftWindowsDesktop" } | Select PublisherName

PublisherName
-------------
MicrosoftWindowsDesktop

PS C:\Users\packer&gt; 

### Get available Win11-Image Offers
PS C:\Users\packer&gt; Get-AzVMImageOffer -Location "austriaeast" -PublisherName "MicrosoftWindowsDesktop" | Select Offer

Offer
-----
office-365
Windows-10
windows-11
windows-7
windows-ent-cpc
windows10preview
windows11preview
windows11preview-arm64

PS C:\Users\packer&gt;
### Get Windows 11 Image SKU with Office 365 installed
PS C:\Users\packer&gt; Get-AzVMImageSku -Location "austriaeast" -PublisherName "MicrosoftWindowsDesktop" -Offer "office-365" | Select Skus

Skus
----
rs5-evd-o365pp-g2
win10-22h2-avd-m365
win10-22h2-avd-m365-g2
win11-22h2-avd-m365
win11-23h2-avd-m365
win11-24h2-avd-m365
win11-25h2-avd-m365

PS C:\Users\packer&gt;
### Get final Image version ID
PS C:\Users\packer&gt; Get-AzVMImage -Location "austriaeast" -PublisherName "MicrosoftWindowsDesktop" -Offer "office-365" -Sku "win11-25h2-avd-m365" | Select Version

Version
-------
26200.6584.250915
26200.6899.251014
26200.7171.251111

PS C:\Users\packer&gt;Determining the software packages to be installed using ChocolateyThe next step is to determine which software packages you want to use – Chocolatey currently offers more than 10,000 packages. Note You can find an actual list at https://community.chocolatey.org/packages.  Important Please check whether a package is supported in your environment before deploying it. Each package has a lot of information tied to – the most important is if tests have failed. You can then look for a valid, error-free package in the package history. Example package data – Google Chrome:   In our environment, we, of course, only use problem-free packages. You can most easily check the last available problem-free versions using a PowerShell script to determine these packages: choco search --exact googlechrome -a --approved-only --not-broken --limit-outputPS C:\TMM&gt; choco search --exact googlechrome -a --approved-only --not-broken --limit-output
GoogleChrome|143.0.7499.41
GoogleChrome|142.0.7444.176
GoogleChrome|142.0.7444.163
GoogleChrome|142.0.7444.135
GoogleChrome|142.0.7444.60
GoogleChrome|142.0.7444.52
GoogleChrome|141.0.7390.77
GoogleChrome|141.0.7390.66
GoogleChrome|141.0.7390.55
GoogleChrome|141.0.7390.37
...Choosing the software packages to be installed using ChocolateyChocolatey needs a packages.config file to know which packages should be installed during the creation of the master image. Example of a packages.config file: &lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;packages&gt;
  &lt;package id="adobereader" version="2025.1.20577" /&gt;
  &lt;package id="googlechrome" version="143.0.7499.41" /&gt;
  &lt;package id="git.install" version="2.52.0" /&gt;
  &lt;package id="sysinternals" version="2025.11.17" /&gt;
  &lt;package id="winscp.install" version="6.5.5" /&gt;
  &lt;package id="teamviewer" version="15.72.6" /&gt;
  &lt;package id="dropbox" version="238.4.6075" /&gt;
&lt;/packages&gt;Based on the package list seen earlier, you can manually create a suitable package.config file, or you can use any other mechanism. For demonstration purposes, we created a self-service web application that lets you choose packages via a web frontend. The table with the packages and their versions is dynamically created out of the JSON file mentioned above:   Configuring Packer to create the master image and deploy the packagesLet´s look at a valid example of an HCL to create a Windows 11-based master image on Azure, deploy the software packages using Chocolatey, and upload the final image to an Azure Image Gallery – from there, you can, for example, create an image definition and image version in Citrix Daas to deploy a Machine catalog. win11-azure.packer.hcl: # Defining all used variables
variable "azure_clientid" {
  type        = string
  description = "azure Service Principal App ID"
  sensitive   = true
}

variable "azure_clientsecret" {
  type        = string
  description = "azure Service Principal Secret"
  sensitive   = true
}

variable "azure_subscriptionid" {
  type        = string
  description = "azure Subscription ID"
  sensitive   = true
}

variable "azure_tenantid" {
  type        = string
  description = "azure Tenant ID"
  sensitive   = true
}

...

variable "vda_location" {
  type = string
}

variable "script_installvda" {
  type    = string
  default = "install-vda.ps1"
}

variable "script_optimizer" {
  type    = string
  default = "run-optimizer.ps1"
}

variable "script_cleanup" {
  type    = string
  default = "run-cleanup.ps1"
}

variable "location_setup" {
  type    = string
  default = "c:\\setup"
}

# Defining the used Packer Plugin(s)
packer {
  required_plugins {
    azure = {
      source  = "github.com/hashicorp/azure"
      version = "&gt;= 2.5.0"
    }
  }
}

# Defining the source(s) block(s)
source "azure-arm" "W11MIWithSWPackagesWithVDA" {
  # Tagging the Azure VM
  azure_tags = {
    environment        = var.azure_tag_environment,
    environment-entity = var.azure_tag_environment_entity,
    usage              = var.azure_tag_usage
  }

  # WinRM Communicator
  communicator   = "winrm"
  winrm_use_ssl  = true
  winrm_insecure = false
  winrm_timeout  = "5m"
  winrm_username = "packer"

  # Service Principal Authentication
  client_id       = var.azure_clientid
  client_secret   = var.azure_clientsecret
  subscription_id = var.azure_subscriptionid
  tenant_id       = var.azure_tenantid

  # Source Image
  os_type         = "Windows"
  image_publisher = var.azure_imgpublisher
  image_offer     = var.azure_imgoffer
  image_sku       = var.azure_imgsku
  image_version   = var.azure_imgversion

  # Destination Image - we want to upload the artifact directly into the Azure Image Gallery, so no creation of a stand-alone image is needed
  # managed_image_resource_group_name = var.azure_RG
  # managed_image_name                = var.azure_ManagedImgName

  # Store created Image in Shared Image Gallery 
  shared_image_gallery_destination {
    resource_group       = var.azure_rg
    gallery_name         = var.azure_sig_name
    image_name           = var.azure_sig_imgname
    image_version        = var.azure_sig_imgversion
    replication_regions  = ["austriaeast"]
    storage_account_type = "Standard_LRS"
  }

  # Packer Computing Resources
  build_resource_group_name = var.azure_temprg
  vm_size                   = var.azure_vmsize

}

# Defining the building blocks
build {
  source "azure-arm.W11MIWithSWPackagesWithVDA" {}

  # Install Chocolatey: https://chocolatey.org/install#individual
  provisioner "powershell" {
    inline = ["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'))"]
  }

  # Install Chocolatey packages
  provisioner "file" {
    source      = "./scripts/SWPackagesToInstall.config"
    destination = "${var.location_setup}/scripts/SWPackagesToInstall.config"
  }

  provisioner "powershell" {
    inline = ["choco install --ignore-hash --ignore-checksums --confirm ${var.location_setup}/scripts/SWPackagesToInstall.config"]
    # See https://docs.chocolatey.org/en-us/choco/commands/install#exit-codes
    valid_exit_codes = [0, 3, 1641, 3010]
  }

  provisioner "windows-restart" {}

  provisioner "powershell" {
    inline = ["New-Item -Path ${var.location_setup} -Force -ItemType \"directory\" | Out-Null"]
  }

  provisioner "file" {
    destination = "${var.location_setup}/${var.script_installvda}"
    source      = "./scripts/${var.script_installvda}"
  }

  provisioner "file" {
    destination = "${var.location_setup}/${var.script_optimizer}"
    source      = "./scripts/${var.script_optimizer}"
  }

  provisioner "powershell" {
    inline = ["${var.location_setup}/${var.script_installvda} -VdaSetupDownloadUri ${var.vda_location}"]
  }

  provisioner "windows-restart" {
    restart_check_command = "powershell -command \"&amp; {Write-Host 'Script: computer restarted.'}\""
    restart_timeout       = "30m"
  }

  provisioner "powershell" {
    inline = ["${var.location_setup}/${var.script_installvda} -VdaSetupDownloadUri ${var.vda_location}"]
  }

  provisioner "powershell" {
    inline = ["$assemblyPath = \"C:\\Program Files (x86)\\Citrix\\ICA Client\\wfica32.exe\"", "$ngen64 = \"C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\ngen.exe\"", "$ngen32 = \"C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319\\ngen.exe\"", "Write-Host \"Running 64-bit Ngen...\"", "&amp; $ngen64 executequeueditems", "Write-Host \"Running 32-bit Ngen...\"", "&amp; $ngen32 executequeueditems"]
  }

  provisioner "powershell" {
    inline = ["${var.location_setup}/${var.script_optimizer} -OptimizerDownloadUri ${var.optimizer_location} -Template ${var.optimizer_template}"]
  }

  # Generalize image using Sysprep
  # See https://www.packer.io/docs/builders/azure/arm#windows
  # See https://docs.microsoft.com/en-us/azure/virtual-machines/windows/build-image-with-packer#define-packer-template
  provisioner "powershell" {
    inline = [
      "while ((Get-Service RdAgent).Status -ne 'Running') { Start-Sleep -s 5 }",
      "while ((Get-Service WindowsazureGuestAgent).Status -ne 'Running') { Start-Sleep -s 5 }",
      "&amp; $env:SystemRoot\\System32\\Sysprep\\Sysprep.exe /oobe /generalize /quiet /quit /mode:vm",
      "while ($true) { $imageState = Get-ItemProperty HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State | Select ImageState; if($imageState.ImageState -ne 'IMAGE_STATE_GENERALIZE_RESEAL_TO_OOBE') { Write-Output $imageState.ImageState; Start-Sleep -s 10  } else { break } }"
    ]
  }
}The variables are defined in a key-value format - example: win11-azure.auto.pkrvars.hcl: azure_clientid       = &lt;sensitive&gt;
azure_clientsecret   = &lt;sensitive&gt;
azure_subscriptionid = &lt;sensitive&gt;
azure_tenantid       = &lt;sensitive&gt;
azure_rg             = "TMM-&lt;sensitive&gt;"
azure_temprg         = "TMM-&lt;sensitive&gt;"
azure_imgpublisher   = "MicrosoftWindowsDesktop"
azure_imgoffer       = "windows-11"
azure_imgsku         = "win11-25h2-pro"
azure_imgversion     = "26200.7462.251207"
...
vda_location         = "https://&lt;sensitive&gt;.blob.core.windows.net/ctxsw/VDAWorkstationSetup_2507.exe"
script_installvda    = "install-vda.ps1"
script_optimizer     = "run-optimizer.ps1"
script_cleanup       = "run-cleanup.ps1"
location_setup       = "c:\\ttemp"
optimizer_location   = "https://&lt;sensitive&gt;.blob.core.windows.net/ctxsw/CitrixOptimizerTool.zip"
optimizer_template   = "Citrix_Windows_11_2009.xml"To start the creation process manually, you can use the well-known Packer command: azadmin@az-weur-devops-automation:/etc/terraform-tmm/PACKER/_AZURE$ packer build -on-error=cleanup -force -var-file="win11-azure.auto.pkrvars.hcl" win11-azure.pkr.hclPacker writes out all relevant information during the building process. ==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Running builder ...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Creating Azure Resource Manager (ARM) client ...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: ARM Client successfully created
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Getting source image id for the deployment ...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  -&gt; SourceImageName: '/subscriptions/&lt;sensitive&gt;/providers/Microsoft.Compute/locations/austriaeast/publishers/MicrosoftWindowsDesktop/ArtifactTypes/vmimage/offers/windows-11/skus/win11-25h2-pro/versions/26200.7462.251207'

...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Getting the VM's IP address ...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  -&gt; ResourceGroupName   : 'TMM-&lt;sensitive&gt;'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  -&gt; PublicIPAddressName : 'pkripgf6zyyvkul'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  -&gt; NicName             : 'pkrnigf6zyyvkul'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  -&gt; Network Connection  : 'PublicEndpoint'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  -&gt; IP Address          : '4.210.182.179'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Waiting for WinRM to become available...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: WinRM connected.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Connected to WinRM!
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Provisioning with Powershell...
...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Forcing web requests to allow TLS v1.2 (Required for requests to Chocolatey.org)
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Getting latest version of the Chocolatey package for download.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Not using proxy.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Getting Chocolatey from https://community.chocolatey.org/api/v2/package/chocolatey/2.6.0.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Downloading https://community.chocolatey.org/api/v2/package/chocolatey/2.6.0 to C:\Users\packer\AppData\Local\Temp\chocolatey\chocoInstall\chocolatey.zip
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Not using proxy.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Extracting C:\Users\packer\AppData\Local\Temp\chocolatey\chocoInstall\chocolatey.zip to C:\Users\packer\AppData\Local\Temp\chocolatey\chocoInstall
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Installing Chocolatey on the local machine
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Creating ChocolateyInstall as an environment variable (targeting 'Machine')
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:   Setting ChocolateyInstall to 'C:\ProgramData\chocolatey'
...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: c:\ttemp/scripts/SWPackagesToInstall.config
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: By installing, you accept licenses for the packages.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Installing the following packages:
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: adobereader
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: googlechrome
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: git.install
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: sysinternals
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: winscp.install
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: teamviewer
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: dropbox
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Downloading package from source 'https://community.chocolatey.org/api/v2/'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Progress: Downloading chocolatey-compatibility.extension 1.0.0... 100%

...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Downloading package from source 'https://community.chocolatey.org/api/v2/'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Progress: Downloading GoogleChrome 143.0.7499.41... 100%
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: GoogleChrome v143.0.7499.41[Approved]
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: GoogleChrome package files install completed. Performing other installation steps.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Downloading googlechrome 64 bit
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:   from 'https://dl.google.com/dl/chrome/install/googlechromestandaloneenterprise64.msi'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Download of googlechromestandaloneenterprise64.msi (-1 B) completed.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: WARNING: Ignoring checksums due to feature checksumFiles turned off or option --ignore-checksums set.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Installing googlechrome...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: googlechrome has been installed.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:   GoogleChrome may be able to be automatically uninstalled.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  The install of GoogleChrome was successful.

...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Chocolatey installed 13/13 packages.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  See the log for details (C:\ProgramData\chocolatey\logs\chocolatey.log).
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Installed:
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  - chocolatey-compatibility.extension v1.0.0
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  - chocolatey-core.extension v1.4.0
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  - chocolatey-dotnetfx.extension v1.0.1
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  - chocolatey-visualstudio.extension v1.13.0
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  - chocolatey-windowsupdate.extension v1.0.5
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  - adobereader v2025-1-20577
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  - filezilla v3.69.5
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  - GoogleChrome v143.0.7499.41
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  - git.install v2.52.0
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  - sysinternals v2025.11.17
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  - winscp.install v6.5.5
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  - teamviewer v15.72.6
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  - dropbox v238.4.6075
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Restarting Machine
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Waiting for machine to restart...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: A system shutdown is in progress.(1115)
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: pkrvmgf6zyyvkul restarted.
...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Uploading ./scripts/install-vda.ps1 =&gt; c:\ttemp/install-vda.ps1
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Uploading ./scripts/run-optimizer.ps1 =&gt; c:\ttemp/run-optimizer.ps1
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Provisioning with Powershell...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Provisioning with powershell script: C:\Users\Gerhard\AppData\Local\Temp\powershell-provisioner3550365223
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 01:55:50 - Initiating Ctrix VDA installation.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 01:55:50 - Adding firewall allow rules for ports 80, 443, 1494, 2598, 8008
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 01:55:53 - Finished adding firewall allow rules for ports 80, 443, 1494, 2598, 8008
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 01:55:53 - Downloading VDASetup file from blob storage
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 01:56:05 - Executing: C:\Users\packer\AppData\Local\Temp\VDASetup.exe, Components: VDA
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 01:56:05 - Parameters: /quiet /noreboot /masterimage /enable_real_time_transport /enable_hdx_ports /components vda /includeadditional "Citrix MCS IODriver","Citrix VDA Upgrade Agent"
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 01:56:40 - XenDesktopVDA installation started
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 01:56:40 - Waiting few seconds to make sure the process extracts and starts
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 01:58:40 - Checking if XenDesktopVdaSetup process is running
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 01:58:40 - Installation process still running

...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 02:00:40 - Installation process completed
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 02:00:40 - GctRegistration already enabled
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 02:00:40 - BrokerAgent found and it is running.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 02:00:40 - Installation of XenDesktopVDA completed with success
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Restarting Machine
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Waiting for machine to restart...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: A system shutdown is in progress.(1115)
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: computer restarted.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: pkrvmgf6zyyvkul restarted.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Machine successfully restarted, moving on

...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Provisioning with Powershell...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Provisioning with powershell script: C:\Users\Gerhard\AppData\Local\Temp\powershell-provisioner2019159959
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 02:20:55 - Downloading Citrix Optimizer from blob storage
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 02:20:55 - Executing: C:\Users\packer\AppData\Local\Temp\CitrixOptimizer\CtxOptimizerEngine.ps1, Template: Citrix_Windows_11_2009.xml
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: ------------------------------
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: | Citrix Optimization Engine |
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: | Version 2.9                |
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: ------------------------------
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Running in execute mode
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Creating Logs folder 2025-12-12_14-20-55
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Starting session log
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Checking permissions
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Processing definition file C:\Users\packer\AppData\Local\Temp\CitrixOptimizer\Templates\Citrix_Windows_11_2009.xml

...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 02:24:38 - Optimization script completed
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Provisioning with Powershell...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: IMAGE_STATE_COMPLETE

...

==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Querying the machine's properties ...

...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Powering off machine ...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Publishing to Shared Image Gallery ...

...

==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  -&gt; Shared Gallery Image Version ID : '/subscriptions/&lt;sensitive&gt;/resourceGroups/TMM-&lt;sensitive&gt;/providers/Microsoft.Compute/galleries/&lt;sensitive&gt;/images/&lt;sensitive&gt;/versions/1.0.0'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Deleting Virtual Machine deployment and its attached resources...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Deleted -&gt; Microsoft.Compute/virtualMachines : 'pkrvmgf6zyyvkul'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Deleted -&gt; Microsoft.Network/networkInterfaces : 'pkrnigf6zyyvkul'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Deleted -&gt; Microsoft.Network/virtualNetworks : 'pkrvngf6zyyvkul'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Deleted -&gt; Microsoft.Network/publicIPAddresses : 'pkripgf6zyyvkul'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Deleted -&gt; Microsoft.Network/networkSecurityGroups : 'pkrsggf6zyyvkul'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Deleted -&gt; Microsoft.Compute/disks : '/subscriptions/&lt;sensitive&gt;/resourceGroups/TMM-sensitive/providers/Microsoft.Compute/disks/pkrosgf6zyyvkul'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Removing the created Deployment object: 'pkrdpgf6zyyvkul'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Deleting KeyVault created during build
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Deleted -&gt; Microsoft.KeyVault/vaults : 'pkrkvgf6zyyvkul'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Removing the created Deployment object: 'kvpkrdpgf6zyyvkul'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: The resource group was not created by Packer, not deleting ...
Build 'azure-arm.W11MIWithSWPackagesWithVDA' finished after 54 minutes 36 seconds.

==&gt; Wait completed after 54 minutes 36 seconds

==&gt; Builds finished. The artifacts of successful builds are:
--&gt; azure-arm.W11MIWithSWPackagesWithVDA: Azure.ResourceManagement.VMImage:

OSType: Windows
ManagedImageSharedImageGalleryId: /subscriptions/&lt;sensitive&gt;/resourceGroups/TMM-&lt;sensitive&gt;/providers/Microsoft.Compute/galleries/&lt;sensitive&gt;/images/&lt;sensitive&gt;/versions/1.0.0
SharedImageGalleryResourceGroup: TMM-&lt;sensitive&gt;
SharedImageGalleryName: &lt;sensitive&gt;
SharedImageGalleryImageName: &lt;sensitive&gt;
SharedImageGalleryImageVersion: 1.0.0
SharedImageGalleryReplicatedRegions: austriaeastThe creation was completed successfully. We can now use this image version, for example, to generate the corresponding image on Citrix DaaS to create a Machine Catalog. #### Creating the Image Definition based on the Azure shared gallery image definition

resource "citrix_image_definition" "CreateAzureImageDefinition" {
  name                     = var.CC-Azure-ImgDef-Name
  description              = var.CC-Azure-ImgDef-Description
  os_type                  = "Windows"
  session_support          = "SingleSession"
  hypervisor               = citrix_azure_hypervisor.CreateAzureHostingConnection.id
  hypervisor_resource_pool = citrix_azure_hypervisor_resource_pool.CreateAzureHostingConnectionPool.id

  azure_image_definition = {
    resource_group     = var.TACG-TMM-ResourceGroup-Name
    use_image_gallery  = true
    image_gallery_name = var.CC-Azure-SIG-Name
  }
}

#### Creating the Image version based on the Azure shared gallery image version
resource "citrix_image_version" "CreateAzureImageVersion" {
  depends_on               = [citrix_image_definition.CreateAzureImageDefinition]
  image_definition         = citrix_image_definition.CreateAzureImageDefinition.id
  hypervisor               = citrix_azure_hypervisor.CreateAzureHostingConnection.id
  hypervisor_resource_pool = citrix_azure_hypervisor_resource_pool.CreateAzureHostingConnectionPool.id
  description              = var.CC-Azure-ImgVer-Description

  azure_image_specs = {
    service_offering = var.CC-Azure-Img-ServiceOffering
    storage_type     = var.CC-Azure-Img-StorageType
    resource_group   = var.TACG-TMM-ResourceGroup-Name
    gallery_image = {
      gallery    = var.CC-Azure-SIG-Name
      definition = var.CC-Azure-SIGDefinition-Name
      version    = var.CC-Azure-SIGDefinition-Version
    }
    machine_profile = {
      machine_profile_resource_group = var.CC-Azure-ImgVer-MachineProfile-RG
      machine_profile_vm_name        = var.CC-Azure-ImgVer-MachineProfile-VMName
    }
  }
}

resource "null_resource" "WriteProgress2" {
  depends_on = [citrix_image_version.CreateAzureImageVersion]
  provisioner "local-exec" {
    command = "echo The Image definition and Image version were successfully created..."
  }
}

#### Creating the Machine Catalog based on the Image version just created
resource "citrix_machine_catalog" "CreateAzureMCSCatalog" {
  depends_on        = [citrix_image_version.CreateAzureImageVersion]
  name              = var.CC-Azure-MC-Name
  description       = var.CC-Azure-MC-Description
  allocation_type   = var.CC-Azure-MC-AllocationType
  session_support   = var.CC-Azure-MC-SessionType
  provisioning_type = "MCS"
  #zone                = data.local_file.LoadZoneID.content
  zone = data.citrix_zone.GetTFAzureZoneID.id
  #scopes              = "${var.CC-GetScopeToBeUsed-Names}"

  provisioning_scheme = {
    hypervisor               = citrix_azure_hypervisor.CreateAzureHostingConnection.id
    hypervisor_resource_pool = citrix_azure_hypervisor_resource_pool.CreateAzureHostingConnectionPool.id
    identity_type            = "${var.CC-Azure-MC-IDPType}"

    machine_domain_identity = {
      domain                   = "${var.CC-Azure-MC-Domain}"
      domain_ou                = "${var.CC-Azure-MC-DomainOU}"
      service_account          = "${var.CC-Azure-MC-DomainAdmin-Username-UPN}"
      service_account_password = "${var.CC-Azure-MC-DomainAdmin-Password}"
    }

    azure_machine_config = {
      storage_type      = "Premium_LRS"
      use_managed_disks = true
      service_offering  = "${var.CC-Azure-MC-VMSize}"

      machine_profile = {
        machine_profile_resource_group = var.CC-Azure-ImgVer-MachineProfile-RG
        machine_profile_vm_name        = var.CC-Azure-ImgVer-MachineProfile-VMName
      }

      prepared_image = {
        image_definition = citrix_image_definition.CreateAzureImageDefinition.id
        image_version    = citrix_image_version.CreateAzureImageVersion.id
      }
    }
    number_of_total_machines = var.CC-Azure-MC-Machine_Count

    machine_account_creation_rules = {
      naming_scheme      = "${var.CC-Azure-MC-NamingScheme-Name}"
      naming_scheme_type = "${var.CC-Azure-MC-NamingScheme-Type}"
    }
  }
}Adding AnsibleIf Chocolatey is not suitable/capable of fulfilling all software deployment needs, we can use Ansible to fill in the gaps. As Ansible is another important IaC tool, we follow our strict IaC strategy. Using Ansible, you can also install Windows features during the creation process – let´s look at some examples: ########################################
# Ansible connection variables (WinRM)
########################################
variable "ansible_user"       { type = string  default = "Administrator" }
variable "ansible_password"   { type = string  sensitive = true }
variable "ansible_connection" { type = string  default = "winrm" }
variable "ansible_transport"  { type = string  default = "basic" } // or ntlm/credssp if needed

########################################
# Hosting Bundle variables (ASP.NET Core)
########################################
variable "hosting_bundle_url" {
  type        = string
  description = "Direct download URL for the .NET Hosting Bundle installer (ASP.NET Core on IIS)."
}
variable "hosting_bundle_args" {
  type        = string
  description = "Silent install args for Hosting Bundle."
  // Install quietly, no restart; optionally skip x86 if you host only x64 apps
  default     = "/quiet /norestart OPT_NO_X86=1"
}

// Run Ansible from the build machine against the Azure VM
  provisioner "ansible" {
    playbook_file = "ansible/windows-iis-dotnet.yml"

    // Group the host for group_vars if you want
    groups        = ["azure", "windows", "iis"]

    // Provisioner user is the Ansible connection user
    user          = var.ansible_user

    // Pass connection vars to Ansible
    extra_arguments = [
      "-e", "ansible_connection=${var.ansible_connection}",
      "-e", "ansible_user=${var.ansible_user}",
      "-e", "ansible_password=${var.ansible_password}",
      "-e", "ansible_winrm_transport=${var.ansible_transport}",
      "-e", "ansible_winrm_server_cert_validation=ignore",
      "-e", "hosting_bundle_url=${var.hosting_bundle_url}",
      "-e", "hosting_bundle_args=${var.hosting_bundle_args}"
    ]

    // For WinRM, letting the provisioner skip localhost proxy is recommended
    use_proxy = false
  }
}windows-iis-dotnet.yml ---
- name: Prepare Windows for IIS + .NET
  hosts: all
  gather_facts: no

  vars:
    # Passed from Packer via extra_arguments
    hosting_bundle_url: "{{ hosting_bundle_url | default('') }}"
    hosting_bundle_args: "{{ hosting_bundle_args | default('/quiet /norestart OPT_NO_X86=1') }}"
    site_name: "Default Web Site"
    site_path: "C:\\inetpub\\wwwroot"

  tasks:
    # 1) Install IIS base + ASP.NET 4.x features
    - name: Install IIS Web-Server with common subfeatures
      ansible.windows.win_feature:
        name:
          - Web-Server
          - Web-Static-Content
          - Web-Default-Doc
          - Web-Dir-Browsing
          - Web-Http-Errors
          - Web-Http-Logging
          - Web-Stat-Compression
          - Web-Filtering
          - Web-Basic-Auth
          - Web-Windows-Auth
          - Web-ISAPI-Ext
          - Web-ISAPI-Filter
          - Web-Asp-Net45         # ASP.NET 4.x integration with IIS
          - Web-Net-Ext45         # .NET Extensibility 4.x
          - Web-Mgmt-Console
        state: present
        include_management_tools: true
      register: iis_features
      # Ansible feature names &amp; usage:
      # https://docs.ansible.com/.../win_feature_module.html
      # (See citations in the prose above)

    - name: Reboot if IIS feature installation requires it
      ansible.windows.win_reboot:
      when: iis_features.reboot_required | default(false)

    # 2) (Optional) Ensure .NET Framework 4.8/4.8.1 is present if your apps need it
    # Use win_feature or a dedicated role/installer if you target specific KBs
    # Supported versions reference is here:
    # https://learn.microsoft.com/.../framework/install/on-windows-and-server
    # (See citations in the prose above)

    # 3) Install the .NET Hosting Bundle (ASP.NET Core runtime + IIS integration)
    - name: Fail fast if Hosting Bundle URL not provided
      ansible.builtin.fail:
        msg: "Please pass hosting_bundle_url via Packer variable."
      when: hosting_bundle_url | length == 0

    - name: Download .NET Hosting Bundle installer
      ansible.windows.win_get_url:
        url: "{{ hosting_bundle_url }}"
        dest: "C:\\Windows\\Temp\\dotnet-hosting-bundle.exe"

    - name: Install .NET Hosting Bundle silently
      ansible.windows.win_package:
        path: "C:\\Windows\\Temp\\dotnet-hosting-bundle.exe"
        arguments: "{{ hosting_bundle_args }}"
        state: present

    - name: Reboot after Hosting Bundle (pick up PATH &amp; module changes)
      ansible.windows.win_reboot:

    # 4) Configure IIS website (requires microsoft.iis collection)
    - name: Ensure IIS Default Web Site is present and started
      microsoft.iis.website:
        name: "{{ site_name }}"
        state: started
        physical_path: "{{ site_path }}"
        port: 80
        ip: "*"
        hostname: ""
      # Module docs: https://docs.ansible.com/.../microsoft/iis/website_module.htmlUse Ansible to deploy Chocolatey packagesYou might also use Ansible to deploy the Chocolatey packages -some benefits are: Ansible idempotency: Rerun the deployment process to make sure, all needed packages are deployed  Update/Uninstall/Add packages depending on conditions Example for deploying Software packages initially: ---
- name: Install SW Packages using Chocolatey
  hosts: packer_masterimages_w11

  vars:
    # Add all packages which should be installed by Chocolatey
    choco_packages:
      - 7zip
      - git
      - putty
      - notepadplusplus
      - adobereader
      - firefox
      - sql-server-management-studio
      - newpackagename

  tasks:
  - name: Install packages
    win_chocolatey:
      name: "{{ item }}"
      state: present
    loop: "{{ choco_packages }}"
    register: get_choco_installation_results
    failed_when: false

  - name: Determine if any choco install returned reboot-required (3010)
    set_fact:
      reboot_required: &gt;-
        {{
          (
            get_choco_installation_results.results
            | selectattr('rc','defined') | selectattr('rc','equalto',3010) | list | length &gt; 0
          )
          or
          (
            get_choco_installation_results.results
            | selectattr('return_code','defined') | selectattr('return_code','equalto',3010) | list | length &gt; 0
          )
          or
          (
            get_choco_installation_results.results
            | selectattr('chocolatey_exit_code','defined') | selectattr('chocolatey_exit_code','equalto',3010) | list | length &gt; 0
          )
        }}

  - name: Show whether a host reboot is required
    debug:
      msg: "Chocolatey requests a host reboot: {{ reboot_required }}"

  - name: Reboot the host if Chocolatey signaled that a host reboot is required
    ansible.windows.win_reboot:
      reboot_timeout: 600
      pre_reboot_delay: 5
      post_reboot_delay: 30
    when: reboot_required | boolIn this snippet, Ansible reads a list of packages to install and installs them sequentially. It then looks through each installation´s return code and, if a reboot is necessary, automatically reboots the machine. If you add package names here, even after the initial run, Ansible and its Idempotency would only install the additional package. This is also used in our self-written website and framework, where we can visually select all required packages for deployment. The framework adds or removes the package names and triggers the playbook run. choco_packages:
      - 7zip
      - git
      - putty
      - notepadplusplus
      - adobereader
      - firefox
      - sql-server-management-studio
      - newpackagenameAs mentioned, you can also use the same snippet for uninstalling the packages – you only need to change the state from present to absent: tasks:
  - name: Install packages
    win_chocolatey:
      name: "{{ item }}"
      state: present # ----------------------------------
    loop: "{{ choco_packages }}"to tasks:
  - name: Remove packages
    win_chocolatey:
      name: "{{ item }}"
      state: absent # ----------------------------------
    loop: "{{ choco_packages }}"A Terraform code snippet starts the Ansible Playbook, as Terraform is the backend of all our IaC workflows. # Terraform Deployment to install Software Packages on Target machines using Ansible and Chocolatey
## Reference https://docs.ansible.com/projects/ansible/latest/collections/chocolatey/chocolatey/win_chocolatey_module.html
### Reference https://community.chocolatey.org/packages
#### Ansible-related Variables
###### ***** IMPORTANT: You must set the permissions to the destination directory accordingly (sudo) chmod 777 otherwise the provisioning will fail *****
resource "null_resource" "CopyPlaybooksForTargetToAnsibleServer" {
  connection {
    type     = var.TACG-TMM-Ansible-Connection-Type
    user     = var.TACG-TMM-Ansible-Connection-User
    password = var.TACG-TMM-Ansible-Connection-Password
    host     = var.TACG-TMM-Ansible-Connection-HostIP
  }

  provisioner "file" {
    source      = var.TACG-TMM-Ansible-GetSWPackagesInstalledByChocolatey-Playbook-Source
    destination = var.TACG-TMM-Ansible-GetSWPackagesInstalledByChocolatey-Playbook-Destination
  }

  provisioner "file" {
    source      = var.TACG-TMM-Ansible-InstallSWPackagesUsingChocolatey-Playbook-Source
    destination = var.TACG-TMM-Ansible-InstallSWPackagesUsingChocolatey-Playbook-Destination
  }

  provisioner "file" {
    source      = var.TACG-TMM-Ansible-RemoveSWPackagesUsingChocolatey-Playbook-Source
    destination = var.TACG-TMM-Ansible-RemoveSWPackagesUsingChocolatey-Playbook-Destination
  }

    provisioner "file" {
    source      = var.TACG-TMM-Ansible-CheckAnsibleReturnCodeShellScript-Source
    destination = var.TACG-TMM-Ansible-CheckAnsibleReturnCodeShellScript-Destination
  }

  provisioner "file" {
    source      = var.TACG-TMM-Ansible-Inventory-Source
    destination = var.TACG-TMM-Ansible-Inventory-Destination
  }
}
 
#### Wait 10s Minute for Backgroud Processes to settle
resource "time_sleep" "wait_10_seconds" {
  depends_on = [null_resource.CopyPlaybooksForTargetToAnsibleServer]
  create_duration = "10s"
}

#### Use Ansible to install Software Packages using Chocolatey on Target Machine 
resource "null_resource" "DeploySW" {
  depends_on = [time_sleep.wait_10_seconds]
  provisioner "local-exec" {
    working_dir = "/etc/ansible"
    command     = var.TACG-TMM-Ansible-InstallSWPackagesUsingChocolatey-CMD
  }
}

##### Check Ansible Return Code
data "external" "CheckAnsibleReturnCode" {
  depends_on = [null_resource.DeploySW]
  program    = ["bash", "/etc/ansible/packer/assets/CheckAnsibleReturnCode.sh", "/etc/ansible/packer/assets/ansible_exit_code.txt", "0|3010"]
}

##### Terminate if Return Code is not 0
resource "null_resource" "IfExitCodeIsNotZeroThenHalt" {
  depends_on = [data.external.CheckAnsibleReturnCode]
  provisioner "local-exec" {
    interpreter = ["/bin/bash", "-c"]
    command     = "[ \"${data.external.CheckAnsibleReturnCode.result.found}\" = \"1\" ] &amp;&amp; echo \"Software Packages were successfully deployed - Everything OK\" || { echo \"Deploying Software Packages failed. Please review the settings. Halting Terraform.\"; exit 1; }"
  }
}

resource "time_sleep" "wait_10_seconds_1" {
  depends_on      = [null_resource.IfExitCodeIsNotZeroThenHalt]
  create_duration = "10s"
}

#### Use Ansible to list all Software Packages using Chocolatey on Target Machine 
resource "null_resource" "GetSWPackages" {
  depends_on = [time_sleep.wait_10_seconds_1]
  provisioner "local-exec" {
    working_dir = "/etc/ansible"
    command     = var.TACG-TMM-Ansible-GetSWPackagesInstalledByChocolate-CMD
  }
}

##### Check Ansible Return Code
data "external" "CheckAnsibleReturnCode_1" {
  depends_on = [null_resource.GetSWPackages]
  program    = ["bash", "/etc/ansible/packer/assets/CheckAnsibleReturnCode.sh", "/etc/ansible/packer/assets/ansible_exit_code.txt", "0"]
}

##### Terminate if Return Code is not 0
resource "null_resource" "IfExitCodeIsNotZeroThenHalt_1" {
  depends_on = [data.external.CheckAnsibleReturnCode_1]
  provisioner "local-exec" {
    interpreter = ["/bin/bash", "-c"]
    command     = "[ \"${data.external.CheckAnsibleReturnCode_1.result.found}\" = \"1\" ] &amp;&amp; echo \"Software Packages were successfully listed - Everything OK\" || { echo \"Listing Software Packages failed. Please review the settings. Halting Terraform.\"; exit 1; }"
  }
}

##### All necessary playbooks on the VMs are completeTerraform uploads all necessary scripts and configuration items to the Ansible cluster, then runs the Software deployment playbook, and, after installation, the Software inventory playbook to list all packages installed by Ansible. Let´s have a look at the output: azadmin@az-weur-devops-automation:/etc/terraform-tmm/PACKER/_AZURE/ImgWithSoftwareFromAnsibleAndChocolatey$ terraform apply

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create
 &lt;= read (data resources)

Terraform will perform the following actions:

  # data.external.CheckAnsibleReturnCode will be read during apply
  # (depends on a resource or a module with changes pending)
 &lt;= data "external" "CheckAnsibleReturnCode" {
      + id      = (known after apply)
      + program = [
          + "bash",
          + "/etc/ansible/packer/assets/CheckAnsibleReturnCode.sh",
          + "/etc/ansible/packer/assets/ansible_exit_code.txt",
          + "0",
        ]
      + result  = (known after apply)
    }

  # data.external.CheckAnsibleReturnCode_1 will be read during apply
  # (depends on a resource or a module with changes pending)
 &lt;= data "external" "CheckAnsibleReturnCode_1" {
      + id      = (known after apply)
      + program = [
          + "bash",
          + "/etc/ansible/packer/assets/CheckAnsibleReturnCode.sh",
          + "/etc/ansible/packer/assets/ansible_exit_code.txt",
          + "0",
        ]
      + result  = (known after apply)
    }

  # null_resource.CopyPlaybooksForTargetToAnsibleServer will be created
  + resource "null_resource" "CopyPlaybooksForTargetToAnsibleServer" {
      + id = (known after apply)
    }

  # null_resource.DeploySW will be created
  + resource "null_resource" "DeploySW" {
      + id = (known after apply)
    }

  # null_resource.GetSWPackages will be created
  + resource "null_resource" "GetSWPackages" {
      + id = (known after apply)
    }

  # null_resource.IfExitCodeIsNotZeroThenHalt will be created
  + resource "null_resource" "IfExitCodeIsNotZeroThenHalt" {
      + id = (known after apply)
    }

  # null_resource.IfExitCodeIsNotZeroThenHalt_1 will be created
  + resource "null_resource" "IfExitCodeIsNotZeroThenHalt_1" {
      + id = (known after apply)
    }

  # time_sleep.wait_10_seconds will be created
  + resource "time_sleep" "wait_10_seconds" {
      + create_duration = "10s"
      + id              = (known after apply)
    }

  # time_sleep.wait_10_seconds_1 will be created
  + resource "time_sleep" "wait_10_seconds_1" {
      + create_duration = "10s"
      + id              = (known after apply)
    }

Plan: 7 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.CopyPlaybooksForTargetToAnsibleServer: Creating...
null_resource.CopyPlaybooksForTargetToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPlaybooksForTargetToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPlaybooksForTargetToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPlaybooksForTargetToAnsibleServer: Creation complete after 1s [id=7884248548548556180]
time_sleep.wait_10_seconds: Creating...
time_sleep.wait_10_seconds: Still creating... [00m10s elapsed]
time_sleep.wait_10_seconds: Creation complete after 10s [id=2025-12-01T13:41:41Z]
null_resource.DeploySW: Creating...
null_resource.DeploySW: Provisioning with 'local-exec'...
null_resource.DeploySW (local-exec): Executing: ["/bin/sh" "-c" "ansible-playbook -i /etc/ansible/packer/assets/inventory.ini /etc/ansible/packer/assets/InstallSWPackagesUsingChocolatey.ansible.yml -v -c winrm; echo $? &gt; /etc/ansible/packer/assets/ansible_exit_code.txt"]
null_resource.DeploySW (local-exec): Using /etc/ansible/ansible.cfg as config file

null_resource.DeploySW (local-exec): PLAY [Install SW Packages using Chocolatey] ************************************

null_resource.DeploySW (local-exec): TASK [Gathering Facts] *********************************************************
null_resource.DeploySW: Still creating... [00m10s elapsed]
null_resource.DeploySW (local-exec): ok: [10.53.16.16]

null_resource.DeploySW (local-exec): TASK [Install packages] ********************************************************
null_resource.DeploySW: Still creating... [00m20s elapsed]
null_resource.DeploySW: Still creating... [00m30s elapsed]
...
null_resource.DeploySW: Still creating... [14m10s elapsed]
null_resource.DeploySW: Still creating... [14m20s elapsed]
null_resource.DeploySW (local-exec): changed: [10.53.16.16] =&gt; (item=7zip) =&gt; {"ansible_loop_var": "item", "changed": true, "choco_cli_version": "2.5.1", "failed_when_result": false, "item": "7zip", "rc": 0}
null_resource.DeploySW (local-exec): changed: [10.53.16.16] =&gt; (item=git) =&gt; {"ansible_loop_var": "item", "changed": true, "choco_cli_version": "2.5.1", "failed_when_result": false, "item": "git", "rc": 0}
null_resource.DeploySW (local-exec): changed: [10.53.16.16] =&gt; (item=putty) =&gt; {"ansible_loop_var": "item", "changed": true, "choco_cli_version": "2.5.1", "failed_when_result": false, "item": "putty", "rc": 0}
null_resource.DeploySW (local-exec): changed: [10.53.16.16] =&gt; (item=notepadplusplus) =&gt; {"ansible_loop_var": "item", "changed": true, "choco_cli_version": "2.5.1", "failed_when_result": false, "item": "notepadplusplus", "rc": 0}
null_resource.DeploySW (local-exec): changed: [10.53.16.16] =&gt; (item=adobereader) =&gt; {"ansible_loop_var": "item", "changed": true, "choco_cli_version": "2.5.1", "failed_when_result": false, "item": "adobereader", "rc": 0}
null_resource.DeploySW (local-exec): changed: [10.53.16.16] =&gt; (item=firefox) =&gt; {"ansible_loop_var": "item", "changed": true, "choco_cli_version": "2.5.1", "failed_when_result": false, "item": "firefox", "rc": 0}
null_resource.DeploySW (local-exec): changed: [10.53.16.16] =&gt; (item=sql-server-management-studio) =&gt; {"ansible_loop_var": "item", "changed": true, "choco_cli_version": "2.5.1", "failed_when_result": false, "item": "sql-server-management-studio", "rc": 3010}

null_resource.DeploySW (local-exec): TASK [Determine if any choco install returned reboot-required (3010)] **********
null_resource.DeploySW (local-exec): ok: [10.53.16.16] =&gt; {"ansible_facts": {"reboot_required": true}, "changed": false}

null_resource.DeploySW (local-exec): TASK [Show whether a host reboot is required] **********************************
null_resource.DeploySW (local-exec): ok: [10.53.16.16] =&gt; {
null_resource.DeploySW (local-exec):     "msg": "Chocolatey requests a host reboot: True"
null_resource.DeploySW (local-exec): }

null_resource.DeploySW (local-exec): TASK [Reboot the host if Chocolatey signaled that a host reboot is required] ***
null_resource.DeploySW: Still creating... [14m30s elapsed]
null_resource.DeploySW: Still creating... [14m40s elapsed]
...
null_resource.DeploySW: Still creating... [17m10s elapsed]
null_resource.DeploySW: Still creating... [17m20s elapsed]
null_resource.DeploySW (local-exec): changed: [10.53.16.16] =&gt; {"changed": true, "elapsed": 180, "rebooted": true, "unreachable": false}

null_resource.DeploySW (local-exec): PLAY RECAP *********************************************************************
null_resource.DeploySW (local-exec): 10.53.16.16                : ok=5    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

null_resource.DeploySW: Creation complete after 17m30s [id=1780552660517598418]
data.external.CheckAnsibleReturnCode: Reading...
data.external.CheckAnsibleReturnCode: Read complete after 0s [id=-]
null_resource.IfExitCodeIsNotZeroThenHalt: Creating...
null_resource.IfExitCodeIsNotZeroThenHalt: Provisioning with 'local-exec'...
null_resource.IfExitCodeIsNotZeroThenHalt (local-exec): Executing: ["/bin/bash" "-c" "[ \"1\" = \"1\" ] &amp;&amp; echo \"Software Packages were successfully deployed - Everything OK\" || { echo \"Deploying Software Packages failed. Please review the settings. Halting Terraform.\"; exit 1; }"]
null_resource.IfExitCodeIsNotZeroThenHalt (local-exec): Software Packages were successfully deployed - Everything OK
null_resource.IfExitCodeIsNotZeroThenHalt: Creation complete after 0s [id=5539246426978853570]
time_sleep.wait_10_seconds_1: Creating...
time_sleep.wait_10_seconds_1: Still creating... [00m10s elapsed]
time_sleep.wait_10_seconds_1: Creation complete after 10s [id=2025-12-01T13:59:20Z]
null_resource.GetSWPackages: Creating...
null_resource.GetSWPackages: Provisioning with 'local-exec'...
null_resource.GetSWPackages (local-exec): Executing: ["/bin/sh" "-c" "ansible-playbook -i /etc/ansible/packer/assets/inventory.ini /etc/ansible/packer/assets/GetSWPackagesInstalledByChocolatey.ansible.yml -v -c winrm; echo $? &gt; /etc/ansible/packer/assets/ansible_exit_code.txt"]
null_resource.GetSWPackages (local-exec): [WARNING]: Ansible is being run in a world writable directory (/etc/ansible), ignoring it as an ansible.cfg source. For more information see https://docs.ansible.com/ansible/devel/reference_appendices/config.html#cfg-in-world-writable-dir
null_resource.GetSWPackages (local-exec): Using /etc/ansible/ansible.cfg as config file

null_resource.GetSWPackages (local-exec): PLAY [Print out all software packages installed by Chocolatey] *****************

null_resource.GetSWPackages (local-exec): TASK [Gathering Facts] *********************************************************
null_resource.GetSWPackages (local-exec): ok: [10.53.16.16]

null_resource.GetSWPackages (local-exec): TASK [Get all by Chocolatey installed packages] ********************************
null_resource.GetSWPackages: Still creating... [00m10s elapsed]
null_resource.GetSWPackages (local-exec): ok: [10.53.16.16] =&gt; {"ansible_facts": {"ansible_chocolatey": {"config": {"cacheLocation": "", "commandExecutionTimeoutSeconds": 2700, "containsLegacyPackageInstalls": true, "defaultPushSource": "", "defaultTemplateName": "", "proxy": "", "proxyBypassList": "", "proxyBypassOnLocal": true, "proxyPassword": "", "proxyUser": "", "upgradeAllExceptions": "", "webRequestTimeoutSeconds": 30}, "feature": {"allowEmptyChecksums": false, "allowEmptyChecksumsSecure": true, "allowGlobalConfirmation": false, "alwaysIncludeHeaders": false, "autoUninstaller": true, "checksumFiles": true, "disableCompatibilityChecks": false, "exitOnRebootDetected": false, "failOnAutoUninstaller": false, "failOnInvalidOrMissingLicense": false, "failOnStandardError": false, "ignoreInvalidOptionsSwitches": true, "ignoreUnfoundPackagesOnUpgradeOutdated": false, "logEnvironmentValues": false, "logValidationResultsOnWarnings": true, "logWithoutColor": false, "powershellHost": true, "removePackageInformationOnUninstall": false, "showDownloadProgress": true, "showNonElevatedWarnings": true, "skipPackageUpgradesWhenNotInstalled": false, "stopOnFirstPackageFailure": false, "useEnhancedExitCodes": false, "useFipsCompliantChecksums": false, "useHttpCache": true, "usePackageExitCodes": true, "usePackageHashValidation": false, "usePackageRepositoryOptimizations": true, "useRememberedArgumentsForUpgrades": false, "virusCheck": false}, "filter": ["all"], "outdated": [], "packages": [{"package": "7zip", "version": "25.1.0"}, {"package": "7zip.install", "version": "25.1.0"}, {"package": "adobereader", "version": "2025.1.20844"}, {"package": "chocolatey", "version": "2.5.1"}, {"package": "chocolatey-compatibility.extension", "version": "1.0.0"}, {"package": "chocolatey-core.extension", "version": "1.4.0"}, {"package": "chocolatey-dotnetfx.extension", "version": "1.0.1"}, {"package": "chocolatey-visualstudio.extension", "version": "1.13.0"}, {"package": "dotnetfx", "version": "4.8.0.20220524"}, {"package": "Firefox", "version": "145.0.2"}, {"package": "git", "version": "2.52.0"}, {"package": "git.install", "version": "2.52.0"}, {"package": "KB2919355", "version": "1.0.20160915"}, {"package": "KB2919442", "version": "1.0.20160915"}, {"package": "notepadplusplus", "version": "8.8.8"}, {"package": "notepadplusplus.install", "version": "8.8.8"}, {"package": "putty", "version": "0.83.0"}, {"package": "putty.portable", "version": "0.83.0"}, {"package": "sql-server-management-studio", "version": "22.0.0"}], "sources": [{"admin_only": false, "allow_self_service": false, "bypass_proxy": false, "certificate": null, "disabled": false, "name": "chocolatey", "priority": 0, "source": "https://community.chocolatey.org/api/v2/", "source_username": null}]}}, "changed": false}

null_resource.GetSWPackages (local-exec): TASK [Print out facts] *********************************************************
null_resource.GetSWPackages (local-exec): ok: [10.53.16.16] =&gt; {
null_resource.GetSWPackages (local-exec):     "list_choco_packages.ansible_facts.ansible_chocolatey.packages": [
null_resource.GetSWPackages (local-exec):         {
null_resource.GetSWPackages (local-exec):             "package": "7zip",
null_resource.GetSWPackages (local-exec):             "version": "25.1.0"
null_resource.GetSWPackages (local-exec):         },
null_resource.GetSWPackages (local-exec):         {
null_resource.GetSWPackages (local-exec):             "package": "7zip.install",
null_resource.GetSWPackages (local-exec):             "version": "25.1.0"
null_resource.GetSWPackages (local-exec):         },
null_resource.GetSWPackages (local-exec):         {
null_resource.GetSWPackages (local-exec):             "package": "adobereader",
null_resource.GetSWPackages (local-exec):             "version": "2025.1.20844"
null_resource.GetSWPackages (local-exec):         },
null_resource.GetSWPackages (local-exec):         {
null_resource.GetSWPackages (local-exec):             "package": "chocolatey",
null_resource.GetSWPackages (local-exec):             "version": "2.5.1"
null_resource.GetSWPackages (local-exec):         },
null_resource.GetSWPackages (local-exec):         {
null_resource.GetSWPackages (local-exec):             "package": "chocolatey-compatibility.extension",
null_resource.GetSWPackages (local-exec):             "version": "1.0.0"
null_resource.GetSWPackages (local-exec):         },
null_resource.GetSWPackages (local-exec):         {
null_resource.GetSWPackages (local-exec):             "package": "chocolatey-core.extension",
null_resource.GetSWPackages (local-exec):             "version": "1.4.0"
null_resource.GetSWPackages (local-exec):         },
null_resource.GetSWPackages (local-exec):         {
null_resource.GetSWPackages (local-exec):             "package": "chocolatey-dotnetfx.extension",
null_resource.GetSWPackages (local-exec):             "version": "1.0.1"
null_resource.GetSWPackages (local-exec):         },
null_resource.GetSWPackages (local-exec):         {
null_resource.GetSWPackages (local-exec):             "package": "chocolatey-visualstudio.extension",
null_resource.GetSWPackages (local-exec):             "version": "1.13.0"
null_resource.GetSWPackages (local-exec):         },
null_resource.GetSWPackages (local-exec):         {
null_resource.GetSWPackages (local-exec):             "package": "dotnetfx",
null_resource.GetSWPackages (local-exec):             "version": "4.8.0.20220524"
null_resource.GetSWPackages (local-exec):         },
null_resource.GetSWPackages (local-exec):         {
null_resource.GetSWPackages (local-exec):             "package": "Firefox",
null_resource.GetSWPackages (local-exec):             "version": "145.0.2"
null_resource.GetSWPackages (local-exec):         },
null_resource.GetSWPackages (local-exec):         {
null_resource.GetSWPackages (local-exec):             "package": "git",
null_resource.GetSWPackages (local-exec):             "version": "2.52.0"
null_resource.GetSWPackages (local-exec):         },
null_resource.GetSWPackages (local-exec):         {
null_resource.GetSWPackages (local-exec):             "package": "git.install",
null_resource.GetSWPackages (local-exec):             "version": "2.52.0"
null_resource.GetSWPackages (local-exec):         },
null_resource.GetSWPackages (local-exec):         {
null_resource.GetSWPackages (local-exec):             "package": "KB2919355",
null_resource.GetSWPackages (local-exec):             "version": "1.0.20160915"
null_resource.GetSWPackages (local-exec):         },
null_resource.GetSWPackages (local-exec):         {
null_resource.GetSWPackages (local-exec):             "package": "KB2919442",
null_resource.GetSWPackages (local-exec):             "version": "1.0.20160915"
null_resource.GetSWPackages (local-exec):         },
null_resource.GetSWPackages (local-exec):         {
null_resource.GetSWPackages (local-exec):             "package": "notepadplusplus",
null_resource.GetSWPackages (local-exec):             "version": "8.8.8"
null_resource.GetSWPackages (local-exec):         },
null_resource.GetSWPackages (local-exec):         {
null_resource.GetSWPackages (local-exec):             "package": "notepadplusplus.install",
null_resource.GetSWPackages (local-exec):             "version": "8.8.8"
null_resource.GetSWPackages (local-exec):         },
null_resource.GetSWPackages (local-exec):         {
null_resource.GetSWPackages (local-exec):             "package": "putty",
null_resource.GetSWPackages (local-exec):             "version": "0.83.0"
null_resource.GetSWPackages (local-exec):         },
null_resource.GetSWPackages (local-exec):         {
null_resource.GetSWPackages (local-exec):             "package": "putty.portable",
null_resource.GetSWPackages (local-exec):             "version": "0.83.0"
null_resource.GetSWPackages (local-exec):         },
null_resource.GetSWPackages (local-exec):         {
null_resource.GetSWPackages (local-exec):             "package": "sql-server-management-studio",
null_resource.GetSWPackages (local-exec):             "version": "22.0.0"
null_resource.GetSWPackages (local-exec):         }
null_resource.GetSWPackages (local-exec):     ]
null_resource.GetSWPackages (local-exec): }

null_resource.GetSWPackages (local-exec): PLAY RECAP *********************************************************************
null_resource.GetSWPackages (local-exec): 10.53.16.16                : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

null_resource.GetSWPackages: Creation complete after 14s [id=2076355028329137182]
data.external.CheckAnsibleReturnCode_1: Reading...
data.external.CheckAnsibleReturnCode_1: Read complete after 0s [id=-]
null_resource.IfExitCodeIsNotZeroThenHalt_1: Creating...
null_resource.IfExitCodeIsNotZeroThenHalt_1: Provisioning with 'local-exec'...
null_resource.IfExitCodeIsNotZeroThenHalt_1 (local-exec): Executing: ["/bin/bash" "-c" "[ \"1\" = \"1\" ] &amp;&amp; echo \"Software Packages were successfully listed - Everything OK\" || { echo \"Listing Software Packages failed. Please review the settings. Halting Terraform.\"; exit 1; }"]
null_resource.IfExitCodeIsNotZeroThenHalt_1 (local-exec): Software Packages were successfully listed - Everything OK
null_resource.IfExitCodeIsNotZeroThenHalt_1: Creation complete after 0s [id=4904832862807509256]

Apply complete! Resources: 7 added, 0 changed, 0 destroyed.
azadmin@az-weur-devops-automation:/etc/terraform-tmm/PACKER/_AZURE/ImgWithSoftwareFromAnsibleAndChocolatey$ Rerunning Software Deployment PlaybookIf you rerun the Software deployment playbook, Ansible uses its idempotency to ensure all playbooks are still installed: Example for rerunning the Terraform code to ensure all packages are installed: ...
null_resource.DeploySW (local-exec): TASK [Install packages] ********************************************************
null_resource.DeploySW: Still creating... [00m10s elapsed]
null_resource.DeploySW: Still creating... [00m20s elapsed]
null_resource.DeploySW: Still creating... [00m30s elapsed]
null_resource.DeploySW: Still creating... [00m40s elapsed]
null_resource.DeploySW (local-exec): ok: [10.53.16.16] =&gt; (item=7zip) =&gt; {"ansible_loop_var": "item", "changed": false, "choco_cli_version": "2.5.1", "failed_when_result": false, "item": "7zip", "rc": 0}
null_resource.DeploySW (local-exec): ok: [10.53.16.16] =&gt; (item=git) =&gt; {"ansible_loop_var": "item", "changed": false, "choco_cli_version": "2.5.1", "failed_when_result": false, "item": "git", "rc": 0}
null_resource.DeploySW (local-exec): ok: [10.53.16.16] =&gt; (item=putty) =&gt; {"ansible_loop_var": "item", "changed": false, "choco_cli_version": "2.5.1", "failed_when_result": false, "item": "putty", "rc": 0}
null_resource.DeploySW (local-exec): ok: [10.53.16.16] =&gt; (item=notepadplusplus) =&gt; {"ansible_loop_var": "item", "changed": false, "choco_cli_version": "2.5.1", "failed_when_result": false, "item": "notepadplusplus", "rc": 0}
null_resource.DeploySW (local-exec): ok: [10.53.16.16] =&gt; (item=adobereader) =&gt; {"ansible_loop_var": "item", "changed": false, "choco_cli_version": "2.5.1", "failed_when_result": false, "item": "adobereader", "rc": 0}
null_resource.DeploySW (local-exec): ok: [10.53.16.16] =&gt; (item=firefox) =&gt; {"ansible_loop_var": "item", "changed": false, "choco_cli_version": "2.5.1", "failed_when_result": false, "item": "firefox", "rc": 0}
null_resource.DeploySW (local-exec): ok: [10.53.16.16] =&gt; (item=sql-server-management-studio) =&gt; {"ansible_loop_var": "item", "changed": false, "choco_cli_version": "2.5.1", "failed_when_result": false, "item": "sql-server-management-studio", "rc": 0}

null_resource.DeploySW (local-exec): TASK [Determine if any choco install returned reboot-required (3010)] **********
null_resource.DeploySW (local-exec): ok: [10.53.16.16] =&gt; {"ansible_facts": {"reboot_required": false}, "changed": false}

null_resource.DeploySW (local-exec): TASK [Show whether a host reboot is required] **********************************
null_resource.DeploySW (local-exec): ok: [10.53.16.16] =&gt; {
null_resource.DeploySW (local-exec):     "msg": "Chocolatey requests a host reboot: False"
null_resource.DeploySW (local-exec): }

...No packages need to be changed; everything is fine. Uninstalling Software PackagesAs mentioned above, you can use Terraform and Ansible to remove previously installed Software packages. You only need to set the state to absent and tell Ansible which packages should be removed. To make sure all dependencies are also removed, you can use the corresponding setting: ---
- name: Remove SW Packages using Chocolatey
  hosts: packer_masterimages_w11

  vars:
    # Add all packages which should be removed by Chocolatey
    choco_packages:
      - notepadplusplus

  tasks:
  - name: Remove packages
    win_chocolatey:
      name: "{{ item }}"
      remove_dependencies: true
      state: absent
    loop: "{{ choco_packages }}"
    register: get_choco_installation_results
    failed_when: false

  - name: Determine if any choco install returned reboot-required (3010)
    set_fact:
      reboot_required: &gt;-
        {{
          (
            get_choco_installation_results.results
            | selectattr('rc','defined') | selectattr('rc','equalto',3010) | list | length &gt; 0
          )
          or
          (
            get_choco_installation_results.results
            | selectattr('return_code','defined') | selectattr('return_code','equalto',3010) | list | length &gt; 0
          )
          or
          (
            get_choco_installation_results.results
            | selectattr('chocolatey_exit_code','defined') | selectattr('chocolatey_exit_code','equalto',3010) | list | length &gt; 0
          )
        }}

  - name: Show whether a host reboot is required
    debug:
      msg: "Chocolatey requests a host reboot: {{ reboot_required }}"

  - name: Reboot the host if Chocolatey signaled that a host reboot is required
    ansible.windows.win_reboot:
      reboot_timeout: 600
      pre_reboot_delay: 5
      post_reboot_delay: 30
    when: reboot_required | bool
Example for running the Terraform code to remove specific packages: azadmin@az-weur-devops-automation:/etc/terraform-tmm/PACKER/_AZURE/ImgWithSoftwareFromAnsibleAndChocolatey$ terraform apply

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create
 &lt;= read (data resources)

Terraform will perform the following actions:

  # data.external.CheckAnsibleReturnCodeRemove will be read during apply
  # (depends on a resource or a module with changes pending)
 &lt;= data "external" "CheckAnsibleReturnCodeRemove" {
      + id      = (known after apply)
      + program = [
          + "bash",
          + "/etc/ansible/packer/assets/CheckAnsibleReturnCode.sh",
          + "/etc/ansible/packer/assets/ansible_exit_code.txt",
          + "0|3010",
        ]
      + result  = (known after apply)
    }

  # data.external.CheckAnsibleReturnCodeRemove_1 will be read during apply
  # (depends on a resource or a module with changes pending)
 &lt;= data "external" "CheckAnsibleReturnCodeRemove_1" {
      + id      = (known after apply)
      + program = [
          + "bash",
          + "/etc/ansible/packer/assets/CheckAnsibleReturnCode.sh",
          + "/etc/ansible/packer/assets/ansible_exit_code.txt",
          + "0",
        ]
      + result  = (known after apply)
    }

  # null_resource.GetSWPackagesRemove will be created
  + resource "null_resource" "GetSWPackagesRemove" {
      + id = (known after apply)
    }

  # null_resource.IfExitCodeIsNotZeroThenHaltRemove will be created
  + resource "null_resource" "IfExitCodeIsNotZeroThenHaltRemove" {
      + id = (known after apply)
    }

  # null_resource.IfExitCodeIsNotZeroThenHaltRemove_1 will be created
  + resource "null_resource" "IfExitCodeIsNotZeroThenHaltRemove_1" {
      + id = (known after apply)
    }

  # null_resource.RemoveSW will be created
  + resource "null_resource" "RemoveSW" {
      + id = (known after apply)
    }

  # time_sleep.wait_10_seconds_remove_1 will be created
  + resource "time_sleep" "wait_10_seconds_remove_1" {
      + create_duration = "10s"
      + id              = (known after apply)
    }

Plan: 5 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.RemoveSW: Creating...
null_resource.RemoveSW: Provisioning with 'local-exec'...
null_resource.RemoveSW (local-exec): Executing: ["/bin/sh" "-c" "ansible-playbook -i /etc/ansible/packer/assets/inventory.ini /etc/ansible/packer/assets/RemoveSWPackagesUsingChocolatey.ansible.yml -v -c winrm; echo $? &gt; /etc/ansible/packer/assets/ansible_exit_code.txt"]
null_resource.RemoveSW (local-exec): Using /etc/ansible/ansible.cfg as config file

null_resource.RemoveSW (local-exec): PLAY [Remove SW Packages using Chocolatey] *************************************

null_resource.RemoveSW (local-exec): TASK [Gathering Facts] *********************************************************
null_resource.RemoveSW (local-exec): ok: [10.53.16.16]

null_resource.RemoveSW (local-exec): TASK [Remove packages] ********************************************************
null_resource.RemoveSW: Still creating... [00m10s elapsed]
null_resource.RemoveSW (local-exec): changed: [10.53.16.16] =&gt; (item=notepadplusplus) =&gt; {"ansible_loop_var": "item", "changed": true, "choco_cli_version": "2.5.1", "failed_when_result": false, "item": "notepadplusplus", "rc": 0}

null_resource.RemoveSW (local-exec): TASK [Determine if any choco install returned reboot-required (3010)] **********
null_resource.RemoveSW (local-exec): ok: [10.53.16.16] =&gt; {"ansible_facts": {"reboot_required": false}, "changed": false}

null_resource.RemoveSW (local-exec): TASK [Show whether a host reboot is required] **********************************
null_resource.RemoveSW (local-exec): ok: [10.53.16.16] =&gt; {
null_resource.RemoveSW (local-exec):     "msg": "Chocolatey requests a host reboot: False"
null_resource.RemoveSW (local-exec): }

null_resource.RemoveSW (local-exec): TASK [Reboot the host if Chocolatey signaled that a host reboot is required] ***
null_resource.RemoveSW (local-exec): skipping: [10.53.16.16] =&gt; {"changed": false, "false_condition": "reboot_required | bool", "skip_reason": "Conditional result was False"}

null_resource.RemoveSW (local-exec): PLAY RECAP *********************************************************************
null_resource.RemoveSW (local-exec): 10.53.16.16                : ok=4    changed=1    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0

null_resource.RemoveSW: Creation complete after 16s [id=4885597512309489198]
data.external.CheckAnsibleReturnCodeRemove: Reading...
data.external.CheckAnsibleReturnCodeRemove: Read complete after 0s [id=-]
null_resource.IfExitCodeIsNotZeroThenHaltRemove: Creating...
null_resource.IfExitCodeIsNotZeroThenHaltRemove: Provisioning with 'local-exec'...
null_resource.IfExitCodeIsNotZeroThenHaltRemove (local-exec): Executing: ["/bin/bash" "-c" "[ \"1\" = \"1\" ] &amp;&amp; echo \"Software Packages were successfully removed - Everything OK\" || { echo \"Removing Software Packages failed. Please review the settings. Halting Terraform.\"; exit 1; }"]
null_resource.IfExitCodeIsNotZeroThenHaltRemove (local-exec): Software Packages were successfully removed - Everything OK
null_resource.IfExitCodeIsNotZeroThenHaltRemove: Creation complete after 0s [id=7865390904371031353]
time_sleep.wait_10_seconds_remove_1: Creating...
time_sleep.wait_10_seconds_remove_1: Still creating... [00m10s elapsed]
time_sleep.wait_10_seconds_remove_1: Creation complete after 10s [id=2025-12-01T15:11:06Z]
null_resource.GetSWPackagesRemove: Creating...
null_resource.GetSWPackagesRemove: Provisioning with 'local-exec'...
null_resource.GetSWPackagesRemove (local-exec): Executing: ["/bin/sh" "-c" "ansible-playbook -i /etc/ansible/packer/assets/inventory.ini /etc/ansible/packer/assets/GetSWPackagesInstalledByChocolatey.ansible.yml -v -c winrm; echo $? &gt; /etc/ansible/packer/assets/ansible_exit_code.txt"]
null_resource.GetSWPackagesRemove (local-exec): [WARNING]: Ansible is being run in a world writable directory (/etc/ansible), ignoring it as an ansible.cfg source. For more information see https://docs.ansible.com/ansible/devel/reference_appendices/config.html#cfg-in-world-writable-dir
null_resource.GetSWPackagesRemove (local-exec): Using /etc/ansible/ansible.cfg as config file

null_resource.GetSWPackagesRemove (local-exec): PLAY [Print out all software packages installed by Chocolatey] *****************

null_resource.GetSWPackagesRemove (local-exec): TASK [Gathering Facts] *********************************************************
null_resource.GetSWPackagesRemove (local-exec): ok: [10.53.16.16]

null_resource.GetSWPackagesRemove (local-exec): TASK [Get all by Chocolatey installed packages] ********************************
null_resource.GetSWPackagesRemove: Still creating... [00m10s elapsed]
null_resource.GetSWPackagesRemove (local-exec): ok: [10.53.16.16] =&gt; {"ansible_facts": {"ansible_chocolatey": {"config": {"cacheLocation": "", "commandExecutionTimeoutSeconds": 2700, "containsLegacyPackageInstalls": true, "defaultPushSource": "", "defaultTemplateName": "", "proxy": "", "proxyBypassList": "", "proxyBypassOnLocal": true, "proxyPassword": "", "proxyUser": "", "upgradeAllExceptions": "", "webRequestTimeoutSeconds": 30}, "feature": {"allowEmptyChecksums": false, "allowEmptyChecksumsSecure": true, "allowGlobalConfirmation": false, "alwaysIncludeHeaders": false, "autoUninstaller": true, "checksumFiles": true, "disableCompatibilityChecks": false, "exitOnRebootDetected": false, "failOnAutoUninstaller": false, "failOnInvalidOrMissingLicense": false, "failOnStandardError": false, "ignoreInvalidOptionsSwitches": true, "ignoreUnfoundPackagesOnUpgradeOutdated": false, "logEnvironmentValues": false, "logValidationResultsOnWarnings": true, "logWithoutColor": false, "powershellHost": true, "removePackageInformationOnUninstall": false, "showDownloadProgress": true, "showNonElevatedWarnings": true, "skipPackageUpgradesWhenNotInstalled": false, "stopOnFirstPackageFailure": false, "useEnhancedExitCodes": false, "useFipsCompliantChecksums": false, "useHttpCache": true, "usePackageExitCodes": true, "usePackageHashValidation": false, "usePackageRepositoryOptimizations": true, "useRememberedArgumentsForUpgrades": false, "virusCheck": false}, "filter": ["all"], "outdated": [], "packages": [{"package": "7zip", "version": "25.1.0"}, {"package": "7zip.install", "version": "25.1.0"}, {"package": "adobereader", "version": "2025.1.20844"}, {"package": "chocolatey", "version": "2.5.1"}, {"package": "chocolatey-compatibility.extension", "version": "1.0.0"}, {"package": "chocolatey-core.extension", "version": "1.4.0"}, {"package": "chocolatey-dotnetfx.extension", "version": "1.0.1"}, {"package": "chocolatey-visualstudio.extension", "version": "1.13.0"}, {"package": "dotnetfx", "version": "4.8.0.20220524"}, {"package": "Firefox", "version": "145.0.2"}, {"package": "git", "version": "2.52.0"}, {"package": "git.install", "version": "2.52.0"}, {"package": "KB2919355", "version": "1.0.20160915"}, {"package": "KB2919442", "version": "1.0.20160915"}, {"package": "putty", "version": "0.83.0"}, {"package": "putty.portable", "version": "0.83.0"}, {"package": "sql-server-management-studio", "version": "22.0.0"}], "sources": [{"admin_only": false, "allow_self_service": false, "bypass_proxy": false, "certificate": null, "disabled": false, "name": "chocolatey", "priority": 0, "source": "https://community.chocolatey.org/api/v2/", "source_username": null}]}}, "changed": false}

null_resource.GetSWPackagesRemove (local-exec): TASK [Print out facts] *********************************************************
null_resource.GetSWPackagesRemove (local-exec): ok: [10.53.16.16] =&gt; {
null_resource.GetSWPackagesRemove (local-exec):     "list_choco_packages.ansible_facts.ansible_chocolatey.packages": [
null_resource.GetSWPackagesRemove (local-exec):         {
null_resource.GetSWPackagesRemove (local-exec):             "package": "7zip",
null_resource.GetSWPackagesRemove (local-exec):             "version": "25.1.0"
null_resource.GetSWPackagesRemove (local-exec):         },
null_resource.GetSWPackagesRemove (local-exec):         {
null_resource.GetSWPackagesRemove (local-exec):             "package": "7zip.install",
null_resource.GetSWPackagesRemove (local-exec):             "version": "25.1.0"
null_resource.GetSWPackagesRemove (local-exec):         },
null_resource.GetSWPackagesRemove (local-exec):         {
null_resource.GetSWPackagesRemove (local-exec):             "package": "adobereader",
null_resource.GetSWPackagesRemove (local-exec):             "version": "2025.1.20844"
null_resource.GetSWPackagesRemove (local-exec):         },
null_resource.GetSWPackagesRemove (local-exec):         {
null_resource.GetSWPackagesRemove (local-exec):             "package": "chocolatey",
null_resource.GetSWPackagesRemove (local-exec):             "version": "2.5.1"
null_resource.GetSWPackagesRemove (local-exec):         },
null_resource.GetSWPackagesRemove (local-exec):         {
null_resource.GetSWPackagesRemove (local-exec):             "package": "chocolatey-compatibility.extension",
null_resource.GetSWPackagesRemove (local-exec):             "version": "1.0.0"
null_resource.GetSWPackagesRemove (local-exec):         },
null_resource.GetSWPackagesRemove (local-exec):         {
null_resource.GetSWPackagesRemove (local-exec):             "package": "chocolatey-core.extension",
null_resource.GetSWPackagesRemove (local-exec):             "version": "1.4.0"
null_resource.GetSWPackagesRemove (local-exec):         },
null_resource.GetSWPackagesRemove (local-exec):         {
null_resource.GetSWPackagesRemove (local-exec):             "package": "chocolatey-dotnetfx.extension",
null_resource.GetSWPackagesRemove (local-exec):             "version": "1.0.1"
null_resource.GetSWPackagesRemove (local-exec):         },
null_resource.GetSWPackagesRemove (local-exec):         {
null_resource.GetSWPackagesRemove (local-exec):             "package": "chocolatey-visualstudio.extension",
null_resource.GetSWPackagesRemove (local-exec):             "version": "1.13.0"
null_resource.GetSWPackagesRemove (local-exec):         },
null_resource.GetSWPackagesRemove (local-exec):         {
null_resource.GetSWPackagesRemove (local-exec):             "package": "dotnetfx",
null_resource.GetSWPackagesRemove (local-exec):             "version": "4.8.0.20220524"
null_resource.GetSWPackagesRemove (local-exec):         },
null_resource.GetSWPackagesRemove (local-exec):         {
null_resource.GetSWPackagesRemove (local-exec):             "package": "Firefox",
null_resource.GetSWPackagesRemove (local-exec):             "version": "145.0.2"
null_resource.GetSWPackagesRemove (local-exec):         },
null_resource.GetSWPackagesRemove (local-exec):         {
null_resource.GetSWPackagesRemove (local-exec):             "package": "git",
null_resource.GetSWPackagesRemove (local-exec):             "version": "2.52.0"
null_resource.GetSWPackagesRemove (local-exec):         },
null_resource.GetSWPackagesRemove (local-exec):         {
null_resource.GetSWPackagesRemove (local-exec):             "package": "git.install",
null_resource.GetSWPackagesRemove (local-exec):             "version": "2.52.0"
null_resource.GetSWPackagesRemove (local-exec):         },
null_resource.GetSWPackagesRemove (local-exec):         {
null_resource.GetSWPackagesRemove (local-exec):             "package": "KB2919355",
null_resource.GetSWPackagesRemove (local-exec):             "version": "1.0.20160915"
null_resource.GetSWPackagesRemove (local-exec):         },
null_resource.GetSWPackagesRemove (local-exec):         {
null_resource.GetSWPackagesRemove (local-exec):             "package": "KB2919442",
null_resource.GetSWPackagesRemove (local-exec):             "version": "1.0.20160915"
null_resource.GetSWPackagesRemove (local-exec):         },
null_resource.GetSWPackagesRemove (local-exec):         {
null_resource.GetSWPackagesRemove (local-exec):             "package": "putty",
null_resource.GetSWPackagesRemove (local-exec):             "version": "0.83.0"
null_resource.GetSWPackagesRemove (local-exec):         },
null_resource.GetSWPackagesRemove (local-exec):         {
null_resource.GetSWPackagesRemove (local-exec):             "package": "putty.portable",
null_resource.GetSWPackagesRemove (local-exec):             "version": "0.83.0"
null_resource.GetSWPackagesRemove (local-exec):         },
null_resource.GetSWPackagesRemove (local-exec):         {
null_resource.GetSWPackagesRemove (local-exec):             "package": "sql-server-management-studio",
null_resource.GetSWPackagesRemove (local-exec):             "version": "22.0.0"
null_resource.GetSWPackagesRemove (local-exec):         }
null_resource.GetSWPackagesRemove (local-exec):     ]
null_resource.GetSWPackagesRemove (local-exec): }

null_resource.GetSWPackagesRemove (local-exec): PLAY RECAP *********************************************************************
null_resource.GetSWPackagesRemove (local-exec): 10.53.16.16                : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

null_resource.GetSWPackagesRemove: Creation complete after 12s [id=1019312554423816987]
data.external.CheckAnsibleReturnCodeRemove_1: Reading...
data.external.CheckAnsibleReturnCodeRemove_1: Read complete after 0s [id=-]
null_resource.IfExitCodeIsNotZeroThenHaltRemove_1: Creating...
null_resource.IfExitCodeIsNotZeroThenHaltRemove_1: Provisioning with 'local-exec'...
null_resource.IfExitCodeIsNotZeroThenHaltRemove_1 (local-exec): Executing: ["/bin/bash" "-c" "[ \"1\" = \"1\" ] &amp;&amp; echo \"Software Packages were successfully listed - Everything OK\" || { echo \"Listing Software Packages failed. Please review the settings. Halting Terraform.\"; exit 1; }"]
null_resource.IfExitCodeIsNotZeroThenHaltRemove_1 (local-exec): Software Packages were successfully listed - Everything OK
null_resource.IfExitCodeIsNotZeroThenHaltRemove_1: Creation complete after 0s [id=613552392272345868]

Apply complete! Resources: 5 added, 0 changed, 0 destroyed.
azadmin@az-weur-devops-automation:/etc/terraform-tmm/PACKER/_AZURE/ImgWithSoftwareFromAnsibleAndChocolatey$Ansible successfully removed the Notepad++ package, the inventory does not list it anymore… Upgrading installed Software PackagesAs mentioned above, you can use Terraform and Ansible to upgrade all or specific installed Software packages. You only need to set the state to latest and tell Ansible which packages should be upgraded. tasks:
  - name: Upgrade packages
    win_chocolatey:
      name: "{{ item }}"
      state: latest
    loop: "{{ choco_packages }}"
    register: get_choco_installation_results
    failed_when: false Installing Software Packages using Ansible StandaloneThere might be circumstances where using Chocolatey would not be suitable to fulfill all Software deployment needs, e.g. no Chocolatey package is available. Terraform and Ansible serve complementary roles in modern Infrastructure-as-Code (IaC) architectures. Terraform excels at provisioning infrastructure resources—compute, networks, storage, identity, and cloud services—while Ansible specializes in configuring and managing the software, applications, and operating system state on those resources once they are created. When combined, they form a robust, scalable, and fully automated delivery pipeline that allows organizations to provision secure, consistent environments and configure them in a modular, policy-driven manner. Ansible has various built-in or available interaction points with Windows-based machines. You can virtually configure anything using Ansible. Ansible modules for Windows—such as win_package, win_service, and win_regedit—perform the heavy lifting: win_package installs MSI or EXE packages silently win_service ensures services are running as expected win_regedit tweaks registry keys for custom configurations Because of Ansible´s idempotency, it skips the configuration if the target machine already has the correct configuration. It offers more flexibility than Chocolatey, but is also more error-prone due to its complex syntax. Let´s look at some examples: inventory.yml all:
  vars:
    ansible_user: ""{{ vault_ansible_user }}""
    ansible_password: "{{ vault_ansible_password }}"
    ansible_connection: winrm
    ansible_winrm_transport: kerberos  # or "credssp" / "basic" over HTTPS
    ansible_port: 5986
    ansible_winrm_scheme: https
    ansible_winrm_server_cert_validation: ignore  # use 'validate' with proper certs in prod
  children:
    windows:
      hosts:
        win-app-01.domain.local:
        win-app-02.domain.local:
        win-util-01.domain.local:
    appservers:
      hosts:
        win-app-01.domain.local:
        win-app-02.domain.local:
    utility:
      hosts:
        win-util-01.domain.local:windows_vars.yml: # Global Windows settings for all hosts
reboot_if_needed: true
firewall_enable: true

# Chocolatey packages to install/ensure
choco_packages:
  - name: googlechrome
    state: present
  - name: 7zip
    state: latest
  - name: notepadplusplus
    state: present

# MSI/EXE package installs via win_package
msi_packages:
  - name: "ContosoApp"
    path: "https://download.contoso.com/ContosoApp-5.2.1.msi"
    product_id: "{A1B2C3D4-E5F6-1234-ABCD-000000000001}"
    arguments: "/qn /norestart"
    state: present

# Registry keys to enforce
registry_items:
  - path: 'HKLM:\Software\Contoso\App'
    name: 'TelemetryEnabled'
    type: dword
    value: 0
  - path: 'HKLM:\Software\Policies\Microsoft\Windows\PowerShell'
    name: 'EnableScripts'
    type: dword
    value: 1

# Services to manage
services:
  - name: "ContosoService"
    state: started
    start_mode: auto

# Firewall rules
firewall_rules:
  - name: "Allow_ContosoService_TCP_8443"
    localport: 8443
    protocol: tcp
    direction: in
    action: allow
    enabled: yes
    profiles: ["Domain"]

# Scheduled tasks
scheduled_tasks:
  - name: "ContosoLogCleanup"
    description: "Rotate logs weekly"
    actions:
      - path: "PowerShell.exe"
        arguments: "-File C:\\ProgramData\\Contoso\\scripts\\cleanup.ps1"
    triggers:
      - type: weekly
        start_boundary: "2025-01-01T03:00:00"
        days_of_week: ["Sunday"]
    username: "SYSTEM"
    run_level: highestapp_deploy_and_configure.yml: ---
- name: Prepare Windows hosts (base tools &amp; system policy)
  hosts: windows
  gather_facts: yes
  vars:
    chocolatey_state: present
  tasks:
    - name: Ensure Chocolatey is installed
      win_chocolatey:
        state: "{{ chocolatey_state }}"
      tags: [choco, base]

    - name: Install/Update Chocolatey packages
      win_chocolatey:
        name: "{{ item.name }}"
        state: "{{ item.state | default('present') }}"
      loop: "{{ choco_packages }}"
      tags: [choco, apps]

    - name: Configure registry settings
      win_regedit:
        path: "{{ item.path }}"
        name: "{{ item.name }}"
        type: "{{ item.type }}"
        data: "{{ item.value }}"
      loop: "{{ registry_items }}"
      tags: [registry, compliance]

    - name: Set PowerShell execution policy (machine-wide)
      win_shell: "Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope LocalMachine -Force"
      args:
        executable: PowerShell
      changed_when: false  # report-only unless you need to enforce idempotence via win_regedit
      tags: [policy]

    - name: Create directories for app config
      win_file:
        path: "{{ app_config_dir | default('C:\\ProgramData\\Contoso\\config') }}"
        state: directory
      tags: [files, config]

    - name: Copy static app settings
      win_copy:
        src: "{{ playbook_dir }}/../files/configs/appsettings.json"
        dest: "{{ app_config_dir | default('C:\\ProgramData\\Contoso\\config') }}\\appsettings.json"
      tags: [files, config]

    - name: Deploy templated logging config
      win_template:
        src: "{{ playbook_dir }}/../templates/{{ log_config_template | default('log4net.config.j2') }}"
        dest: "{{ app_config_dir | default('C:\\ProgramData\\Contoso\\config') }}\\log4net.config"
      vars:
        log_level: "{{ log_level | default('INFO') }}"
      notify: Restart ContosoService
      tags: [templates, config]

    - name: Place service config XML
      win_copy:
        src: "{{ playbook_dir }}/../files/configs/service.config.xml"
        dest: "{{ app_config_dir | default('C:\\ProgramData\\Contoso\\config') }}\\service.config.xml"
      notify: Restart ContosoService
      tags: [files, config]

    - name: Install MSI/EXE packages
      win_package:
        path: "{{ item.path }}"
        product_id: "{{ item.product_id | default(omit) }}"
        arguments: "{{ item.arguments | default(omit) }}"
        state: "{{ item.state | default('present') }}"
        creates_path: "{{ item.creates_path | default(omit) }}"
      loop: "{{ msi_packages }}"
      register: msi_results
      tags: [msi, apps]

    - name: Ensure Windows services are running
      win_service:
        name: "{{ item.name }}"
        state: "{{ item.state }}"
        start_mode: "{{ item.start_mode | default('auto') }}"
      loop: "{{ services }}"
      tags: [services]

    - name: Configure firewall rules
      when: firewall_enable | default(true)
      win_firewall_rule:
        name: "{{ item.name }}"
        localport: "{{ item.localport }}"
        protocol: "{{ item.protocol }}"
        direction: "{{ item.direction }}"
        action: "{{ item.action }}"
        enabled: "{{ item.enabled }}"
        profiles: "{{ item.profiles }}"
      loop: "{{ firewall_rules }}"
      tags: [firewall, security]

    - name: Deploy maintenance script
      win_copy:
        content: |
          param([string]$LogPath = "C:\\ProgramData\\Contoso\\logs")
          Get-ChildItem $LogPath -Filter *.log -Recurse |
            Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-14) } |
            Remove-Item -Force
        dest: "C:\\ProgramData\\Contoso\\scripts\\cleanup.ps1"
      tags: [scripts, maintenance]

    - name: Create scheduled tasks
      win_scheduled_task:
        name: "{{ item.name }}"
        description: "{{ item.description | default('') }}"
        actions: "{{ item.actions }}"
        triggers: "{{ item.triggers }}"
        username: "{{ item.username | default(omit) }}"
        run_level: "{{ item.run_level | default('highest') }}"
        state: present
        enabled: yes
      loop: "{{ scheduled_tasks }}"
      tags: [schedule, maintenance]

    - name: Reboot if the system requires it
      when: reboot_if_needed | default(true)
      win_reboot:
        reboot_timeout: 1200
        msg: "Rebooting after package installations/config changes."
      tags: [reboot]

  handlers:
    - name: Restart ContosoService
      win_service:
        name: "ContosoService"
        state: restarted

# Optional: appserver-specific play
- name: Appserver feature flags &amp; validation
  hosts: appservers
  gather_facts: no
  tasks:
    - name: Write feature flags JSON
      win_copy:
        content: "{{ app_feature_flags | to_nice_json }}"
        dest: "{{ app_config_dir }}\\features.json"
      notify: Restart ContosoService
      tags: [config]

    - name: Validate service responsive on port 8443
      win_shell: |
        Test-NetConnection -ComputerName $env:COMPUTERNAME -Port 8443 -InformationLevel Detailed
      args:
        executable: PowerShell
      register: port_check
      changed_when: false
      tags: [validate]

    - name: Show connectivity result
      debug:
        var: port_check
      tags: [validate]

  handlers:
    - name: Restart ContosoService
      win_service:
        name: "ContosoService"
        state: restartedExample for a baseline configuration of a Windows machine: ---
- name: Configure Windows OS baseline
  hosts: windows
  gather_facts: yes

  pre_tasks:
    - name: Show target info
      debug:
        msg: "Configuring {{ inventory_hostname }} (OS: {{ ansible_distribution }} {{ ansible_os_name }})"
      tags: [always]

  tasks:
    # Time zone &amp; NTP (time zone via module; NTP can be enforced via registry/commands)
    - name: Set system time zone
      win_timezone:
        timezone: "{{ desired_timezone }}"
      tags: [timezone]

    # PowerShell execution policy
    - name: Ensure PowerShell execution policy (Machine)
      win_regedit:
        path: 'HKLM:\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell'
        name: 'ExecutionPolicy'
        type: string
        data: "{{ powershell_execution_policy }}"
      tags: [policy, powershell]

    # Enable/Disable RDP
    - name: Configure RDP (Terminal Services)
      win_regedit:
        path: 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server'
        name: 'fDenyTSConnections'
        type: dword
        data: "{{ 0 if rdp_enabled else 1 }}"
      notify: Restart TermService
      tags: [rdp]

    # Windows Firewall baseline
    - name: Configure firewall rules
      win_firewall_rule:
        name: "{{ item.name }}"
        localport: "{{ item.localport }}"
        protocol: "{{ item.protocol }}"
        direction: "{{ item.direction }}"
        action: "{{ item.action }}"
        enabled: "{{ item.enabled }}"
        profiles: "{{ item.profiles }}"
      loop: "{{ firewall_rules }}"
      tags: [firewall]

    # Local groups
    - name: Ensure local groups exist
      win_group:
        name: "{{ item.name }}"
        description: "{{ item.description | default(omit) }}"
        state: "{{ item.state | default('present') }}"
      loop: "{{ local_groups }}"
      tags: [accounts, groups]

    - name: Ensure group membership
      win_group_membership:
        name: "{{ item.name }}"
        members: "{{ item.members | default([]) }}"
        state: present
      loop: "{{ local_groups | selectattr('members','defined') | list }}"
      tags: [accounts, groups]

    # Local user accounts
    - name: Ensure local users exist
      win_user:
        name: "{{ item.name }}"
        password: "{{ item.password | default(omit) }}"
        state: "{{ item.state | default('present') }}"
        groups: "{{ item.groups | default(omit) }}"
        password_never_expires: "{{ item.password_never_expires | default(false) }}"
        user_cannot_change_password: "{{ item.user_cannot_change_password | default(false) }}"
      loop: "{{ local_users }}"
      tags: [accounts, users]

    # Windows features
    - name: Ensure Windows features state
      win_feature:
        name: "{{ item.name }}"
        state: "{{ item.state }}"
      loop: "{{ windows_features }}"
      register: feature_changes
      tags: [features]

    # Registry hardening
    - name: Apply registry hardening
      win_regedit:
        path: "{{ item.path }}"
        name: "{{ item.name }}"
        type: "{{ item.type }}"
        data: "{{ item.value }}"
      loop: "{{ registry_hardening }}"
      register: reg_changes
      tags: [registry, security]

    # Power settings (example: disable sleep on AC)
    - name: Disable sleep when plugged in (AC)
      win_shell: |
        powercfg /change standby-timeout-ac 0
      args:
        executable: PowerShell
      changed_when: false
      tags: [power]

    # Services baseline (example: ensure Remote Registry disabled)
    - name: Disable Remote Registry service
      win_service:
        name: "RemoteRegistry"
        state: stopped
        start_mode: disabled
      tags: [services, security]

    # Scheduled tasks
    - name: Place cleanup script
      win_copy:
        content: |
          # Remove files older than 14 days from temp
          Get-ChildItem 'C:\Windows\Temp' -Recurse -Force |
            Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-14) } |
            Remove-Item -Force -ErrorAction SilentlyContinue
        dest: "C:\\Windows\\Temp\\cleanup.ps1"
      tags: [tasks]

    - name: Create scheduled tasks
      win_scheduled_task:
        name: "{{ item.name }}"
        description: "{{ item.description | default('') }}"
        actions: "{{ item.actions }}"
        triggers: "{{ item.triggers }}"
        username: "{{ item.username | default(omit) }}"
        run_level: "{{ item.run_level | default('highest') }}"
        state: present
        enabled: yes
      loop: "{{ scheduled_tasks }}"
      tags: [tasks]

    # Windows Update (configuration run; not app installs)
    - name: Install Windows updates (critical/security)
      win_updates:
        category_names: "{{ windows_update.category_names }}"
        state: "{{ windows_update.state }}"
        reboot: "{{ windows_update.reboot }}"
      register: update_result
      tags: [updates]

    # Validation checks
    - name: Validate RDP listening (if enabled)
      when: rdp_enabled
      win_shell: |
        Test-NetConnection -ComputerName $env:COMPUTERNAME -Port 3389 -InformationLevel Detailed
      args:
        executable: PowerShell
      register: rdp_port
      changed_when: false
      tags: [validate]

    - name: Show validation outputs
      debug:
        msg:
          - "Features changed: {{ feature_changes.results | selectattr('changed') | list | length }} host operations"
          - "Registry changes applied: {{ reg_changes.results | selectattr('changed') | list | length }}"
          - "Updates installed: {{ update_result.installed_update_count | default('n/a') }}"
          - "RDP port check (if enabled): {{ rdp_port.TcpTestSucceeded | default('n/a') }}"
      tags: [validate]

    # Reboot only if needed (modules signal this)
    - name: Reboot if required
      when: reboot_if_needed | default(true)
      win_reboot:
        reboot_timeout: 1800
        msg: "Baseline configuration applied. Rebooting if required."
      tags: [reboot]

  handlers:
    - name: Restart TermService
      win_service:
        name: "TermService"
        state: restartedThese snippets cover only a tiny part of Ansible's power. Because of this power, every IaC deployment strategy must include Ansible to be successful.  Theory: Deploying a Citrix Virtual Apps and Desktops or Citrix DaaS Environment using TerraformOverviewUsing the Triad allows for a start-to-end IaC strategy. Combining the benefits of all three toolkits in the right order brings you to success when deploying a Citrix CVAD or Citrix DaaS environment. We use Hashicorp Packer as the framework for generating the Master Images and templates for deploying all needed Windows-based virtual machines on XenServer 8.4, Hashicorp Terraform as the backbone and “coordinating” framework for creating all needed Infrastructure components, and Ansible as the configuration framework for all configuration needs of the components that Terraform cannot do during the initial creation of the respective element. All needed Automation components were installed on an Ubuntu-based VM cluster.  If all IaC frameworks reside on the same machine/cluster, communication between them is less error-prone. Example flow for deploying a Citrix CVAD infrastructure:  Important There are many ways to create a Citrix CVAD or Citrix DaaS environment across different Hypervisors using Terraform. Listing all possibilities, snippets, and runtime outputs is out of scope. Please consult the documentation at https://github.com/citrix/terraform-provider-citrix/tree/main/docs/resources for other examples. In the following examples, we will: Focus on XenServer 8.4 for deploying a Citrix CVAD environment and all relevant entities, as it is the most requested deployment option for customers Focus onAzure for deploying a Citrix DaaS environment and all relevant entities, as it is the most requested deployment option for customers  Examples from the Field - Creating a Citrix Virtual Apps and Desktops 2507 LTSR Deployment on XenServer 8.4Let´s have a look at another real-world example from the field - we cover the creation of a Citrix Virtual Apps and Desktops 2507 LTSR deployment on XenServer 8.4. All these examples have more detailed guides available in our Automation Landing Zone on Citrix TechZone at https://community.citrix.com/tech-zone/automation. Due to the size of the corresponding deployment guide, we created a dedicated page; you can find this example in the Citrix Automation Handbook 2601, Part 4.  Examples from the Field - Creating a Citrix DaaS Deployment on AzureLet´s have a look at another real-world example from the field - we cover the creation of a Citrix DaaS deployment on Azure. All these examples have more detailed guides available in our Automation Landing Zone on Citrix TechZone at https://community.citrix.com/tech-zone/automation Due to the size of the corresponding deployment guide, we created a dedicated page; you can find this example in the Citrix Automation Handbook 2601, Part 5.  Special Use Cases of the TriadUsing CI/CDUsing CI/CD is the enterprise-ready approach to creating, configuring, and administering your environment. In our demo environment, we have various CI/CD workflows configured - we already touched on some configuration examples earlier. Let´s see an example of using a GitHub workflow in action: Code snippet of a GitHub workflow for deploying a Master Image on Azure using Packer name: Packer building a Master Image on Azure

on:
  push:
    branches: [ "main" ]
  workflow_dispatch:

jobs:
  packer-build:
    runs-on: ubuntu-latest
    env:
      PKR_VAR_Azure_ClientID: ${{ secrets.AZURE_CLIENTID }}
      PKR_VAR_Azure_ClientSecret: ${{ secrets.AZURE_CLIENTSECRET }}
      PKR_VAR_Azure_SubscriptionID: ${{ secrets.AZURE_SUBSCRIPTIONID }}
      PKR_VAR_Azure_TenantID: ${{ secrets.AZURE_TENANTID }}
      PKR_VAR_Azure_RG: ${{ secrets.AZURE_RG }}
      PKR_VAR_Azure_TempRG: ${{ secrets.AZURE_TempRG }}

      PACKER_LOG: "1"
      PACKER_LOG_PATH: "packer.log"

    steps:
      - name: Checkout repo
        uses: actions/checkout@v4

      - name: Packer init
        run: packer init ./packer

      - name: Packer validate
        run: packer validate -var-file "./packer/win11-azure.auto.pkrvars.hcl" ./packer/win11-azure.pkr.hcl

      - name: Packer build
        run: packer build -on-error=abort -force -var-file="./packer/win11-azure.auto.pkrvars.hcl" ./packer/win11-azure.pkr.hclThe workflow can be triggered in various ways: manually, via a PR, or via a REST API call. In this case, we use a REST API call to trigger the run using the Postman app:  The return code 204 No Content indicates that GitHub has accepted the REST API call and triggered the workflow run. GitHub reflects the triggering with information about the run:   Current runner version: '2.329.0' 
Runner Image Provisioner 
Hosted Compute Agent 
Version: 20251124.448 
Commit: fda5086b43ec66ade217e5fcd18146c879571177 
Build Date: 2025-11-24T21:16:26Z 
Operating System 
Ubuntu 
24.04.3 
LTS 
Runner Image 
Image: ubuntu-24.04 
Version: 20251126.144.1 
Included Software: https://github.com/actions/runner-images/blob/ubuntu24/20251126.144/images/ubuntu/Ubuntu2404-Readme.md 
Image Release: https://github.com/actions/runner-images/releases/tag/ubuntu24%2F20251126.144 
GITHUB_TOKEN Permissions 
Contents: read 
Metadata: read 
Packages: read 
Secret source: Actions 
Prepare workflow directory 
Prepare all required actions 
Getting action download info 
Download action repository 'actions/checkout@v4' (SHA:34e114876b0b11c390a56381ad16ebd13914f8d5) 
Complete job name: packer-build 
0s
Run actions/checkout@v4 
Syncing repository: *** 
Getting Git version info 
Temporarily overriding HOME='/home/runner/work/_temp/72de3656-802c-4fea-b70f-fec21f79ffaa' before making global git config changes 
Adding repository directory to the temporary git global config as a safe directory 
/usr/bin/git config --global --add safe.directory /home/runner/work/tacg-packer-github/tacg-packer-github 
Deleting the contents of '/home/runner/work/tacg-packer-github/tacg-packer-github' 
Initializing the repository 
Disabling automatic garbage collection 
Setting up auth 
Fetching the repository 
Determining the checkout info 
/usr/bin/git sparse-checkout disable 
/usr/bin/git config --local --unset-all extensions.worktreeConfig 
Checking out the ref 
/usr/bin/git log -1 --format=%H 
2688fe1583b4385319cd928b763697e1ef8b4e06 
4s
Run packer init ./packer 
Installed plugin github.com/hashicorp/azure v2.5.0 in "/home/runner/.config/packer/plugins/github.com/hashicorp/azure/packer-plugin-azure_v2.5.0_x5.0_linux_amd64" 
1s
Run packer validate -var-file "./packer/win11-azure.auto.pkrvars.hcl" ./packer/win11-azure.pkr.hcl 
The configuration is valid. 
25m 59s
Run packer build -on-error=abort -force -var-file="./packer/win11-azure.auto.pkrvars.hcl" ./packer/win11-azure.pkr.hcl 
azure-arm.W11MIWitSWPackagesWithoutVDA: output will be in this color. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Running builder ... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Creating Azure Resource Manager (ARM) client ... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: ARM Client successfully created 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: the managed image named TMM_TF_SIG_PACKR_W11_MI already exists, but deleting it due to -force flag 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Getting source image id for the deployment ... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: -&gt; SourceImageName: '/subscriptions/&lt;sensitive&gt;/providers/Microsoft.Compute/locations/westeurope/publishers/MicrosoftWindowsDesktop/ArtifactTypes/vmimage/offers/office-365/skus/win11-25h2-avd-m365/versions/26200.7171.251111' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Using existing resource group ... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: -&gt; ResourceGroupName : '***' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: -&gt; Location : 'westeurope' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Validating deployment template ... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: -&gt; ResourceGroupName : '***' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: -&gt; DeploymentName : 'kvpkrdp635shf1s4d' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Deploying deployment template ... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: -&gt; ResourceGroupName : '***' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: -&gt; DeploymentName : 'kvpkrdp635shf1s4d' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Getting the certificate's URL ... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: -&gt; Key Vault Name : 'pkrkv635shf1s4d' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: -&gt; Key Vault Secret Name : 'packerKeyVaultSecret' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: -&gt; Certificate URL : '***' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Setting the certificate's URL ... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Validating deployment template ... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: -&gt; ResourceGroupName : '***' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: -&gt; DeploymentName : 'pkrdp635shf1s4d' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Deploying deployment template ... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: -&gt; ResourceGroupName : '***' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: -&gt; DeploymentName : 'pkrdp635shf1s4d' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Getting the VM's IP address ... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: -&gt; ResourceGroupName : '***' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: -&gt; PublicIPAddressName : 'pkrip635shf1s4d' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: -&gt; NicName : 'pkrni635shf1s4d' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: -&gt; Network Connection : 'PublicEndpoint' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: -&gt; IP Address : '20.61.110.209' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Waiting for WinRM to become available... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: WinRM connected. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Connected to WinRM! 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Provisioning with Powershell... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Provisioning with powershell script: /tmp/powershell-provisioner512226397 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Forcing web requests to allow TLS v1.2 (Required for requests to Chocolatey.org) 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Getting latest version of the Chocolatey package for download. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Not using proxy. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Getting Chocolatey from https://community.chocolatey.org/api/v2/package/chocolatey/2.6.0. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Downloading https://community.chocolatey.org/api/v2/package/chocolatey/2.6.0 to C:\Users\packer\AppData\Local\Temp\chocolatey\chocoInstall\chocolatey.zip 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Not using proxy. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Extracting C:\Users\packer\AppData\Local\Temp\chocolatey\chocoInstall\chocolatey.zip to C:\Users\packer\AppData\Local\Temp\chocolatey\chocoInstall 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Installing Chocolatey on the local machine 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Creating ChocolateyInstall as an environment variable (targeting 'Machine') 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Setting ChocolateyInstall to 'C:\ProgramData\chocolatey' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: WARNING: It's very likely you will need to close and reopen your shell 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: before you can use choco. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Restricting write permissions to Administrators 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: We are setting up the Chocolatey package repository. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: The packages themselves go to 'C:\ProgramData\chocolatey\lib' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: (i.e. C:\ProgramData\chocolatey\lib\yourPackageName). 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: A shim file for the command line goes to 'C:\ProgramData\chocolatey\bin' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: and points to an executable in 'C:\ProgramData\chocolatey\lib\yourPackageName'. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Creating Chocolatey CLI folders if they do not already exist. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: chocolatey.nupkg file not installed in lib. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Attempting to locate it from bootstrapper. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: PATH environment variable does not have C:\ProgramData\chocolatey\bin in it. Adding... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: WARNING: Not setting tab completion: Profile file does not exist at 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 'C:\Users\packer\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1'. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Chocolatey CLI (choco.exe) is now ready. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: You can call choco from anywhere, command line or PowerShell by typing choco. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Run choco /? for a list of functions. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: You may need to shut down and restart PowerShell and/or consoles 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: first prior to using choco. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Ensuring Chocolatey commands are on the path 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Ensuring chocolatey.nupkg is in the lib folder 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Uploading ./packer/SWPackagesToInstall.config =&gt; D:/SWPackagesToInstall.config 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Provisioning with Powershell... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Provisioning with powershell script: /tmp/powershell-provisioner249548071 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Chocolatey v2.6.0 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Installing from config file: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: D:/SWPackagesToInstall.config 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: By installing, you accept licenses for the packages. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Installing the following packages: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: powershell-core 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: openssh 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: firefox 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: googlechrome 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: rsat 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: filezilla 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: git 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: sql-server-management-studio 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 7zip 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: adobereader 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Downloading package from source 'https://community.chocolatey.org/api/v2/' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Progress: Downloading chocolatey-compatibility.extension 1.0.0... 100% 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: chocolatey-compatibility.extension v1.0.0 [Approved] 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: chocolatey-compatibility.extension package files install completed. Performing other installation steps. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Installed/updated chocolatey-compatibility extensions. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: The install of chocolatey-compatibility.extension was successful. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Deployed to 'C:\ProgramData\chocolatey\extensions\chocolatey-compatibility' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Downloading package from source 'https://community.chocolatey.org/api/v2/' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Progress: Downloading chocolatey-core.extension 1.4.0... 100% 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: chocolatey-core.extension v1.4.0 [Approved] 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: chocolatey-core.extension package files install completed. Performing other installation steps. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Installed/updated chocolatey-core extensions. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: The install of chocolatey-core.extension was successful. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Deployed to 'C:\ProgramData\chocolatey\extensions\chocolatey-core' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Downloading package from source 'https://community.chocolatey.org/api/v2/' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Progress: Downloading chocolatey-windowsupdate.extension 1.0.5... 100% 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: chocolatey-windowsupdate.extension v1.0.5 [Approved] 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: chocolatey-windowsupdate.extension package files install completed. Performing other installation steps. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Installed/updated chocolatey-windowsupdate extensions. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: The install of chocolatey-windowsupdate.extension was successful. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Deployed to 'C:\ProgramData\chocolatey\extensions\chocolatey-windowsupdate' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Downloading package from source 'https://community.chocolatey.org/api/v2/' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Progress: Downloading KB2919442 1.0.20160915... 100% 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: KB2919442 v1.0.20160915 [Approved] 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: KB2919442 package files install completed. Performing other installation steps. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Skipping installation because this hotfix only applies to Windows 8.1 and Windows Server 2012 R2. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: The install of KB2919442 was successful. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Software install location not explicitly set, it could be in package or 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: default install location of installer. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Downloading package from source 'https://community.chocolatey.org/api/v2/' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Progress: Downloading KB2919355 1.0.20160915... 100% 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: KB2919355 v1.0.20160915 [Approved] 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: KB2919355 package files install completed. Performing other installation steps. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Skipping installation because this hotfix only applies to Windows 8.1 and Windows Server 2012 R2. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: The install of KB2919355 was successful. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Software install location not explicitly set, it could be in package or 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: default install location of installer. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Downloading package from source 'https://community.chocolatey.org/api/v2/' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Progress: Downloading KB3118401 1.0.5... 100% 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: KB3118401 v1.0.5 [Approved] 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: KB3118401 package files install completed. Performing other installation steps. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Skipping installation because update KB3118401 does not apply to this operating system (Microsoft Windows 11 Enterprise multi-session). 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: The install of KB3118401 was successful. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Software install location not explicitly set, it could be in package or 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: default install location of installer. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Downloading package from source 'https://community.chocolatey.org/api/v2/' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Progress: Downloading powershell-core 7.5.4... 100% 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: powershell-core v7.5.4 [Approved] 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: powershell-core package files install completed. Performing other installation steps. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 7.5.4 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: WARNING: If you started this package under PowerShell core, replacing an in-use version may be unpredictable, require multiple attempts or produce errors. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Downloading powershell-core 64 bit 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: from 'https://github.com/PowerShell/PowerShell/releases/download/v7.5.4/PowerShell-7.5.4-win-x64.msi' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Progress: 100% - Completed download of C:\Users\packer\AppData\Local\Temp\chocolatey\powershell-core\7.5.4\PowerShell-7.5.4-win-x64.msi (107.9 MB). 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Download of PowerShell-7.5.4-win-x64.msi (107.9 MB) completed. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Hashes match. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Installing powershell-core... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: powershell-core has been installed. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: ************************************************************************************ 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: * INSTRUCTIONS: Your system default WINDOWS PowerShell version has not been changed. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: * PowerShell CORE 7.5.4, was installed to: "C:\Program Files\PowerShell\7" 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: * To start PowerShell Core 7.5.4, at a prompt or the start menu execute: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: * "pwsh.exe" 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: * Or start it from the desktop or start menu shortcut installed by this package. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: * This is your new default version of PowerShell CORE (pwsh.exe). 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: ************************************************************************************ 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: ************************************************************************************** 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: * As of OpenSSH 0.0.22.0 Universal Installer, a script is distributed that allows * 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: * setting the default shell for openssh. You could call it with code like this: * 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: * If (Test-Path "C:\Program Files\openssh-win64\Set-SSHDefaultShell.ps1") * 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: * {&amp; "C:\Program Files\openssh-win64\Set-SSHDefaultShell.ps1" [PARAMETERS]} * 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: * Learn more with this: * 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: * Get-Help "C:\Program Files\openssh-win64\Set-SSHDefaultShell.ps1" * 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: * Or here: * 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: * https://github.com/DarwinJS/ChocoPackages/blob/main/openssh/readme.md * 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: ************************************************************************************** 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: powershell-core may be able to be automatically uninstalled. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Environment Vars (like PATH) have changed. Close/reopen your shell to 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: see the changes (or in powershell/cmd.exe just type `refreshenv`). 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: The install of powershell-core was successful. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Software installed as 'msi', install location is likely default. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Downloading package from source 'https://community.chocolatey.org/api/v2/' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Progress: Downloading openssh 8.0.0.1... 100% 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: openssh v8.0.0.1 [Approved] 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: openssh package files install completed. Performing other installation steps. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Running on: Windows 10 Enterprise multi-session, (ServerRdsh) 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Windows Version: 10.0.26200 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: ************************************************************************************ 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: ************************************************************************************ 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: This package is a Universal Installer and can ALSO install Win32-OpenSSH on 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Nano, Server Core, Docker Containers and more WITHOUT using Chocolatey. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: See the following for more details: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: https://github.com/DarwinJS/ChocoPackages/blob/master/openssh/readme.md 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: ************************************************************************************ 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: ************************************************************************************ 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Extracting C:\ProgramData\chocolatey\lib\openssh\tools\OpenSSH-Win64.zip to C:\Users\packer\AppData\Local\Temp\chocolatey\OpenSSHTemp... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: C:\Users\packer\AppData\Local\Temp\chocolatey\OpenSSHTemp 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Hashes for internal source match 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: C:\Program Files\OpenSSH-Win64 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: C:\Program Files\OpenSSH-Win64\FixHostFilePermissions.ps1 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: C:\Program Files\OpenSSH-Win64\FixUserFilePermissions.ps1 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: C:\Program Files\OpenSSH-Win64\install-sshd.ps1 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: C:\Program Files\OpenSSH-Win64\libcrypto.dll 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: C:\Program Files\OpenSSH-Win64\openssh-events.man 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: C:\Program Files\OpenSSH-Win64\OpenSSHUtils.psd1 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: C:\Program Files\OpenSSH-Win64\OpenSSHUtils.psm1 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: C:\Program Files\OpenSSH-Win64\scp.exe 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: C:\Program Files\OpenSSH-Win64\sftp-server.exe 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: C:\Program Files\OpenSSH-Win64\sftp.exe 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: C:\Program Files\OpenSSH-Win64\ssh-add.exe 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: C:\Program Files\OpenSSH-Win64\ssh-agent.exe 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: C:\Program Files\OpenSSH-Win64\ssh-keygen.exe 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: C:\Program Files\OpenSSH-Win64\ssh-keyscan.exe 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: C:\Program Files\OpenSSH-Win64\ssh-shellhost.exe 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: C:\Program Files\OpenSSH-Win64\ssh.exe 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: C:\Program Files\OpenSSH-Win64\sshd.exe 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: C:\Program Files\OpenSSH-Win64\sshd_config_default 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: C:\Program Files\OpenSSH-Win64\uninstall-sshd.ps1 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: C:\Program Files\OpenSSH-Win64\Set-SSHDefaultShell.ps1 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: PATH environment variable does not have C:\Program Files\OpenSSH-Win64 in it. Adding... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Updating machine environment variable TERM from "" to "" 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: NEW VERSIONS OF SSH EXES: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: WARNING: You must start a new prompt, or use the command 'refreshenv' (provided by your chocolatey install) to re-read the environment for the tools to be available in this shell session. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Environment Vars (like PATH) have changed. Close/reopen your shell to 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: see the changes (or in powershell/cmd.exe just type `refreshenv`). 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: The install of openssh was successful. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Deployed to 'C:\Users\packer\AppData\Local\Temp\chocolatey\OpenSSHTemp' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Downloading package from source 'https://community.chocolatey.org/api/v2/' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Progress: Downloading Firefox 145.0.2... 100% 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Firefox v145.0.2 [Approved] 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Firefox package files install completed. Performing other installation steps. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Using locale 'en-US'... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Downloading Firefox 64 bit 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: from 'https://download.mozilla.org/?product=firefox-145.0.2-ssl&amp;os=win64&amp;lang=en-US' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Progress: 100% - Completed download of C:\Users\packer\AppData\Local\Temp\chocolatey\Firefox\145.0.2\Firefox Setup 145.0.2.exe (81.93 MB). 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Download of Firefox Setup 145.0.2.exe (81.93 MB) completed. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Hashes match. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Installing Firefox... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Firefox has been installed. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: WARNING: No registry key found based on 'Mozilla Firefox' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Firefox may be able to be automatically uninstalled. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: The install of Firefox was successful. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Deployed to 'C:\Program Files\Mozilla Firefox' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Downloading package from source 'https://community.chocolatey.org/api/v2/' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Progress: Downloading GoogleChrome 143.0.7499.41... 100% 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: GoogleChrome v143.0.7499.41 [Approved] 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: GoogleChrome package files install completed. Performing other installation steps. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Downloading googlechrome 64 bit 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: from 'https://dl.google.com/dl/chrome/install/googlechromestandaloneenterprise64.msi' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Download of googlechromestandaloneenterprise64.msi (-1 B) completed. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Hashes match. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Installing googlechrome... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: googlechrome has been installed. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: GoogleChrome may be able to be automatically uninstalled. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: The install of GoogleChrome was successful. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Software installed as 'MSI', install location is likely default. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Downloading package from source 'https://community.chocolatey.org/api/v2/' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Progress: Downloading RSAT 2.1809.1.20241223... 100% 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: rsat v2.1809.1.20241223 [Approved] 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: rsat package files install completed. Performing other installation steps. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Detected: Microsoft Windows 11 Enterprise multi-session 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Adding Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0 to Windows 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Progress: 100% - RunningWARNING: Access is denied. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: The install of rsat was successful. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Software install location not explicitly set, it could be in package or 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: default install location of installer. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Downloading package from source 'https://community.chocolatey.org/api/v2/' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Progress: Downloading filezilla 3.69.1... 100% 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: filezilla v3.69.1 [Approved] 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: filezilla package files install completed. Performing other installation steps. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Installing 64-bit filezilla... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: filezilla has been installed. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: filezilla may be able to be automatically uninstalled. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: The install of filezilla was successful. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Deployed to 'C:\Program Files\FileZilla FTP Client' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Downloading package from source 'https://community.chocolatey.org/api/v2/' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Progress: Downloading git.install 2.52.0... 100% 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: git.install v2.52.0 [Approved] 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: git.install package files install completed. Performing other installation steps. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Using Git LFS 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Installing 64-bit git.install... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: git.install has been installed. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: git.install installed to 'C:\Program Files\Git' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: git.install can be automatically uninstalled. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Environment Vars (like PATH) have changed. Close/reopen your shell to 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: see the changes (or in powershell/cmd.exe just type `refreshenv`). 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: The install of git.install was successful. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Deployed to 'C:\Program Files\Git\' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Downloading package from source 'https://community.chocolatey.org/api/v2/' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Progress: Downloading git 2.52.0... 100% 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: git v2.52.0 [Approved] 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: git package files install completed. Performing other installation steps. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: The install of git was successful. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Deployed to 'C:\ProgramData\chocolatey\lib\git' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Downloading package from source 'https://community.chocolatey.org/api/v2/' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Progress: Downloading chocolatey-dotnetfx.extension 1.0.1... 100% 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: chocolatey-dotnetfx.extension v1.0.1 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: chocolatey-dotnetfx.extension package files install completed. Performing other installation steps. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Installed/updated chocolatey-dotnetfx extensions. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: The install of chocolatey-dotnetfx.extension was successful. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Deployed to 'C:\ProgramData\chocolatey\extensions\chocolatey-dotnetfx' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Downloading package from source 'https://community.chocolatey.org/api/v2/' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Progress: Downloading chocolatey-visualstudio.extension 1.13.0... 100% 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: chocolatey-visualstudio.extension v1.13.0 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: chocolatey-visualstudio.extension package files install completed. Performing other installation steps. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Installed/updated chocolatey-visualstudio extensions. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: The install of chocolatey-visualstudio.extension was successful. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Deployed to 'C:\ProgramData\chocolatey\extensions\chocolatey-visualstudio' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Downloading package from source 'https://community.chocolatey.org/api/v2/' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Progress: Downloading dotnetfx 4.8.0.20220524... 100% 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: dotnetfx v4.8.0.20220524 [Approved] 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: dotnetfx package files install completed. Performing other installation steps. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Microsoft .NET Framework 4.8 or later is already installed. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: The install of dotnetfx was successful. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Software install location not explicitly set, it could be in package or 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: default install location of installer. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Downloading package from source 'https://community.chocolatey.org/api/v2/' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Progress: Downloading sql-server-management-studio 22.0.0... 100% 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: sql-server-management-studio v22.0.0 [Approved] 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: sql-server-management-studio package files install completed. Performing other installation steps. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Downloading channel manifest 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: from 'https://aka.ms/ssms/22/release/channel' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Progress: 100% - Completed download of C:\Users\packer\AppData\Local\Temp\chocolatey\chocolatey-visualstudio.extension\ChannelManifest_-2000454548.man (44.57 KB). 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Download of ChannelManifest_-2000454548.man (44.57 KB) completed. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Downloading catalog manifest 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: from 'https://download.visualstudio.microsoft.com/download/pr/d3b4e0f6-4bc0-4ec0-ba9c-20b355d61cc4/ecaa0d29877aed722c085ea92a7dc206515b627f8f653512024cc6c58d80e6d3/SSMS.vsman' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Progress: 100% - Completed download of C:\Users\packer\AppData\Local\Temp\chocolatey\chocolatey-visualstudio.extension\Catalog_1806514720.man (9.45 MB). 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Download of Catalog_1806514720.man (9.45 MB) completed. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Downloading sql-server-management-studio 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: from 'https://download.visualstudio.microsoft.com/download/pr/d3b4e0f6-4bc0-4ec0-ba9c-20b355d61cc4/41b0ec8225d7d6a773abc363b0fb90a9443072e49fa4619271220e244b11f832/vs_SSMS.exe' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Progress: 100% - Completed download of C:\Users\packer\AppData\Local\Temp\chocolatey\sql-server-management-studio\22.0.0\vs_SSMS.exe (4.21 MB). 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Download of vs_SSMS.exe (4.21 MB) completed. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Hashes match. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Installing sql-server-management-studio... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\HelpFile\2052\help.html... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\HelpFile\1028\help.html... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\HelpFile\1029\help.html... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\HelpFile\1033\help.html... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\HelpFile\1046\help.html... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\HelpFile\1055\help.html... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\HelpFile\1042\help.html... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\HelpFile\1040\help.html... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\HelpFile\3082\help.html... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\HelpFile\1031\help.html... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\HelpFile\1036\help.html... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\HelpFile\1041\help.html... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\HelpFile\1049\help.html... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\HelpFile\1045\help.html... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\vs_setup_bootstrapper.exe... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\Microsoft.C2RSignatureReader.Interop.dll... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\Microsoft.C2RSignatureReader.Native.dll... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\Microsoft.Identity.Client.Broker.dll... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\Microsoft.Identity.Client.dll... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\Microsoft.Identity.Client.Extensions.Msal.dll... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\Microsoft.Identity.Client.NativeInterop.dll... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\Microsoft.IdentityModel.Abstractions.dll... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\Microsoft.VisualStudio.RemoteControl.dll... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\Microsoft.VisualStudio.Setup.Common.dll... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\Microsoft.VisualStudio.Setup.dll... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\Microsoft.VisualStudio.Setup.Download.dll... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\Microsoft.VisualStudio.Telemetry.dll... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\Microsoft.VisualStudio.Utilities.Internal.dll... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\runtimes\win-x64\native\msalruntime.dll... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\runtimes\win-arm64\native\msalruntime_arm64.dll... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\runtimes\win-x86\native\msalruntime_x86.dll... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\Newtonsoft.Json.dll... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\System.Memory.dll... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\System.Runtime.CompilerServices.Unsafe.dll... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\VSInstallerElevationService.Contracts.dll... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\zh-Hant\vs_setup_bootstrapper.resources.dll... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\ko\vs_setup_bootstrapper.resources.dll... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\ja\vs_setup_bootstrapper.resources.dll... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\pt-BR\vs_setup_bootstrapper.resources.dll... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\tr\vs_setup_bootstrapper.resources.dll... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\cs\vs_setup_bootstrapper.resources.dll... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\de\vs_setup_bootstrapper.resources.dll... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\fr\vs_setup_bootstrapper.resources.dll... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\es\vs_setup_bootstrapper.resources.dll... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\it\vs_setup_bootstrapper.resources.dll... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\pl\vs_setup_bootstrapper.resources.dll... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\ru\vs_setup_bootstrapper.resources.dll... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\zh-Hans\vs_setup_bootstrapper.resources.dll... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\vs_setup_bootstrapper.config... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\vs_setup_bootstrapper.exe.config... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\detection.json... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Preparing: C:\Users\packer\AppData\Local\Temp\chocolatey\8c41d392761dead58726e9a5b8\vs_bootstrapper_d15\vs_setup_bootstrapper.json... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: sql-server-management-studio has been installed. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: sql-server-management-studio may be able to be automatically uninstalled. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: The install of sql-server-management-studio was successful. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Deployed to '"C:\Program Files (x86)\Microsoft Visual Studio\Installer"' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Downloading package from source 'https://community.chocolatey.org/api/v2/' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Progress: Downloading 7zip.install 25.1.0... 100% 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 7zip.install v25.1.0 [Approved] 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 7zip.install package files install completed. Performing other installation steps. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Installing 64 bit version 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Installing 7zip.install... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 7zip.install has been installed. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 7zip installed to 'C:\Program Files\7-Zip' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Added C:\ProgramData\chocolatey\bin\7z.exe shim pointed to 'c:\program files\7-zip\7z.exe'. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 7zip.install can be automatically uninstalled. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: The install of 7zip.install was successful. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Deployed to 'C:\Program Files\7-Zip\' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Downloading package from source 'https://community.chocolatey.org/api/v2/' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Progress: Downloading 7zip 25.1.0... 100% 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 7zip v25.1.0 [Approved] 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 7zip package files install completed. Performing other installation steps. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: The install of 7zip was successful. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Deployed to 'C:\ProgramData\chocolatey\lib\7zip' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Downloading package from source 'https://community.chocolatey.org/api/v2/' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Progress: Downloading adobereader 2025.1.20937... 100% 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: adobereader v2025.1.20937 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: adobereader package files install completed. Performing other installation steps. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: WARNING: No registry key found based on 'Adobe Acrobat*' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Configuring manual update checks and installs. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Downloading adobereader 64 bit 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: from 'https://ardownload3.adobe.com/pub/adobe/acrobat/win/AcrobatDC/2500120937/AcroRdrDCx642500120937_MUI.exe' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Progress: 100% - Completed download of C:\Users\packer\AppData\Local\Temp\chocolatey\AcroRdrDCx642500120937_MUI.exe (805.13 MB). 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Download of AcroRdrDCx642500120937_MUI.exe (805.13 MB) completed. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Hashes match. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Installing adobereader (installer)... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: adobereader (installer) has been installed. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Progress: 100% - 2/2 completed adobereader may be able to be automatically uninstalled. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: The install of adobereader was successful. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Deployed to 'C:\Program Files\Adobe\Acrobat DC\' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Chocolatey installed 21/21 packages. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: See the log for details (C:\ProgramData\chocolatey\logs\chocolatey.log). 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Installed: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: - 7zip v25.1.0 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: - 7zip.install v25.1.0 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: - adobereader v2025.1.20937 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: - chocolatey-compatibility.extension v1.0.0 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: - chocolatey-core.extension v1.4.0 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: - chocolatey-dotnetfx.extension v1.0.1 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: - chocolatey-visualstudio.extension v1.13.0 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: - chocolatey-windowsupdate.extension v1.0.5 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: - dotnetfx v4.8.0.20220524 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: - filezilla v3.69.1 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: - Firefox v145.0.2 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: - git v2.52.0 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: - git.install v2.52.0 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: - GoogleChrome v143.0.7499.41 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: - KB2919355 v1.0.20160915 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: - KB2919442 v1.0.20160915 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: - KB3118401 v1.0.5 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: - openssh v8.0.0.1 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: - powershell-core v7.5.4 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: - rsat v2.1809.1.20241223 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: - sql-server-management-studio v22.0.0 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Restarting Machine 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Waiting for machine to restart... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: A system shutdown is in progress.(1115) 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: pkrvm635shf1s4d restarted. 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Machine successfully restarted, moving on 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Provisioning with Powershell... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Provisioning with powershell script: /tmp/powershell-provisioner2853369393 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: IMAGE_STATE_COMPLETE 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Querying the machine's properties ... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: -&gt; ResourceGroupName : '***' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: -&gt; ComputeName : 'pkrvm635shf1s4d' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: -&gt; Managed OS Disk : '/subscriptions/&lt;sensitive&gt;/resourceGroups/***/providers/Microsoft.Compute/disks/pkros635shf1s4d' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Querying the machine's additional disks properties ... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: -&gt; ResourceGroupName : '***' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: -&gt; ComputeName : 'pkrvm635shf1s4d' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Powering off machine ... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: -&gt; ResourceGroupName : '***' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: -&gt; ComputeName : 'pkrvm635shf1s4d' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: -&gt; Compute ResourceGroupName : '***' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: -&gt; Compute Name : 'pkrvm635shf1s4d' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: -&gt; Compute Location : 'westeurope' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Generalizing machine ... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Capturing image ... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: -&gt; Image ResourceGroupName : '***' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: -&gt; Image Name : 'TMM_TF_SIG_PACKR_W11_MI' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: -&gt; Image Location : 'westeurope' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Deleting Virtual Machine deployment and its attached resources... 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Deleted -&gt; Microsoft.Compute/virtualMachines : 'pkrvm635shf1s4d' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Deleted -&gt; Microsoft.Network/networkInterfaces : 'pkrni635shf1s4d' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Deleted -&gt; Microsoft.Network/virtualNetworks : 'pkrvn635shf1s4d' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Deleted -&gt; Microsoft.Network/publicIPAddresses : 'pkrip635shf1s4d' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Deleted -&gt; Microsoft.Network/networkSecurityGroups : 'pkrsg635shf1s4d' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Deleted -&gt; Microsoft.Compute/disks : '/subscriptions/&lt;sensitive&gt;/resourceGroups/***/providers/Microsoft.Compute/disks/pkros635shf1s4d' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Removing the created Deployment object: 'pkrdp635shf1s4d' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Deleting KeyVault created during build 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Deleted -&gt; Microsoft.KeyVault/vaults : 'pkrkv635shf1s4d' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Removing the created Deployment object: 'kvpkrdp635shf1s4d' 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: 
==&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: The resource group was not created by Packer, not deleting ... 
Build 'azure-arm.W11MIWitSWPackagesWithoutVDA' finished after 25 minutes 58 seconds. 
==&gt; Wait completed after 25 minutes 58 seconds 
==&gt; Builds finished. The artifacts of successful builds are: 
--&gt; azure-arm.W11MIWitSWPackagesWithoutVDA: Azure.ResourceManagement.VMImage: 
OSType: Windows 
ManagedImageResourceGroupName: *** 
ManagedImageName: TMM_TF_SIG_PACKR_W11_MI 
ManagedImageId: /subscriptions/&lt;sensitive&gt;/resourceGroups/***/providers/Microsoft.Compute/images/TMM_TF_SIG_PACKR_W11_MI 
ManagedImageLocation: westeurope 
0s
Post job cleanup. 
/usr/bin/git version 
git version 2.52.0 
Temporarily overriding HOME='/home/runner/work/_temp/2a3532df-c470-48e6-9814-538e48ab6811' before making global git config changes 
Adding repository directory to the temporary git global config as a safe directory 
/usr/bin/git config --global --add safe.directory /home/runner/work/tacg-packer-github/tacg-packer-github 
/usr/bin/git config --local --name-only --get-regexp core\.sshCommand 
/usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'core\.sshCommand' &amp;&amp; git config --local --unset-all 'core.sshCommand' || :" 
/usr/bin/git config --local --name-only --get-regexp http\.https\:\/\/github\.com\/\.extraheader 
http.https://github.com/.extraheader 
/usr/bin/git config --local --unset-all http.https://github.com/.extraheader 
/usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'http\.https\:\/\/github\.com\/\.extraheader' &amp;&amp; git config --local --unset-all 'http.https://github.com/.extraheader' || :" 
/usr/bin/git config --local --name-only --get-regexp ^includeIf\.gitdir: 
/usr/bin/git submodule foreach --recursive git config --local --show-origin --name-only --get-regexp remote.origin.url 
0s
Cleaning up orphan processesGitHub has successfully created a Master Image with installed software on Azure. The same flow also applies to all other configured REST-API calls. The same kind of workflows can, of course, be used to trigger all other examples mentioned in the handbook, such as the deployment of entire infrastructures or only discrete entities like Machine Catalogs, Delivery Groups, etc. Tip Using the REST API and GitHub workflows, you can trigger the workflows from any application, as we do in our self-service portal.  Example Application to showcase using an IaC framework built on the TriadTo summarize and demonstrate all the benefits of IaC and automation outlined in this handbook, we created a Web-based service portal applicationthat lets you use Terraform, Ansible, and Packer together to perform various tasks. The application is built into our leading demo company, WorldWide Co. WWCo has its backend infrastructure entirely on Azure, so our application executes all instructions and operations in Azure.  GitHub actions support all these functionalities.  The application currently has these functionalities implemented – they are constantly being expanded: Using Packer for Master Image creation Creation of Master Images based on different Operating systems Automatic installation of Software packages using Chocolatey Different types of master images can be created: VHD Managed image Image version stored in an Azure Compute Gallery  Using Ansible to deploy Software packages on existing images/machines  Using Ansible to configure baseline OS settings on existing images/machines  Using Terraform to deploy infrastructure components or whole infrastructures All Citrix CVAD entities All Citrix DaaS entities A complete Azure-based Citrix CVAD infrastructure A complete Citrix Azure-based DaaS infrastructure  Using Terraform to alter infrastructure components  Using Terraform to demonstrate Day 2 administrative operations: Checking for changes in the desired configuration alerting Alerting Self-healing Changing the settings of VDI worker machines Changing the settings of machine catalogs Increasing the machine count in the machine catalog Changing the image version of the machine catalog Changing the settings of delivery groups Assigning new machines to the delivery group Adding new users to the delivery group Changing Autoscale™ settings The application was developed to provide inspiration for integrating IaC into your own environments and to facilitate entry into the world of automation and Infrastructure-as-code. Be inspired by the sheer endless possibilities and implement parts of it in your infrastructure.  Simplification of Administrative Efforts/“Day Two” OperationsThe Triad can be leveraged to simplify administrative efforts. We have already touched on some of these points, but let's summarize them again here together Importing an existing infrastructure into IaCWe have discussed that topic already in Part 1 of the Citrix Automation Handbook - Integrate an existing Citrix Environment into your Automation Strategy Check for configuration drifts and get notificationsUsing Terraform´s or Ansible´s idempotency, you can create a task to run the respective snippet and check for changes. Implement drift detection (e.g., terraform plan in scheduled tasks or cron jobs)  Using scheduled tasks or cron jobs, along with an advanced notification mechanism, lets you detect infrastructure changes and respond promptly. Example: The output of the Config Drift Notification script shows the changes: PS C:\_TERRAFORM\&gt; .\config-drift.ps1
Terraform Config Drift Detected:
 ---------------------------------------------------------------------------------------------------

  # citrix_delivery_group.CreateDG has changed
  ~ resource "citrix_delivery_group" "CreateDG" {
      ~ autoscale_settings          = {
          ~ autoscale_enabled                                     = true -&gt; false
            # (22 unchanged attributes hidden)
        }
        id                          = "3dee598d-3a66-4a43-b3ce-1d989e75a3c4"
        name                        = "DG-TMM-GK-TF-Azure"
        # (12 unchanged attributes hidden)
    }You can hook onto the output and take further steps. Example for combining the check for changes and the CVAD/DaaS feature Configuration Logging: You must install the Citrix Remote PowerShell SDK to retrieve the Configuration Logging events. Add-PSSnapin Citrix.*
$Date = Get-Date -Format "yyyy-MM-dd"
$OutDirFull = "C:\_TEMP\ConfigDrift\" + $Date
$StartRange = $Date + " 00:00"
$EndRange = $Date + " 23:59"
Set-XDCredentials -StoreAs TACG -ProfileType CloudApi -CustomerId xXxXxXxXxXxXx -APIKey yYyYyYyYyYyYyYy -SecretKey zZzZzZzZzZzZzZz
Get-XDAuthentication -ProfileName TACG
New-Item -Path $OutDirFull -ItemType "directory" -Force
Export-LogReportHtml -OutputDirectory $OutDirFull -StartDateRange $StartRange -EndDateRange $EndRangeIt enables you to get an overview of who did what when, making it easier to track. We created a simple script to get a notification out via email and on a specific Teams channel:   Altering Infrastructure components like VM sizesYou can easily adjust your infrastructure components, e.g., if you need more compute power for your VMs, if you followed your advice to use variables and do not hard-code values in your Terraform snippets by changing the corresponding value: Example: Changing the VM size of your Cloud Connector-VMs on Azure Changing from ...
   "TACG-TMM-CCX-VM-Size":"Standard_D2s_v5",
...to ...
   "TACG-TMM-CCX-VM-Size":"Standard_D4s_v5",
...allows you to simply adjust the value by running the same script as you would for an initial deployment. Terraform detects the wanted change and acts accordingly: PS C:\_TERRAFORM\ChangeVMSize&gt;terraform apply
...
Terraform will perform the following actions:

  # azurerm_windows_virtual_machine.TACG-TMM-CC1 will be updated in-place
  ~ resource "azurerm_windows_virtual_machine" "TACG-TMM-CC1" {
        id                                                     = "/subscriptions/.../Microsoft.Compute/virtualMachines/TACG-TMM-TF-CC1"
        name                                                   = "TACG-TMM-TF-CC1"
      ~ size                                                   = "Standard_D2s_v5" -&gt; "Standard_D4s_v5"
        tags                                                   = {
            "Environment" = "TMM-Gerhard"
        }
        # (41 unchanged attributes hidden)

        # (2 unchanged blocks hidden)
    }

  # azurerm_windows_virtual_machine.TACG-TMM-CC2 will be updated in-place
  ~ resource "azurerm_windows_virtual_machine" "TACG-TMM-CC2" {
        id                                                     = "/subscriptions/.../Microsoft.Compute/virtualMachines/TACG-TMM-TF-CC2"
        name                                                   = "TACG-TMM-TF-CC2"
      ~ size                                                   = "Standard_D2s_v5" -&gt; "Standard_D4s_v5"
        tags                                                   = {
            "Environment" = "TMM-Gerhard"
        }
        # (41 unchanged attributes hidden)

        # (2 unchanged blocks hidden)
    }

Plan: 0 to add, 2 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

azurerm_windows_virtual_machine.TACG-TMM-CC2: Modifying... [id=/subscriptions/.../Microsoft.Compute/virtualMachines/TACG-TMM-TF-CC2]
azurerm_windows_virtual_machine.TACG-TMM-CC1: Modifying... [id=/subscriptions.../Microsoft.Compute/virtualMachines/TACG-TMM-TF-CC1]
azurerm_windows_virtual_machine.TACG-TMM-CC1: Still modifying... [id=/subscriptions/....ompute/virtualMachines/TACG-TMM-TF-CC1, 10s elapsed]
azurerm_windows_virtual_machine.TACG-TMM-CC2: Still modifying... [id=/subscriptions/....ompute/virtualMachines/TACG-TMM-TF-CC2, 10s elapsed]
azurerm_windows_virtual_machine.TACG-TMM-CC1: Modifications complete after 12s [id=/subscriptions/.../Microsoft.Compute/virtualMachines/TACG-TMM-TF-CC1]
azurerm_windows_virtual_machine.TACG-TMM-CC2: Modifications complete after 12s [id=/subscriptions/.../Microsoft.Compute/virtualMachines/TACG-TMM-TF-CC2]

Apply complete! Resources: 0 added, 2 changed, 0 destroyed.

PS C:\_TERRAFORM\ChangeVMSize&gt;You can see that 41 attributes were not touched; only the VM sizes of each VM were updated. Altering Citrix entities: Updating the Machine Catalog with a new Image versionIn this example, we will update a Machine Catalog and deploy a new version of the Master Image. The master image version of the machine catalog is stored in a variable: ...
	azure_master_image = {
                resource_group       = var.CC-Azure-MCImgDef-MasterImage-RG
                master_image         = var.CC-Azure-MCImgDef-MasterImage
            }
...Changing from ...
   "CC-Azure-MCImgDef-MasterImage":"W11-BaseImageDefinition-1-baseDisk-3w0te",
...to ...
   "CC-Azure-MCImgDef-MasterImage":"W11-BaseImageDefinition-3-baseDisk-my3dm",
...allows you to simply adjust the value by running the same script as you would for an initial deployment. Terraform detects the wanted change and acts accordingly: PS C:\_TERRAFORM\ChangeMCImage&gt; terraform apply
...

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # citrix_machine_catalog.CreateAzureImgDefMCSCatalog will be updated in-place
  ~ resource "citrix_machine_catalog" "CreateAzureImgDefMCSCatalog" {
        id                       = "c7364b7a-c045-41cc-af81-7a3da2c5d84f"
      ~ inherited_scopes         = [] -&gt; (known after apply)
        name                     = "MC-TMM-GK-TF-Azure-ImgDef-Win11"
      ~ provisioning_scheme      = {
          ~ azure_machine_config           = {
              ~ azure_master_image = {
                  ~ master_image   = "W11-BaseImageDefinition-1-baseDisk-3w0te" -&gt; "W11-BaseImageDefinition-3-baseDisk-my3dm"
                    # (1 unchanged attribute hidden)
                }
                # (5 unchanged attributes hidden)
            }
            # (6 unchanged attributes hidden)
        }
      + tenants                  = (known after apply)
        # (8 unchanged attributes hidden)
    }

Plan: 0 to add, 1 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_machine_catalog.CreateAzureImgDefMCSCatalog: Modifying... [id=c7364b7a-c045-41cc-af81-7a3da2c5d84f]
citrix_machine_catalog.CreateAzureImgDefMCSCatalog: Still modifying... [id=c7364b7a-c045-41cc-af81-7a3da2c5d84f, 10s elapsed]
citrix_machine_catalog.CreateAzureImgDefMCSCatalog: Still modifying... [id=c7364b7a-c045-41cc-af81-7a3da2c5d84f, 20s elapsed]
citrix_machine_catalog.CreateAzureImgDefMCSCatalog: Still modifying... [id=c7364b7a-c045-41cc-af81-7a3da2c5d84f, 30s elapsed]
...
citrix_machine_catalog.CreateAzureImgDefMCSCatalog: Still modifying... [id=c7364b7a-c045-41cc-af81-7a3da2c5d84f, 7m30s elapsed]
citrix_machine_catalog.CreateAzureImgDefMCSCatalog: Still modifying... [id=c7364b7a-c045-41cc-af81-7a3da2c5d84f, 7m40s elapsed]
citrix_machine_catalog.CreateAzureImgDefMCSCatalog: Still modifying... [id=c7364b7a-c045-41cc-af81-7a3da2c5d84f, 7m50s elapsed]
citrix_machine_catalog.CreateAzureImgDefMCSCatalog: Modifications complete after 7m53s [id=c7364b7a-c045-41cc-af81-7a3da2c5d84f]

Apply complete! Resources: 0 added, 1 changed, 0 destroyed.
PS C:\_TERRAFORM\ChangeMCImage&gt;Terraform has successfully updated the Machine Catalog. Altering Citrix Entities: Creating additional machines in the Machine Catalog and the adjacent Delivery GroupIn this example, we will add additional machines to the Machine Catalog and the Delivery Group. The number of machines in the Machine Catalog and the number of machines in the Delivery Group are stored in a variable: ...
# Machine Catalog setting	
number_of_total_machines       = var.CC-Azure-MCImgDef-NumberOfVMs

# Delivery Group Setting
...
 associated_machine_catalogs             = [
        {
            machine_catalog                 = citrix_machine_catalog.CreateAzureImgDefMCSCatalog.id
            machine_count                   = var.CC-Azure-MCImgDef-NumberOfVMs
        }
    ]
...Changing the value from ...
   "CC-Azure-MCImgDef-NumberOfVMs":50,
...to ...
   "CC-Azure-MCImgDef-NumberOfVMs":100,
...allows you to simply alter the entities running the same script as you would for an initial deployment. Terraform detects the wanted change and acts accordingly, as shown above - the output of this snippet is omitted due to resource scarcity. Altering Citrix Entities: Adding users to a Delivery GroupIn this example, we will add additional AD users/AD user groups to the Delivery Group. The allowed users in the Delivery Group are stored in a variable: ...
	desktops = [
    {
      published_name = var.TACG-TMM-CVAD-DG-Desktops-Name
      description    = var.TACG-TMM-CVAD-DG-Desktops-Description
      restricted_access_users = {
        allow_list = var.TACG-TMM-CVAD-DG-Desktops-AllowList
      }
      enabled = var.TACG-TMM-CVAD-DG-Desktops-Enabled
...
    }
...
  ]
...Changing the value from ...
  TACG-TMM-CVAD-DG-Desktops-AllowList = ["TACG\\vdaallowed"]
...to ...
   TACG-TMM-CVAD-DG-Desktops-AllowList = ["TACG\\vdaallowed","TACG\\temporaryallowedmanda"]
...allows you to simply add users to the adjacent Delivery Group. Since all configuration files in the IaC universe are text-based, you can edit them as needed. Our self-service portal application simply updates the required configuration files and, e.g., uploads them to the appropriate deployment locations. Explore the endless possibilities of using IaC and the triad to ease administrative operations in your infrastructure.  That completes Part 3 of the Citrix Automation Handbook. In the first part of the Citrix Automation Handbook, we discuss the Citrix Automation story and the basics of Infrastructure-as-Code, of Continuous Integration/Continuous Deployment (CI/CD) strategies, and our main IaC tools: Packer, Terraform, and Ansible. In the second part of the Citrix Automation Handbook, we focus on: Applying automation and infrastructure-as-code in the Citrix universe. We discuss all relevant components and provide code examples from real environments Best practises for designing an environment for automation and IaC use, and discuss deployment tips In this part, we focused on: Common and special use cases, as well as complete deployment examples from the field. In the fourth part of the Citrix Automation Handbook, we cover creating a Citrix Virtual Apps and Desktops 2507 LTSR deployment on XenServer 8.4. In the fifth part of the Citrix Automation Handbook, we cover creating a Citrix DaaS deployment on Azure. In the sixth part of the Citrix Automation Handbook, we cover some final topics.  DisclaimerMost important All code snippets mentioned in this handbook were thoroughly tested and run in a sandbox environment. All shown Packer, Terraform, Ansible Playbooks, and PowerShell scripts were tailored exclusively for the environment used for this handbook. You need to adjust all settings, values, snippets, etc., in the most suitable way tailored to your requirements, regulations, and existing environments. All settings, scripts, and code examples listed in this document are intended only to illustrate the possibilities and serve as a basis for your own deployment. We tried to show different ways, even if they were not always in line with best-practice guidelines, such as unencrypted WinRM communication for demonstration purposes. Using all the provided code snippets is at your own risk – please read the disclaimer below for further information.   EXCEPT WHERE EXPRESSLY PROVIDED OTHERWISE BY CLOUD SOFTWARE GROUP, THE CONTENT IN THIS DOCUMENT IS PROVIDED “AS IS” AND CLOUD SOFTWARE GROUP HEREBY DISCLAIMS ALL EXPRESS OR IMPLIED REPRESENTATIONS, WARRANTIES, GUARANTIES, AND CONDITIONS, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES OR CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD TO BE LEGALLY INVALID. CLOUD SOFTWARE GROUP MAKES NO REPRESENTATIONS, WARRANTIES, GUARANTIES, OR CONDITIONS AS TO THE QUALITY, SUITABILITY, TRUTH, ACCURACY, OR COMPLETENESS OF ANY OF THE CONTENT CONTAINED ON THE WEBSITE. The above disclaimer applies to any damages, liability, or injuries caused by any failure of performance, error, omission, interruption, deletion, defect, delay in operation or transmission, computer virus, communication line failure, theft or destruction of or unauthorized access to, alteration of, or use, whether for breach of contract, tort, negligence or any other cause of action.]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2026_01/audit.png.42fdc677aad6b9b5e75a2c0ac40a4cea.png" length="133394" type="image/png"/><pubDate>Wed, 21 Jan 2026 09:12:37 +0000</pubDate></item><item><title>The Citrix Automation Handbook 2601 - Part 2</title><link>https://community.stage.citrix.com/tech-zone/automation/automation-handbook-2601-part2/</link><description><![CDATA[The Citrix Automation Handbook - Part 2In the first part of the Citrix Automation Handbook, we discuss the Citrix Automation story and the basics of Infrastructure-as-Code, of Continuous Integration/Continuous Deployment (CI/CD) strategies, and our main IaC tools: Packer, Terraform, and Ansible. In this part of the Citrix Automation Handbook, we focus on: Applying automation and infrastructure-as-code in the Citrix universe. We discuss all relevant components and provide code examples from real environments Best practises for designing an environment for automation and IaC use, and discuss deployment tips In the third part of the Citrix Automation Handbook, we focus on common and special use cases, as well as complete deployment examples from the field. In the fourth part of the Citrix Automation Handbook, we cover creating a Citrix Virtual Apps and Desktops 2507 LTSR deployment on XenServer 8.4. In the fifth part of the Citrix Automation Handbook, we cover creating a Citrix DaaS deployment on Azure. In the sixth part of the Citrix Automation Handbook, we cover some final topics.  Part 2 As mentioned in the Automation Matrix at the beginning of the first part of the handbook, there are many different ways to automate Citrix components. Let´s have a look at possible automations. Important Please review the main concepts: Declarative (Functional) Approach (e.g., Terraform, Ansible): Focuses on the target configuration that should be achieved. Defines the desired state, and the system executes the steps needed to achieve it. Imperative (Procedural): Focuses on how the infrastructure is to be changed to meet this.  Defines specific commands that must be executed in the appropriate order to reach the desired conclusion. Idempotency (Terraform and Ansible):  You can run the same Terraform or Ansible snippet as often as you want/need – it will only change the infrastructure if the snippet or the infrastructure changes. Terraform and Ansible compare the code with the actual infrastructure. You receive the same result regardless of the execution count. Scripting (PowerShell, REST API): Each time you run a PowerShell script or invoke a REST API call, the engine tries to create the entities coded in the script. This can lead to unwanted resources or even crashes of the script. The engine itself does NOT compare the code with the actual infrastructure.  Most Important All code snippets mentioned in this handbook were thoroughly tested and run in a sandbox environment. All shown Terraform codes, Ansible Playbooks, and PowerShell scripts were tailored exclusively for the specific environment used for this handbook. You need to adjust all settings, values, snippets, etc., in the most suitable way tailored to your requirements, regulations, and existing environments. All settings, scripts, and code examples listed in this document are intended only to illustrate the possibilities and serve as a basis for your own deployment. We tried to show different ways, even if they were not always in line with best-practice guidelines, such as unencrypted WinRM communication for demonstration purposes. For some code snippets, we also show the console output during code execution to visualize backend processes for information purposes. Using all the provided code snippets is at your own risk – please read the disclaimer at the end of the document for further information.  Automation of Hypervisors/HyperscalersIf you want to automate the deployment or configuration of your Hypervisors/Hyperscalers, you can leverage the corresponding providers. Here´s a short matrix of supported toolkits:  Important There are many ways to automate and use IaC across different Hypervisors and Hyperscalers using Terraform and Ansible. Listing all possibilities, snippets, and runtime outputs is out of scope. Please consult the documentation at https://github.com/citrix/terraform-provider-citrix/tree/main/docs/resources for other examples. Example: Prepare Microsoft Azure for deploying a Citrix DaaS environment using Terraform### Create a Resource Group on Azure
#### https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/resource_group
resource "azurerm_resource_group" "TACG-AZ-V2-RG" {
  count = var.CreateRG ? 1 : 0
  location = "${var.RG-DeployLocation}"
  name     = "${var.RG-Name}"

  tags                           = {
                                        Environment = "${var.TAG-Environment}",
                                        Environment-Entity = "${var.TAG-Environment-Entity}",
                                        Environment-Usage = "${var.TAG-Environment-Usage}",
  }
}

### Create a Shared Image Gallery
#### https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/shared_image_gallery

data "azurerm_resource_group" "TMM-RG" {
  name                = "${var.RG-Name}"
}

resource "azurerm_shared_image_gallery" "TACG-AZ-V2-SIG" {
  name                = "${var.SIG-Name}"
  description         = "${var.SIG-Description}"
  resource_group_name = data.azurerm_resource_group.TMM-RG.name
  location            = data.azurerm_resource_group.TMM-RG.location
  tags                           = {
                                        Environment = "${var.TAG-Environment}",
                                        Environment-Entity = "${var.TAG-Environment-Entity}",
                                        Environment-Usage = "${var.TAG-Environment-Usage}",
  }
}

### Create an Image Definition for the Master Image
#### https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/shared_image
resource "azurerm_shared_image" "TACG-AZ-V2-SIG-IDW2K25M" {
  depends_on = [ azurerm_shared_image_gallery.TACG-AZ-V2-SIG ]
  name                = "${var.SIG-IDW2K25M-Name}"
  gallery_name        = azurerm_shared_image_gallery.TACG-AZ-V2-SIG.name
  resource_group_name = data.azurerm_resource_group.TMM-RG.name
  location            = data.azurerm_resource_group.TMM-RG.location
  os_type             = "${var.SIG-IDW2K25M-OsType}"
  hyper_v_generation  = "V2" 
  hibernation_enabled = true
  accelerated_network_support_enabled = true
  trusted_launch_enabled = false
  

  
  identifier {
    publisher = "${var.SIG-IDW2K25M-Publisher}"
    offer     = "${var.SIG-IDW2K25M-Offer}"
    sku       = "${var.SIG-IDW2K25M-SKU}"
  }

  tags                           = {
                                        Environment = "${var.TAG-Environment}",
                                        Environment-Entity = "${var.TAG-Environment-Entity}",
                                        Environment-Usage = "${var.TAG-Environment-Usage}",
  }
}

resource "azurerm_shared_image_version" "TACG-AZ-V2-SIG-IDW2K25M-ImgVersion" {
  depends_on = [ azurerm_shared_image.TACG-AZ-V2-SIG-IDW2K25M ]
  name                = "${var.SIG-IDW2K25M-Version}"
  gallery_name        = azurerm_shared_image_gallery.TACG-AZ-V2-SIG.name
  image_name          = "${var.SIG-IDW2K25M-Name}"
  resource_group_name = data.azurerm_resource_group.TMM-RG.name
  location            = data.azurerm_resource_group.TMM-RG.location
  managed_image_id    = "${var.SIG-IDW2K25M-ID}"

  target_region {
    name                   = data.azurerm_resource_group.TMM-RG.location
    regional_replica_count = 1
    storage_account_type   = "Standard_LRS"
  }

  tags                           = {
                                        Environment = "${var.TAG-Environment}",
                                        Environment-Entity = "${var.TAG-Environment-Entity}",
                                        Environment-Usage = "${var.TAG-Environment-Usage}",
  }
}

### We are using existing entitites like the Resource Group, NSGs, Firewall Rules, etc
#### Get existing entity data
data "azurerm_virtual_network" "TACG-TMM-VNet" {
  name                = "${var.TACG-TMM-VNet-Name}"
  resource_group_name = "${var.TACG-TMM-ResourceGroup-Name}"
}

data "azurerm_subnet" "TACG-TMM-Subnet" {
  name                 = "${var.TACG-TMM-Subnet-Name}"
  virtual_network_name = data.azurerm_virtual_network.TACG-TMM-VNet.name
  resource_group_name = "${var.TACG-TMM-ResourceGroup-Name}"
}

data "azurerm_network_security_group" "TACG-TMM-NSG" {
  name                 = "${var.TACG-TMM-NSG-Name}"
  resource_group_name = "${var.TACG-TMM-ResourceGroup-Name}"
}

output "VNet_ID" {
  value = data.azurerm_virtual_network.TACG-TMM-VNet.id
}

output "Subnet_id" {
  value = data.azurerm_subnet.TACG-TMM-Subnet.name
}

output "NSG_id" {
  value = data.azurerm_network_security_group.TACG-TMM-NSG.name
}

#### Create a new inbound rule in NSG to allow WinRM Communication
resource "azurerm_network_security_rule" "EnableWinRMInNSG" {
  depends_on = [ data.azurerm_network_security_group.TACG-TMM-NSG ]
   count = var.TACG-TMM-NSGRule-CreateRule ? 1 : 0
  name                        = "${var.TACG-TMM-NSGRule-Name}"
  priority                    = "${var.TACG-TMM-NSGRule-Priority}"
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "${var.TACG-TMM-NSGRule-InboundPorts}"
  source_address_prefix       = "VirtualNetwork"
  destination_address_prefix  = "VirtualNetwork"
  resource_group_name         = "${var.TACG-TMM-ResourceGroup-Name}"
  network_security_group_name = data.azurerm_network_security_group.TACG-TMM-NSG.name
}

#### Create a dedicated Network Interface and IP configuration for the CC1-VM
#### https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/network_interface
##### Set the values in the corresponding .auto.tfvars.json file
resource "azurerm_network_interface" "TACG-TMM-TF-CC1-NIC" {
  depends_on = [ azurerm_network_security_rule.EnableWinRMInNSG ]
  name                           = "${var.TACG-TMM-CC1-NIC-Name}"
  location                       = "${var.TACG-TMM-ResourceGroup-Location}"
  resource_group_name            = "${var.TACG-TMM-ResourceGroup-Name}"
  accelerated_networking_enabled = true
 # dns_servers                    = [ "10.53.8.29" ]
  dns_servers                    = data.azurerm_virtual_network.TACG-TMM-VNet.dns_servers
  tags                           = {
                                        Environment = "${var.TAG-Environment}",
                                        Environment-Entity = "${var.TAG-Environment-Entity}",
                                        Environment-Usage = "${var.TAG-Environment-Usage}",
  }

  ip_configuration {
    name                          = "${var.TACG-TMM-CC1-IPC-Name}"
    subnet_id                     = data.azurerm_subnet.TACG-TMM-Subnet.id
    private_ip_address_allocation = "${var.TACG-TMM-CC1-IPC-Private_IP_Address_Allocation}" 
    private_ip_address            = "${var.TACG-TMM-CC1-IPC-Private_IP_Address}"
  }
}

#### Create a dedicated Network Interface and IP configuration for the CC2-VM
#### https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/network_interface
##### Set the values in the corresponding .auto.tfvars.json file
resource "azurerm_network_interface" "TACG-TMM-TF-CC2-NIC" {
  depends_on = [ data.azurerm_subnet.TACG-TMM-Subnet ]
  name                           = "${var.TACG-TMM-CC2-NIC-Name}"
  location                       = "${var.TACG-TMM-ResourceGroup-Location}"
  resource_group_name            = "${var.TACG-TMM-ResourceGroup-Name}"
  accelerated_networking_enabled = true
  #dns_servers                    = [ "10.53.8.29" ]
  dns_servers                    = data.azurerm_virtual_network.TACG-TMM-VNet.dns_servers
  tags                           = {
                                        Environment = "${var.TAG-Environment}",
                                        Environment-Entity = "${var.TAG-Environment-Entity}",
                                        Environment-Usage = "${var.TAG-Environment-Usage}",
  }

  ip_configuration {
    name                          = "${var.TACG-TMM-CC2-IPC-Name}"
    subnet_id                     = data.azurerm_subnet.TACG-TMM-Subnet.id
    private_ip_address_allocation = "${var.TACG-TMM-CC2-IPC-Private_IP_Address_Allocation}" 
    private_ip_address            = "${var.TACG-TMM-CC2-IPC-Private_IP_Address}"
  }
}

#### Associate the CC1-Network Interface with an existing NSG
#### https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/network_security_group
##### Set the values in the corresponding .auto.tfvars.json file
resource "azurerm_network_interface_security_group_association" "TACG-TMM-TF-CC1-NICNSG" {
  depends_on = [ azurerm_network_interface.TACG-TMM-TF-CC1-NIC, data.azurerm_network_security_group.TACG-TMM-NSG ]
  network_interface_id      = azurerm_network_interface.TACG-TMM-TF-CC1-NIC.id
  network_security_group_id = data.azurerm_network_security_group.TACG-TMM-NSG.id
}

#### Associate the CC2-Network Interface with an existing NSG
#### https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/network_security_group
##### Set the values in the corresponding .auto.tfvars.json file
resource "azurerm_network_interface_security_group_association" "TACG-TMM-TF-CC2-NICNSG" {
  depends_on = [ azurerm_network_interface.TACG-TMM-TF-CC2-NIC, data.azurerm_network_security_group.TACG-TMM-NSG ]
  network_interface_id      = azurerm_network_interface.TACG-TMM-TF-CC2-NIC.id
  network_security_group_id = data.azurerm_network_security_group.TACG-TMM-NSG.id
}
#### Create the CC1-VM
#### https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/virtual_machine
##### Set the values in the corresponding .auto.tfvars.json file
resource "azurerm_windows_virtual_machine" "TACG-TMM-CC1" {
  depends_on = [ azurerm_network_interface_security_group_association.TACG-TMM-TF-CC1-NICNSG ]
  name                  = "${var.TACG-TMM-CC1-VM-Name}"
  location              = "${var.TACG-TMM-ResourceGroup-Location}"
  resource_group_name   = "${var.TACG-TMM-ResourceGroup-Name}"
  network_interface_ids = [azurerm_network_interface.TACG-TMM-TF-CC1-NIC.id]
  size                  = "${var.TACG-TMM-CCX-VM-Size}"
  admin_username        = "${var.TACG-TMM-CCX-VM-Loc-UN}"
  admin_password        = "${var.TACG-TMM-CCX-VM-Loc-PW}"
  provision_vm_agent    = true
  timezone              = "W. Europe Standard Time"
  patch_mode            = "AutomaticByOS"
  enable_automatic_updates = true
  reboot_setting        = "Never"
  hotpatching_enabled   = false
  secure_boot_enabled   = false
  source_image_id       = "${var.TACG-TMM-SharedImage-ID}"
 
  os_disk {
    name                 = "${var.TACG-TMM-CC1-VM-OSDrive-Name}"
    caching              = "${var.TACG-TMM-CCX-VM-OSDrive-Cache}"
    storage_account_type = "${var.TACG-TMM-CCX-VM-OSDrive-StorageType}"
  }

  tags                           = {
                                        Environment = "${var.TAG-Environment}",
                                        Environment-Entity = "${var.TAG-Environment-Entity}",
                                        Environment-Usage = "${var.TAG-Environment-Usage}",
  }
} 

#### Create the CC2-VM
#### https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/virtual_machine
##### Set the values in the corresponding .auto.tfvars.json file
resource "azurerm_windows_virtual_machine" "TACG-TMM-CC2" {
  depends_on = [ azurerm_network_interface_security_group_association.TACG-TMM-TF-CC2-NICNSG ]
  name                  = "${var.TACG-TMM-CC2-VM-Name}"
  location              = "${var.TACG-TMM-ResourceGroup-Location}"
  resource_group_name   = "${var.TACG-TMM-ResourceGroup-Name}"
  network_interface_ids = [azurerm_network_interface.TACG-TMM-TF-CC2-NIC.id]
  size                  = "${var.TACG-TMM-CCX-VM-Size}"
  admin_username        = "${var.TACG-TMM-CCX-VM-Loc-UN}"
  admin_password        = "${var.TACG-TMM-CCX-VM-Loc-PW}"
  provision_vm_agent    = true
  timezone              = "W. Europe Standard Time"
  patch_mode            = "AutomaticByOS"
  enable_automatic_updates = true
  reboot_setting        = "Never"
  hotpatching_enabled   = false
  secure_boot_enabled   = false
  source_image_id       = "${var.TACG-TMM-SharedImage-ID}"
  
  os_disk {
    name                 = "${var.TACG-TMM-CC2-VM-OSDrive-Name}"
    caching              = "${var.TACG-TMM-CCX-VM-OSDrive-Cache}"
    storage_account_type = "${var.TACG-TMM-CCX-VM-OSDrive-StorageType}"
  }

  tags                           = {
                                        Environment = "${var.TAG-Environment}",
                                        Environment-Entity = "${var.TAG-Environment-Entity}",
                                        Environment-Usage = "${var.TAG-Environment-Usage}",
  }
}
Note Please keep in mind that leveraging Terraform´s idempotency and declarative approach is the most flexible way of deploying infrastructure components. Example: Using Ansible to put a Virtual Machine into an AD DomainIn this example, we use the combination of Terraform and Ansible to join a newly created Virtual machine into an Active Directory domain. After deploying the VM using the Terraform snippet above, Terraform runs an Ansible Playbook to join the VM to the AD domain. Terraform snippet: ###### Call Ansible to Join VM to Domain
resource "ansible_host" "TF-W2K25-CC-1" {          
  name   = "10.53.16.101"
  groups = ["cloudconnector1-ip"] 
  variables = {
    ansible_user                 = "XxXxXxXxX",
    ansible_ssh_private_key_file = "~/.ssh/id_ed25519",
    ansible_python_interpreter   = "/usr/bin/python3"
  }
}

resource "ansible_host" "TF-W2K25-CC-2" {         
  name   = "10.53.16.102"
  groups = ["cloudconnector2-ip"] 
  variables = {
    ansible_user                 = "XxXxXxXxX",
    ansible_ssh_private_key_file = "~/.ssh/id_ed25519",
    ansible_python_interpreter   = "/usr/bin/python3"
  }
}

resource "terraform_data" "ansible_inventory" {
  provisioner "local-exec" {
    command = "ansible-inventory -i inventory.yml --graph --vars"
  }
}

resource "terraform_data" "ansible_playbook" {
  provisioner "local-exec" {
    command = "ansible-playbook -v --check /etc/ansible/Join-VMToDomain-CC1.microsoft.ad.ansible.yml -c winrm"
    #command = "ansible-playbook -i inventory.yml /etc/ansible/Join-VMToDomain.microsoft.ad.ansible.yml --check -v"
  }
  depends_on = [terraform_data.ansible_inventory]
}
Ansible Playbook: ---
- name: join host to domain with automatic reboot
  hosts: cloudconnector1-ip:cloudconnector1-ip

  tasks:
  - name: join host cc1 to domain with automatic reboot
    microsoft.ad.membership:
      dns_domain_name: XxXxX.XxX
      hostname: az-weur-tf-cc1
      domain_admin_user: XxXxXxXxXxXxXxXxX
      domain_admin_password: "XxXxXxXxXxXxXxXxX"
      domain_ou_path: "OU=Infra,OU=West EUR,OU=Azure,OU=Cloud,OU=Compute,DC=XxXxX,DC=XxXxX"
      state: domain
      reboot: false

  - name: join host cc2 to domain with automatic reboot
    microsoft.ad.membership:
      dns_domain_name: XxXxX.XxX
      hostname: az-weur-tf-cc2
      domain_admin_user: XxXxXxXxXxXxXxXxX
      domain_admin_password: "XxXxXxXxXxXxXxXxX"
      domain_ou_path: "OU=Infra,OU=West EUR,OU=Azure,OU=Cloud,OU=Compute,DC=XxXxX,DC=XxXxX"
      state: domain
      reboot: falseAnsible Configuration - insecure communication: XxXxXxXxX@devops-automation:/etc/ansible$ sudo nano hosts

[cloudconnector1-ip]
10.53.16.101

[cloudconnector2-ip]
10.53.16.102

[cloudconnector1-ip:vars]
ansible_user="XxXxXxXxX"
ansible_password="XxXxXxXxXxXxXxXxXxXxXxXxX"
ansible_connection=winrm
ansible_winrm_transport=basic
ansible_winrm_port=5985
ansible_winrm_server_cert_validation=ignore

[cloudconnector2-ip:vars]
ansible_user="XxXxXxXxX"
ansible_password="XxXxXxXxXxXxXxXxXxX"
ansible_connection=winrm
ansible_winrm_transport=basic
ansible_winrm_port=5985
ansible_winrm_server_cert_validation=ignoreTesting the Playbook: XxXxXxXxX@devops-automation:/etc/ansible$ ansible-playbook /etc/ansible/Join-VMToDomain.microsoft.ad.ansible.yml --check -c winrm
...

PLAY [join host to domain with automatic reboot] ***********************************************************************

TASK [Gathering Facts] *************************************************************************************************

ok: [10.53.16.101]
ok: [10.53.16.102]

TASK [join host cc1 to domain with automatic reboot] ***********************************************************************
ok: [10.53.16.101]

TASK [join host cc2 to domain with automatic reboot] ***********************************************************************
ok: [10.53.16.102]

PLAY RECAP *************************************************************************************************************
10.53.16.101               : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
10.53.16.102               : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

XxXxXxXxX@devops-automation:/etc/ansible$ Example: Create all needed prerequisites and two VMs for Cloud ConnectorsIn this example, we use Terraform to set up the prerequisites for installing two Cloud Connector VMs on vSphere. Terraform snippet: # Terraform Deployment of Citrix Cloud Connectors on vSphere 8 - 25.11 
## Definition of vSphere Provider Variables - not all available variables are defined, only these we use
### Reference https://registry.terraform.io/providers/hashicorp/vsphere/latest/docs

data "vsphere_datacenter" "datacenter" {
  name = var.vsphere_datacenter
}

data "vsphere_datastore" "datastore" {
  name          = var.vsphere_datastore
  datacenter_id = data.vsphere_datacenter.datacenter.id
}

data "vsphere_network" "network" {
  name          = var.vsphere_network
  datacenter_id = data.vsphere_datacenter.datacenter.id
}

data "vsphere_host" "host" {
  name          = var.vsphere_host
  datacenter_id = data.vsphere_datacenter.datacenter.id
}

### If a cluster is available, uncomment this block
data "vsphere_compute_cluster" "cluster" {
  name          = "TACG"
  datacenter_id = data.vsphere_datacenter.datacenter.id
}

data "vsphere_virtual_machine" "W2K22_Template" {
  name          = var.vsphere_W2K25_template_name
  datacenter_id = data.vsphere_datacenter.datacenter.id
}

### Create CC1-VM
resource "vsphere_virtual_machine" "cc1-vm" {
  name             = var.vsphere_ccc1_vmname
  resource_pool_id = data.vsphere_compute_cluster.cluster.resource_pool_id
  datastore_id     = data.vsphere_datastore.datastore.id
  num_cpus         = var.vsphere_cc1_cpus
  memory           = var.vsphere_cc1_ram
  guest_id         = data.vsphere_virtual_machine.W2K22_Template.guest_id
  scsi_type        = data.vsphere_virtual_machine.W2K22_Template.scsi_type
  firmware         = "efi"

  network_interface {
    network_id   = data.vsphere_network.network.id
    adapter_type = data.vsphere_virtual_machine.W2K22_Template.network_interface_types[0]
  }

  disk {
    label = var.vsphere_cc1_disk_name

    ### If you want to use the same disk size and type as the template, uncomment this block
    size             = data.vsphere_virtual_machine.W2K22_Template.disks.0.size
    thin_provisioned = data.vsphere_virtual_machine.W2K22_Template.disks.0.thin_provisioned

    ### If you want to override the disk size and disk provisioning type, uncomment this block and set the corresponding variables
    #size             = var.vsphere_cc1_disk_size
    #thin_provisioned = var.vsphere_cc1_disk_provtype
  }

  cdrom {
    datastore_id = data.vsphere_datastore.datastore.id
    path         = var.vsphere_iso_destination_path
  }

  clone {
    template_uuid = data.vsphere_virtual_machine.W2K22_Template.id
    customize {
      network_interface {
        ipv4_address    = var.vsphere_cc1_ipv4
        ipv4_netmask    = var.vsphere_cc_ipv4netmask
        dns_server_list = [var.vsphere_cc_dns[0]]
      }
      windows_options {
        computer_name = var.vsphere_computernames[0]
      }
      ipv4_gateway = var.vsphere_cc_ipv4_gateway
    }
  }

}

### Create CC2-VM
resource "vsphere_virtual_machine" "cc2-vm" {
  name             = var.vsphere_cc2_vmname
  resource_pool_id = data.vsphere_compute_cluster.cluster.resource_pool_id
  datastore_id     = data.vsphere_datastore.datastore.id
  num_cpus         = var.vsphere_cc2_cpus
  memory           = var.vsphere_cc2_ram
  guest_id         = data.vsphere_virtual_machine.W2K22_Template.guest_id
  scsi_type        = data.vsphere_virtual_machine.W2K22_Template.scsi_type
  firmware         = "efi"

  network_interface {
    network_id   = data.vsphere_network.network.id
    adapter_type = data.vsphere_virtual_machine.W2K22_Template.network_interface_types[0]
  }

  disk {
    label = var.vsphere_cc2_disk_name

    ### If you want to use the same disk size and type as the template, uncomment this block
    size             = data.vsphere_virtual_machine.W2K22_Template.disks.0.size
    thin_provisioned = data.vsphere_virtual_machine.W2K22_Template.disks.0.thin_provisioned

    ### If you want to override the disk size and disk provisioning type, uncomment this block and set the corresponding variables
    #size             = var.vsphere_cc2_disk_size
    #thin_provisioned = var.vsphere_cc2_disk_provtype
  }
  cdrom {
    datastore_id = data.vsphere_datastore.datastore.id
    path         = var.vsphere_iso_destination_path
  }

  clone {
    template_uuid = data.vsphere_virtual_machine.W2K22_Template.id
    customize {
      network_interface {
        ipv4_address    = var.vsphere_cc2_ipv4
        ipv4_netmask    = var.vsphere_cc_ipv4netmask
        dns_server_list = [var.vsphere_cc_dns[0]]
      }
      windows_options {
        computer_name = var.vsphere_computernames[0]
      }
      ipv4_gateway = var.vsphere_cc_ipv4_gateway
    }
  }
}Please consult further examples of using automation and IaC for Hypervisors/Hyperscalers on our Automation landing page on Citrix TechZone. Automation of DaaS ComponentsIf you want to automate the deployment or configuration of your Hypervisors/Hyperscalers, you can leverage the corresponding providers. Here´s a short matrix of supported toolkits:  Important There are many ways to automate DaaS components and use IaC, especially across different Hypervisors and Hyperscalers using Terraform and Ansible. Listing all possibilities, snippets, and runtime outputs is out of scope. Please consult the documentation at https://github.com/citrix/terraform-provider-citrix/tree/main/docs/resources for additional examples, or visit our Automation landing page on Citrix TechZone. First, we want to show the syntax differences for creating the same Hypervisor Connection and Hypervisor Connection Pool on AWS, Azure, and GCP. The examples are snippets from real-world deployments we did with customers: Example: Create a Hypervisor Connection and Connection Pool on AWS### Creating a Hypervisor Resource Pool
#### Retrieving the Zone ID
data "citrix_zone" "GetTFAWSZoneID" {
  name = "${var.AWSEC2_ZoneID}"
}

#### 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}"
}

#### Creating the Hypervisor Connection
resource "citrix_aws_hypervisor" "CreateHypervisorConnection" {
  depends_on = [ data.local_file.LoadZoneID ]
    name              = "${var.CC_AWSEC2-HypConn-Name}"
    zone              = data.citrix_zone.GetTFAWSZoneID
    api_key           = "${var.AWSEC2_AccessKey}"
    secret_key        = "${var.AWSEC2_AccessKeySecret}"
    region            = "${var.AWSEC2_Region}"
}

#### 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
} Example: Create a Hypervisor Connection and Connection Pool on Azure#### Retrieving the ZoneID
data "citrix_zone" "GetTFAzureZoneID" {
  name = "${var.CC-Azure-ZoneID}"
}

### Creating the Hypervisor Connection
resource "citrix_azure_hypervisor" "CreateAzureHostingConnection" {
  depends_on = [ data.citrix_zone.GetTFAzureZoneID ]
    name                = "${var.CC-Azure-HypConn-Name}"
    zone                = data.citrix_zone.GetTFAzureZoneID.id
    active_directory_id = "${var.azurerm-tenantid}"
    subscription_id     = "${var.azurerm-subscriptionid}"
    application_secret  = "${var.azurerm-clientsecret}"
    application_id      = "${var.azurerm-clientid}"
}

#### Creating the Hypervisor Resource Pool
resource "citrix_azure_hypervisor_resource_pool" "CreateAzureHostingConnectionPool" {
  depends_on = [ citrix_azure_hypervisor.CreateAzureHostingConnection]
  name                           = "${var.CC-Azure-HypConn-Name}"
  hypervisor                     = citrix_azure_hypervisor.CreateAzureHostingConnection.id
  region                         = "${var.TACG-TMM-ResourceGroup-Location}"
  virtual_network_resource_group = "${var.TACG-TMM-ResourceGroup-Name}"
  virtual_network                = "${var.CC-Azure-VNet-Name}"
  subnets                        = var.CC-Azure-Subnets
} Example: Create a Hypervisor Connection and Connection Pool on GCP#### Retrieving the ZoneID
data "citrix_zone" "GetGCPZoneID" {
  name = "${var.GCP-ZoneID}"
}

resource "citrix_gcp_hypervisor" "CreateHypervisorConnection" {
  depends_on = [ data.citrix_zone.GetGCPZoneID ]
    name                        = "${var.CC_GCP-HypConn-Name}"
    zone                        = data.citrix_zone.GetGCPZoneID.id
    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}", ]
}  Note These three examples showcase that there are slight but distinct differences in the syntax depending on the underlying Hypervisor or HyperScaler. Heavier differences occur when creating Machine Catalogs.  Example: Creation of a Machine Catalog on vSphere based on a snapshot of an Imageresource "citrix_machine_catalog" "TACG-TMM-CVAD-V-MC" {
  depends_on        = [citrix_vsphere_hypervisor_resource_pool.TACG-TMM-CVAD-V-HypConnPool]
  name              = var.TACG-TMM-CVAD-MC-Name
  description       = var.TACG-TMM-CVAD-MC-Description
  provisioning_type = var.TACG-TMM-CVAD-MC-ProvisioningType
  allocation_type   = var.TACG-TMM-CVAD-MC-AllocationType
  session_support   = var.TACG-TMM-CVAD-MC-SessionType
  zone              = data.citrix_zone.TACG-TMM-CVAD-V-Zone.id
  provisioning_scheme = {
    hypervisor               = citrix_vsphere_hypervisor.TACG-TMM-CVAD-V-HypConn.id
    hypervisor_resource_pool = citrix_vsphere_hypervisor_resource_pool.TACG-TMM-CVAD-V-HypConnPool.id
    identity_type            = var.TACG-TMM-CVAD-MC-IdentityType
    machine_domain_identity = {
      domain                   = var.TACG-TMM-CVAD-MC-VM-DomainFQDN
      domain_ou                = var.TACG-TMM-CVAD-MC-VM-DomainOU
      service_account          = var.TACG-TMM-CVAD-MC-VM-ServiceUserName
      service_account_password = var.TACG-TMM-CVAD-MC-VM-ServiceUserPassword
    }
    vsphere_machine_config = {
      master_image_vm = var.TACG-TMM-CVAD-MC-MasterImageVMName
      cpu_count       = var.TACG-TMM-CVAD-MC-VM-CPUCount
      memory_mb       = var.TACG-TMM-CVAD-MC-VM-MemorySize
      image_snapshot  = var.TACG-TMM-CVAD-MC-MasterImageVMName-SnapshotName
    }
    number_of_total_machines = var.TACG-TMM-CVAD-MC-VM-NumberOfVMs
    machine_account_creation_rules = {
      naming_scheme      = var.TACG-TMM-CVAD-MC-VM-NamingScheme
      naming_scheme_type = var.TACG-TMM-CVAD-MC-VM-NamingSchemeType
    }
  }
} Example: Creation of a Machine Catalog on Azure based on an Image Gallery-based Image### Retrieving the Shared Image Gallery
 data "azurerm_shared_image_gallery" "GetAzureSIG" {
  name                = "${var.CC-Azure-SIG-Name}"
  resource_group_name = "${var.TACG-TMM-ResourceGroup-Name}"
} 

#### Retrieving the Shared Image Definition
data "azurerm_shared_image" "GetAzureSIGDefinition" {
  name                = "${var.CC-Azure-SIGDefinition-Name}"
  gallery_name        = data.azurerm_shared_image_gallery.GetAzureSIG.name
  resource_group_name = "${var.TACG-TMM-ResourceGroup-Name}"
} 

#### Retrieving the Shared Image Definition Version
data "azurerm_shared_image_version" "GetAzureSIGDefinitionVersion" {
  name                = "${var.CC-Azure-SIGDefinitionVersion}"
  image_name          = data.azurerm_shared_image.GetAzureSIGDefinition.name
  gallery_name        = data.azurerm_shared_image_gallery.GetAzureSIG.name
  resource_group_name = "${var.TACG-TMM-ResourceGroup-Name}"
}
#### Creating the Machine Catalog
resource "citrix_machine_catalog" "CreateAzureMCSCatalog" {
  depends_on            = [ time_sleep.wait_30_seconds1 ]
    name                = "${var.CC-Azure-MC-Name}"
    description         = "${var.CC-Azure-MC-Description}"
    allocation_type     = "${var.CC-Azure-MC-AllocationType}"
    session_support     = "${var.CC-Azure-MC-SessionType}"
    provisioning_type   = "MCS"
    zone                = data.citrix_zone.GetTFAzureZoneID.id
    scopes              = "${var.CC-GetScopeToBeUsed-Names}"
        
    provisioning_scheme          =   {
        hypervisor               = citrix_azure_hypervisor.CreateAzureHostingConnection.id
        hypervisor_resource_pool = citrix_azure_hypervisor_resource_pool.CreateAzureHostingConnectionPool.id
        identity_type            = "${var.CC-Azure-MC-IDPType}"
    
        machine_domain_identity  = {
            domain                   = "${var.CC-Azure-MC-Domain}"
            domain_ou                = "${var.CC-Azure-MC-DomainOU}"
            service_account          = "${var.CC-Azure-MC-DomainAdmin-Username-UPN}"
            service_account_password = "${var.CC-Azure-MC-DomainAdmin-Password}"
        }

        azure_machine_config = {
            storage_type             = "Standard_LRS"
            use_managed_disks        = true
            service_offering         = "${var.CC-Azure-MC-VMSize}"
            
            azure_master_image = {
                resource_group       = var.azure_resource_group
                gallery_image = {
                     gallery    = data.azurerm_shared_image_gallery.GetAzureSIG.name 
                     definition = data.azurerm_shared_image.GetAzureSIGGetAzureSIGDefinition.name
                     version    = data.azurerm_shared_image_version.GetAzureSIGDefinitionVersion.name 
                 }
               }

            machine_profile = {
              machine_profile_resource_group = "${var.TACG-TMM-ResourceGroup-Name}"
              machine_profile_vm_name        = "${var.CC-Azure-MC-VMProfileName}"
            }
        }

       number_of_total_machines              =  "${var.CC-Azure-MC-MachineCount}"
    
       machine_account_creation_rules        = {
            naming_scheme      = "${var.CC-Azure-MC-NamingScheme-Name}"
            naming_scheme_type = "${var.CC-Azure-MC-NamingScheme-Type}"
        }
    }
} Creation of a Machine Catalog on AWS based on an Image AMI#### Creating the Machine Catalog
resource "citrix_machine_catalog" "resource "citrix_machine_catalog" "CreateAWSMCSCatalog" {
    name                        = "${var.AWS-MC-Name}"
    description                 = "${var.AWS-MC-Description}"
    allocation_type             = "${var.AWS-MC-AllocationType}"
    session_support             = "${var.AWS-MC-SessionType}"
     zone                       = data.citrix_zone.GetAWSZoneID.id
    provisioning_type           = "MCS"
    provisioning_scheme         = {
        hypervisor = data.citrix_aws_hypervisor.AWSHypConn.id
        hypervisor_resource_pool = data.citrix_aws_hypervisor_resource_pool.AWSHypConnPool.id
        identity_type            = "${var.AWS-MC-IDPType}"
        
        machine_domain_identity  = {
            domain                   = "${var.AWS-MC-Domain}"
            domain_ou                = "${var.AWS-MC-DomainOU}"
            service_account          = "${var.AWS-MC-DomainAdmin-Username-UPN}"
            service_account_password = "${var.AWS-MC-DomainAdmin-Password}"
        }
        aws_machine_config = {
            image_ami        = "${var.AWS-MC-ImageAMI}"
            master_image     = "${var.AWS-MC-MImageAMIName}"
            service_offering = "${var.AWS-MC-MachineSize}"
            security_groups = [
                "${var.AWS-MC-SecGroup}"
            ]
            tenancy_type        = "${var.AWS-MC-Tenancy}"
            secondary_vm_sizes  = [
                {
                    vm_size                         = "${var.AWS-MC-SecondaryMachineSize}"
                    use_spot_pricing_if_available   = false
                }
            ]
        }
        number_of_total_machines       =  "${var.AWS-MC-MachineCount}"
    
       machine_account_creation_rules  = {
            naming_scheme      = "${var.AWS-MC-NamingScheme-Name}"
            naming_scheme_type = "${var.AWS-MC-NamingScheme-Type}"
        }
    }   
} Note These examples show that there are greater differences in syntax depending on the underlying Hypervisor or HyperScaler and the Master Image type used. No differences occur when creating a Delivery Group.  Example: Creation of a Delivery Group#### Create the Delivery Group
resource "citrix_delivery_group" "TACG-TMM-DG" {
  depends_on = [citrix_machine_catalog.TACG-TMM-MC]
  name                     = var.TACG-TMM-DG-Name
  description              = var.TACG-TMM-DG-Description
  minimum_functional_level = var.TACG-TMM-DG-FunctionalLevel
  associated_machine_catalogs = [
    {
      machine_catalog = citrix_machine_catalog.TACG-TMM-MC.id
      machine_count = var.TACG-TMM-MC-VM-NumberOfVMs
    }
  ]
  desktops = [
    {
      published_name = var.TACG-TMM-DG-Desktops-Name
      description    = var.TACG-TMM-DG-Desktops-Description
      restricted_access_users = {
        allow_list = var.TACG-TMM-CVAD-DG-Desktops-AllowList
      }
      enabled = var.TACG-TMM-CVAD-DG-Desktops-Enabled

      ###### Not needed due to Static assignment type-MC, change if a Random assignment type-MC is used
      # enable_session_roaming = var.TACG-TMM-DG-Desktops-SessionRoaming
    }

  ]
  autoscale_settings = {
    autoscale_enabled                   = var.TACG-TMM-DG-AS-Enabled
    peak_disconnect_timeout_minutes     = var.TACG-TMM-DG-AS-PeakDisconnectTime
    off_peak_disconnect_timeout_minutes = var.TACG-TMM-DG-AS-OffPeakDisconnectTime
    peak_disconnect_action              = var.TACG-TMM-DG-AS-PeakDisconnectAction
    off_peak_disconnect_action          = var.TACG-TMM-DG-AS-OffPeakDisconnectAction
    off_peak_log_off_timeout_minutes    = var.TACG-TMM-DG-AS-PeakLogoffTime
    peak_log_off_timeout_minutes        = var.TACG-TMM-DG-AS-OffPeakLogoffTime
    peak_log_off_action                 = var.TACG-TMM-DG-AS-PeakDisconnectAction
    off_peak_log_off_action             = var.TACG-TMM-DG-AS-OffPeakDisconnectAction

    ###### Not needed due to Static assignment type-MC, change if a Random assignment type-MC is used

    #log_off_off_peak_disconnected_session_after_seconds = var.TACG-TMM-DG-AS-PeakLogoffTime
    #log_off_peak_disconnected_session_after_seconds     = var.TACG-TMM-DG-AS-OffPeakLogoffTime
    #peak_log_off_action                                 = var.TACG-TMM-DG-AS-PeakDisconnectAction
    #off_peak_log_off_action                             = var.TACG-TMM-DG-AS-OffPeakDisconnectAction
    #log_off_reminder_enabled                            = var.TACG-TMM-DG-AS-Enabled
    #log_off_reminder_message                            = var.TACG-TMM-DG-RS-Notification-MessageBody
    #log_off_reminder_title                              = var.TACG-TMM-DG-AS-Notification-MessageTitle
    #log_off_warning_message                             = var.TACG-TMM-DG-RS-Notification-MessageBody
    #log_off_warning_title                               = var.TACG-TMM-DG-AS-Notification-MessageTitle
    timezone = var.TACG-TMM-DG-AS-TimeZone

    power_time_schemes = [
      {
        days_of_week          = var.TACG-TMM-DG-AS-Days
        display_name          = var.TACG-TMM-DG-AS-Name
        peak_time_ranges      = var.TACG-TMM-DG-AS-PeakTime
        pool_using_percentage = false
      },
    ]
  }
  restricted_access_users = {
    allow_list = var.TACG-TMM-DG-Desktops-AllowList
  }
  reboot_schedules = [
    {
      name                    = var.TACG-TMM-DG-RS-Name
      reboot_schedule_enabled = var.TACG-TMM-DG-RS-Enabled
      frequency               = var.TACG-TMM-DG-RS-Frequency
      frequency_factor        = 1
      days_in_week            = var.TACG-TMM-DG-RS-RebootDays
      start_time              = var.TACG-TMM-DG-RS-RebootStartTime
      start_date              = var.TACG-TMM-DG-RS-RebootStartDate
      reboot_duration_minutes = var.TACG-TMM-DG-RS-RebootDuration
      ignore_maintenance_mode = var.TACG-TMM-DG-RS-IgnoreMaintenanceMode
      natural_reboot_schedule = var.TACG-TMM-DG-RS-NormalRebootSchedule

      ###### Not needed due to SingleSession type-MC, change if a Random assignment type-MC is used

      #reboot_notification_to_users = {
      #notification_duration_minutes       = var.TACG-TMM-DG-RS-Notification-Duration
      #notification_message                = var.TACG-TMM-DG-RS-Notification-MessageBody
      #notification_title                  = var.TACG-TMM-DG-RS-Notification-MessageTitle
      #notification_repeat_every_5_minutes = var.TACG-TMM-DG-RS-Notification-MessageRepeat
      #} 
    }
  ]
} Note A syntactically simple entity is the Policy set, but be aware that it has a very complex structure.  Example: Creation of a Policy set with some policies### Make sure that the site settings allow the usage of Policy Sets
resource "citrix_site_settings" "TACG-TMM-SiteSettings" {
    web_ui_policy_set_enabled                           = true
    dns_resolution_enabled                              = true
    multiple_remote_pc_assignments                      = true
    trust_requests_sent_to_the_xml_service_port_enabled = true
}

#### Create a default Policy Set
resource "citrix_policy_set_v2" "TACG-TMM-DefaultPolicySet" {
  depends_on      = [citrix_delivery_group.TACG-TMM-V-DG, citrix_site_settings.TACG-TMM-SiteSettings ]
  name            = var.TACG-TMM-PS-Name
  description     = var.TACG-TMM-PS-Description
  delivery_groups = [citrix_delivery_group.TACG-TMM-V-DG.id]
}

#### Create a Policy in the Policy Set
resource "citrix_policy" "TACG-TMM-DefaultPolicySet-Printing" {
  depends_on    = [citrix_policy_set_v2.TACG-TMM-DefaultPolicySet]
  policy_set_id = citrix_policy_set_v2.TACG-TMM-DefaultPolicySet.id
  name          = "Printing"
  description   = "Example of Printer-related policy in default Policy Set"
  enabled       = true
}

#### Create a Policy in the Policy Set
resource "citrix_policy" "TACG-TMM-DefaultPolicySet-HDX" {
  depends_on    = [citrix_policy_set_v2.TACG-TMM-DefaultPolicySet]
  policy_set_id = citrix_policy_set_v2.TACG-TMM-DefaultPolicySet.id
  name          = "HDX™ Graphics"
  description   = "Example of HDX Graphics-related policy in default Policy Set"
  enabled       = true
}

#### Create a Policy in the Policy Set
resource "citrix_policy" "TACG-TMM-DefaultPolicySet-ClientDrives" {
  depends_on    = [citrix_policy_set_v2.TACG-TMM-DefaultPolicySet]
  policy_set_id = citrix_policy_set_v2.TACG-TMM-DefaultPolicySet.id
  name          = "Client Drives"
  description   = "Example of Client Drive-related policy in default Policy Set"
  enabled       = true
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-DefaultPolicySet-Printing1" {
  policy_id   = citrix_policy.TACG-TMM-DefaultPolicySet-Printing.id
  name        = "ClientPrinterAutoCreation"
  value       = "DefaultPrinterOnly"
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-DefaultPolicySet-Printing2" {
  policy_id   = citrix_policy.TACG-TMM-DefaultPolicySet-Printing.id
  name        = "UniversalPrintDriverUsage"
  value       = "FallbackToSpecific"
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-DefaultPolicySet-HDX1" {
  policy_id   = citrix_policy.TACG-TMM-DefaultPolicySet-HDX.id
  name        = "AllowVisuallyLosslessCompression"
  enabled     = true
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-DefaultPolicySet-HDX2" {
  policy_id   = citrix_policy.TACG-TMM-DefaultPolicySet-HDX.id
  name        = "UseVideoCodecForCompression"
  value       = "UseVideoCodecIfPreferred"
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-DefaultPolicySet-HDX3" {
  policy_id   = citrix_policy.TACG-TMM-DefaultPolicySet-HDX.id
  name        = "UseHardwareEncodingForVideoCodec"
  enabled     = true
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-DefaultPolicySet-CD1" {
  policy_id   = citrix_policy.TACG-TMM-DefaultPolicySet-ClientDrives.id
  name        = "AutoConnectDrives"
  enabled     = true
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-DefaultPolicySet-CD2" {
  policy_id   = citrix_policy.TACG-TMM-DefaultPolicySet-ClientDrives.id
  name        = "ClientDriveRedirection"
  enabled     = true
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-DefaultPolicySet-CD3" {
  policy_id   = citrix_policy.TACG-TMM-DefaultPolicySet-ClientDrives.id
  name        = "ClientFixedDrives"
  enabled     = true
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-DefaultPolicySet-CD4" {
  policy_id   = citrix_policy.TACG-TMM-DefaultPolicySet-ClientDrives.id
  name        = "ClientFloppyDrives"
  enabled     = false
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-DefaultPolicySet-CD5" {
  policy_id   = citrix_policy.TACG-TMM-DefaultPolicySet-ClientDrives.id
  name        = "ClientOpticalDrives"
  enabled     = false
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-DefaultPolicySet-CD6" {
  policy_id   = citrix_policy.TACG-TMM-DefaultPolicySet-ClientDrives.id
  name        = "ClientNetworkDrives"
  enabled     = false
  use_default = false
}

#### Create the corresponding Policy Filters
resource "citrix_delivery_group_policy_filter" "TACG-TMM-DefaultPolicySet-DGFilter-1" {
  depends_on        = [citrix_policy.TACG-TMM-DefaultPolicySet-Printing]
  policy_id         = citrix_policy.TACG-TMM-DefaultPolicySet-Printing.id
  enabled           = true
  allowed           = true
  delivery_group_id = citrix_delivery_group.TACG-TMM-V-DG.id
}

#### Create the corresponding Policy Filters
resource "citrix_delivery_group_policy_filter" "TACG-TMM-DefaultPolicySet-DGFilter-2" {
  depends_on        = [citrix_policy.TACG-TMM-DefaultPolicySet-HDX]
  policy_id         = citrix_policy.TACG-TMM-DefaultPolicySet-HDX.id
  enabled           = true
  allowed           = true
  delivery_group_id = citrix_delivery_group.TACG-TMM-V-DG.id
}

#### Create the corresponding Policy Filters
resource "citrix_delivery_group_policy_filter" "TACG-TMM-DefaultPolicySet-DGFilter-3" {
  depends_on        = [citrix_policy.TACG-TMM-DefaultPolicySet-ClientDrives]
  policy_id         = citrix_policy.TACG-TMM-DefaultPolicySet-ClientDrives.id
  enabled           = true
  allowed           = true
  delivery_group_id = citrix_delivery_group.TACG-TMM-V-DG.id
}

#### Create the corresponding Policy Filters
resource "citrix_user_policy_filter" "TACG-TMM-DefaultPolicySet-SIDFilter-1" {
  depends_on = [citrix_policy.TACG-TMM-DefaultPolicySet-Printing]
  policy_id  = citrix_policy.TACG-TMM-DefaultPolicySet-Printing.id
  enabled    = true
  allowed    = true
  sid        = var.TACG-TMM-PS-UserSID
}

#### Create the corresponding Policy Filters
resource "citrix_user_policy_filter" "TACG-TMM-DefaultPolicySet-SIDFilter-2" {
  depends_on = [citrix_policy.TACG-TMM-DefaultPolicySet-HDX]
  policy_id  = citrix_policy.TACG-TMM-DefaultPolicySet-HDX.id
  enabled    = true
  allowed    = true
  sid        = var.TACG-TMM-PS-UserSID
}

#### Create the corresponding Policy Filters
resource "citrix_user_policy_filter" "TACG-TMM-DefaultPolicySet-SIDFilter-3" {
  depends_on = [citrix_policy.TACG-TMM-DefaultPolicySet-ClientDrives]
  policy_id  = citrix_policy.TACG-TMM-DefaultPolicySet-ClientDrives.id
  enabled    = true
  allowed    = true
  sid        = var.TACG-TMM-PS-UserSID
}

#### Create the corresponding Policy Filters
resource "citrix_client_ip_policy_filter" "TACG-TMM-DefaultPolicySet-ClientIPFilter-1" {
  depends_on = [citrix_policy.TACG-TMM-DefaultPolicySet-Printing]
  policy_id  = citrix_policy.TACG-TMM-DefaultPolicySet-Printing.id
  enabled    = true
  allowed    = true
  ip_address = var.TACG-TMM-PS-IPRange
}

#### Create the corresponding Policy Filters
resource "citrix_client_ip_policy_filter" "TACG-TMM-DefaultPolicySet-ClientIPFilter-2" {
  depends_on = [citrix_policy.TACG-TMM-DefaultPolicySet-HDX]
  policy_id  = citrix_policy.TACG-TMM-DefaultPolicySet-HDX.id
  enabled    = true
  allowed    = true
  ip_address = var.TACG-TMM-PS-IPRange
}

#### Create the corresponding Policy Filters
resource "citrix_client_ip_policy_filter" "TACG-TMM-DefaultPolicySet-ClientIPFilter-3" {
  depends_on = [citrix_policy.TACG-TMM-DefaultPolicySet-ClientDrives]
  policy_id  = citrix_policy.TACG-TMM-DefaultPolicySet-ClientDrives.id
  enabled    = true
  allowed    = true
  ip_address = var.TACG-TMM-PS-IPRange
}

resource "null_resource" "WriteProgress5" {
  depends_on = [citrix_policy_setting.TACG-TMM-DefaultPolicySet-CD1, citrix_policy_setting.TACG-TMM-DefaultPolicySet-CD2, citrix_policy_setting.TACG-TMM-DefaultPolicySet-CD3, citrix_policy_setting.TACG-TMM-DefaultPolicySet-CD4, citrix_policy_setting.TACG-TMM-DefaultPolicySet-CD5, citrix_policy_setting.TACG-TMM-DefaultPolicySet-CD6, citrix_policy_setting.TACG-TMM-DefaultPolicySet-HDX1, citrix_policy_setting.TACG-TMM-DefaultPolicySet-HDX2, citrix_policy_setting.TACG-TMM-DefaultPolicySet-HDX3, citrix_policy_setting.TACG-TMM-DefaultPolicySet-Printing1, citrix_policy_setting.TACG-TMM-DefaultPolicySet-Printing2]
  provisioner "local-exec" {
    command = "echo The default Policy Set and its policies and filters were successfully created..."
  }
}Important Finally, we want to emphasize again: There are many ways to automate DaaS Components and use IaC, especially across different Hypervisors and Hyperscalers using Terraform and Ansible. Listing all possibilities, snippets, and runtime outputs is out of scope. Please consult the documentation at https://github.com/citrix/terraform-provider-citrix/tree/main/docs/resources for additional examples, or visit our Automation landing page on Citrix TechZone.  Automation of CVAD ComponentsIf you want to automate the deployment or configuration of your Hypervisors/Hyperscalers, you can leverage the corresponding providers. Here´s a short matrix of supported toolkits:  Important There are many ways to automate CVAD components and use IaC, especially across different Hypervisors and Hyperscalers using Terraform and Ansible. Listing all possibilities, snippets, and runtime outputs is out of scope. Please consult the documentation at https://github.com/citrix/terraform-provider-citrix/tree/main/docs/resources for additional examples, or visit our Automation landing page on Citrix TechZone. As there is almost no difference between using Automation and IaC for the Citrix DaaS-based entities and the Citrix CVAD-based entities, we want to show only one example: Example: Creation of a StoreFront deploymentCaution Due to limitations in StoreFront´s automation capabilities, this module/snippet must be run on a Windows-based, domain-joined VM. You cannot run it from, e.g., the Ubuntu VM mentioned throughout the handbook.  WARNING The current Automation snippets for StoreFront will only run on Windows PowerShell &lt;= 5.1. They will not run on PowerShell &gt;=6. Be sure to start the Terraform code on the correct PowerShell version. You may encounter this error after starting the Terraform snippet: Error: Error fetching StoreFront version
│
│   with citrix_stf_webreceiver_service.CreateSFWebReceiverService,
│   on CVADOnXS-ConfigureStoreFront.tf line 48, in resource "citrix_stf_webreceiver_service" "CreateSFWebReceiverService":
│   48: resource "citrix_stf_webreceiver_service" "CreateSFWebReceiverService" {
│
│ Error message: error executing command: Get-STFVersion
│  Error Message:
│  ConvertTo-SecureString : "ConvertTo-SecureString" was found in module "Microsoft.PowerShell.Security",
│ we were unable to load the module. Run "Import-Module
│ Microsoft.PowerShell.Security".
│ In row:1 column:205
│ + ... nistrator@the-austrian-citrix-guy.at',(ConvertTo-SecureString -Force  ...
│ +                                            ~~~~~~~~~~~~~~~~~~~~~~
│     + CategoryInfo          : ObjectNotFound: (ConvertTo-SecureString:String) [], CommandNotFoundException
│     + FullyQualifiedErrorId : CouldNotAutoloadMatchingModule
│╵That error means you have not loaded the module Microsoft.PowerShell.Security in Windows PowerShell. Be sure to load the module in Windows PowerShell, not PowerShell: PS C:\__PPMM\__TF\_ConfigureStoreFront&gt; Import-Module Microsoft.PowerShell.Security Terraform snippet for creating a StoreFront deployment: #### Configuring StoreFront 
##### Reference https://github.com/citrix/terraform-provider-citrix/blob/main/StoreFront.md
###### Create an initial StoreFront Deployment
resource "citrix_stf_deployment" "CreateInitialSFDeployment" {
  site_id       = var.TACG-TMM-SF-SiteID
  host_base_url = var.TACG-TMM-SF-SiteURL
  roaming_gateway = [
    {
      name                         = var.TACG-TMM-SF-Gateway-Name
      logon_type                   = var.TACG-TMM-SF-Gateway-LogonType
      gateway_url                  = var.TACG-TMM-SF-Gateway-URL
      callback_url                 = var.TACG-TMM-SF-Gateway-CallbackURL
      subnet_ip_address            = var.TACG-TMM-SF-Gateway-SNIP
      secure_ticket_authority_urls = var.TACG-TMM-SF-Gateway-STAs
    }
  ]
  roaming_beacon = {
    internal_address   = var.TACG-TMM-SF-Beacon-Internal
    external_addresses = var.TACG-TMM-SF-Beacon-External
  }
}

##### Create an Authentication service
resource "citrix_stf_authentication_service" "CreateSFAuthenticationService" {
  depends_on    = [citrix_stf_deployment.CreateInitialSFDeployment]
  site_id       = citrix_stf_deployment.CreateInitialSFDeployment.site_id
  friendly_name = var.TACG-TMM-SF-AuthService-Name
  virtual_path  = var.TACG-TMM-SF-AuthService-Path
}

##### Create a Store service
resource "citrix_stf_store_service" "CreateSFStoreService" {
  depends_on                          = [citrix_stf_authentication_service.CreateSFAuthenticationService]
  site_id                             = citrix_stf_deployment.CreateInitialSFDeployment.site_id
  virtual_path                        = var.TACG-TMM-SF-StoreService-Path
  friendly_name                       = var.TACG-TMM-SF-StoreService-Name
  authentication_service_virtual_path = citrix_stf_authentication_service.CreateSFAuthenticationService.virtual_path
  pna = {
    enable = false
  }
  farms = var.TACG-TMM-SF-StoreService-Farms
}

##### Create a WebReceiver service
resource "citrix_stf_webreceiver_service" "CreateSFWebReceiverService" {
  depends_on         = [citrix_stf_deployment.CreateInitialSFDeployment, citrix_stf_store_service.CreateSFStoreService]
  site_id            = citrix_stf_deployment.CreateInitialSFDeployment.site_id
  friendly_name      = var.TACG-TMM-SF-WebService-Name
  virtual_path       = var.TACG-TMM-SF-WebService-Path
  store_virtual_path = citrix_stf_store_service.CreateSFStoreService.virtual_path
  authentication_methods = [
    "ExplicitForms",
    "CitrixAGBasic"
  ]
  plugin_assistant = {
    enabled                 = var.TACG-TMM-SF-WebService-PA-Enabled
    html5_single_tab_launch = var.TACG-TMM-SF-WebService-PA-HTML5SingleTab
    upgrade_at_login        = var.TACG-TMM-SF-WebService-PA-UpgradeAtLogin
    html5_enabled           = var.TACG-TMM-SF-WebService-PA-HTML5Enabled
  }
  application_shortcuts = {
    prompt_for_untrusted_shortcuts = var.TACG-TMM-SF-WebService-AppSC-Prompt
    trusted_urls                   = var.TACG-TMM-SF-WebService-AppSC-TrustedURLs
  }
  user_interface = {
    auto_launch_desktop     = var.TACG-TMM-SF-WebService-UI-AutoLaunchDesktop
    multi_click_timeout     = var.TACG-TMM-SF-WebService-UI-Timeout
    enable_apps_folder_view = var.TACG-TMM-SF-WebService-UI-AppFolderView
    workspace_control = {
      enabled                 = var.TACG-TMM-SF-WebService-UI-WC-Enabled
      auto_reconnect_at_logon = var.TACG-TMM-SF-WebService-UI-WC-AutoReconnect
      logoff_action           = var.TACG-TMM-SF-WebService-UI-LogoffAction
      show_reconnect_button   = var.TACG-TMM-SF-WebService-UI-WC-ShowReconnect
      show_disconnect_button  = var.TACG-TMM-SF-WebService-UI-WC-ShowDisconnect
    }
    receiver_configuration = {
      enabled = var.TACG-TMM-SF-WebService-RC-Enabled
    }
    app_shortcuts = {
      enabled               = var.TACG-TMM-SF-WebService-ASC-Enabled
      show_desktop_shortcut = var.TACG-TMM-SF-WebService-ASC-ShowDesktopShortcut
    }
    ui_views = {
      show_apps_view     = var.TACG-TMM-SF-WebService-UIViews-Apps
      show_desktops_view = var.TACG-TMM-SF-WebService-UIViews-Desktops
      default_view       = var.TACG-TMM-SF-WebService-UIViews-Default
    }
    category_view_collapsed   = var.TACG-TMM-SF-WebService-CategoryView
    move_app_to_uncategorized = var.TACG-TMM-SF-WebService-Uncategorized
    progressive_web_app = {
      enabled             = var.TACG-TMM-SF-WebService-PWA-Enabled
      show_install_prompt = var.TACG-TMM-SF-WebService-PWA-Install
    }
    show_activity_manager = var.TACG-TMM-SF-WebService-ActivityManager
    show_first_time_use   = var.TACG-TMM-SF-WebService-FirstTimeUse
    prevent_ica_downloads = var.TACG-TMM-SF-WebService-PreventICADownloads
  }
  resources_service = {
    ica_file_cache_expiry         = var.TACG-TMM-SF-WebService-RS-IcaFileExpiry
    persistent_icon_cache_enabled = var.TACG-TMM-SF-WebService-RS-IconCacheEnabled
  }
}

##### A PNA site is not installed due to no effective needs
#resource "citrix_stf_xenapp_default_store" "CreateSFXenAppPath" {
#  depends_on         = [citrix_stf_store_service.CreateSFStoreService]
#  store_virtual_path = citrix_stf_store_service.CreateSFStoreService.virtual_path
#  store_site_id      = citrix_stf_store_service.CreateSFStoreService.site_id
#
} Important Finally, we want to emphasize again: There are many ways to automate CVAD Components and use IaC, especially across different Hypervisors and Hyperscalers using Terraform and Ansible. Listing all possibilities, snippets, and runtime outputs is out of scope. Please consult the documentation at https://github.com/citrix/terraform-provider-citrix/tree/main/docs/resources for additional examples, or visit our Automation landing page on Citrix TechZone.  Automation of Endpoint ComponentsOne of the most requested Automation tasks from the Endpoint perspective is the automated configuration of the Workspace App. You can configure the Citrix Workspace app using the Global App Configuration service (GACS). It helps you manage the app settings for end users on both managed and unmanaged devices. Settings can be configured for both cloud (Citrix Workspace) and on-premises (Citrix StoreFront™) environments. The Global App Configuration service lets you perform the following functions from a centralized interface: Configure settings for both managed and unmanaged devices (Bring Your Own Devices) Configure settings for multiple stores Update and manage client app agents (for example, Endpoint Analysis, ZTNA) and third-party agents (for example, Zoom, Webex) Automatically update and manage the Citrix Workspace app version for end users Test the configuration before rolling it out to your end-users  Of course, GACS can be configured using Terraform. Important Listing all possible settings is out of scope. To determine which settings are supported on each platform, please consult the GACS documentation at https://developer-docs.citrix.com/en-us/server-integration/global-app-configuration-service/getting-started#/supported-settings-and-their-values-per-platform. Example screenshot:  Let´s have a look at the Terraform snippet: # Configuring Citrix Cloud Global App Configuration Service
## Create Settings
resource "citrix_gac_settings" "TMM-TF-GACS-Settings" {
    service_url = "${var.GACS-ServiceURL}"
    name        = "${var.GACS-Settings-Name}"
    description = "${var.GACS-Settings-Description}"
    test_channel = true

    app_settings = {
        windows = [
            {
                user_override = false,
                category = "root",
                settings = [
                    {
                        name = "Hide advanced preferences",
                        value_string = "true"
                    },
                    {
                        name = "Hide connection center",
                        value_string = "true"
                    }  
                ]
            },
            {
                user_override = false,
                category = "browser",
                settings = [
                    {
                        name = "Incognito Mode Availability",
                        value_string = "Incognito mode available"
                    },
                    {
                        name = "saving browser history disabled",
                        value_string = "true"
                    }
                ]
            },
            {
                user_override = false,
                category = "dazzle",
                settings = [
                    {
                        name = "Put shortcuts on desktop",
                        value_string = "true"
                    }
                ]
            },
            {
                user_override = false,
                category = "ICA® client",
                settings = [
                    {
                        name = "Allow Client Clipboard Redirection",
                        value_string = "true"
                    }
                ]
            }
        ],
        html5 = [
            {
                category = "virtual channel",
                user_override = false,
                settings = [
                    {
                        name = "Clipboard Operations Between VDA And Local Device",
                        value_string = "true"
                    }  
                ]
            },
            {
                category = "toolbar",
                user_override = false,
                settings = [
                    {
                        name = "Clipboard Option in Toolbar",
                        value_string = "true"
                    },
                    {
                        name = "USB Devices Option in Toolbar",
                        value_string = "true"
                    }  
                ]
            }
        ],
        ios = [
            {
                category = "advanced",
                user_override = false,
                settings = [
                    {
                        name = "Touch Enable",
                        value_string = "true"
                    },
                    {
                        name = "Fullscreen Window",
                        value_string = "true"
                    }  
                ]
            }
        ],
        android = [
            {
                category = "advanced",
                user_override = false,
                settings = [
                    {
                        name = "Enable clipboard",
                        value_string = "true"
                    }  
                ]
            }
        ],
        macos = [
            {
                category = "browser",
                user_override = false,
                settings = [
                    {
                        name = "Enable Password Save",
                        value_string = "true"
                    },
                    {
                        name = "Enable Citrix Enterprise Browser™ shortcut",
                        value_string = "true"
                    }
                ]
            }
        ],
        linux = [
            {
                category = "browser",
                user_override = false,
                settings = [
                    {
                        name = "Incognito Mode Availability",
                        value_string = "Incognito mode available"
                    },
                    {
                        name = "Enable Password Save",
                        value_string = "true"
                    }
                ]
            }
        ] 
    }
 } Overview of Automating NetScaler and its ComponentsWith NetScaler infrastructure as code (IaC), you can automate the deployment and management of your NetScaler ADC infrastructure and scale it easily. IaC is the process of managing infrastructure declaratively using code. IaC ensures infrastructure consistency by defining configuration in code. This reduces the risk of misconfigurations that can lead to security vulnerabilities or downtime.  NetScaler IaC also lets you version-control your infrastructure, enabling you to track changes and roll back to a previous version if necessary. And it allows teams to collaborate on the same infrastructure using code repositories and version control systems. NetScaler provides multiple tools to automate your ADC deployments and configurations. This document provides a brief summary of various automation tools and references to resources you can use to manage ADC configurations.  Terraform Provider for Citrix ADCCitrix has developed a custom Terraform provider for automating Citrix ADC deployments and configurations. Using Terraform, you can custom-configure your ADCs for different use cases, such as Load Balancing, SSL, Content Switching, GSLB, and WAF. You can find many examples of using Terraform to configure basic ADC operations or ADC use cases in the GitHub repository: https://github.com/citrix/terraform-provider-citrixadc. Example: Deploying a Citrix ADC HA pair on vSphereConfiguration is based on ADC's preboot config functionality.  Additionally, we need to create ISO images to attach to the ADC VMs as they boot.  To do this, we rely on the mkisofs utility available on Linux platforms.  Make sure that Terraform runs on a Linux host that has this utility installed. Also, note that we need to host the OVF and VMDK files on a host with high-speed connectivity to the target vSphere server. This is to reduce the time it takes to transfer these files. Important During the HA pair's bootstrap process, there is a race condition over which node will be Primary and which Secondary. To remove this race condition, we advise running the Terraform apply command with a single thread. Use terraform apply -parallelism=1 variables.tf variable "nsip" {
  description = "NSIP address"
  type = list(string)
}

variable "gw_ip" {
  description = "Gateway IP address"
  type = string
}

variable "subnetmask" {
  description = "Subnet Mask"
  type = string
}

variable "vsphere_ip" {
  description = "IP Address of vsphere"
  type = string
}

variable "vsphere_username" {
  description = "User name for vsphere"
  type = string
}

variable "vsphere_password" {
  description = "vSphere password"
  sensitive = true
  type = string
}

variable "datacenter_name" {
  description = "Data Center name"
  type = string
}

variable "datastore_name" {
  description = "Data Store Name"
  type = string
}

variable "resource_pool_name" {
  description = "Resource Pool Name"
  type = string
}

variable "resource_host" {
  description = "ESXi host ip"
  type = string
}

variable "nic_01_network_name" {
  description = "Network name for 1/1 NIC"
  type = string
}

variable "virtual_machine_name" {
  description = "Virtual machine names in list(string)"
  type        = string
}

variable "remote_vpx_ovf_path" {
  description = "OVF path"
  type = string
}

variable "memory" {
  description = "VPX memory in MB"
  type = number
}

variable "num_cpus" {
  description = "VPX cpu count"
  type = number
}

variable "iso_output_dir" {
  description = "Directory which will be used to construct userdata iso file"
  type = string
  default = "iso_preboot_config"
}

variable "iso_destination_folder" {
  description = "Target directory in datastore to upload iso file"
  type = string
}provider.tf provider "vsphere" {
  user           = "administrator@vsphere.local"
  password       = "XxXxXxXxXxXxXxXxX"
  vsphere_server = "10.217.100.101"

  # If you have a self-signed cert
  allow_unverified_ssl = true
}

terraform {
  required_providers {
    null = {
      source = "hashicorp/null"
      version = "3.1.1"
    }
  }
}resources.tf data "vsphere_datacenter" "dc" {
  name = var.datacenter_name
}

data "vsphere_datastore" "datastore" {
  name          = var.datastore_name
  datacenter_id = data.vsphere_datacenter.dc.id
}

# https://github.com/hashicorp/terraform-provider-vsphere/issues/262#issuecomment-348644869
# Every cluster will have a default `Resources` resource_pool
data "vsphere_resource_pool" "pool" {
  name          = var.resource_pool_name
  datacenter_id = data.vsphere_datacenter.dc.id
}

data "vsphere_host" "host" {
  name          = var.resource_host
  datacenter_id = data.vsphere_datacenter.dc.id
}

data "vsphere_network" "nic_01" {
  name          = var.nic_01_network_name
  datacenter_id = data.vsphere_datacenter.dc.id
}

# Create iso file

resource "null_resource" "makeiso" {
  provisioner "local-exec" {
    command = format("./process_userdata.py --nsip %s --other-node-nsip %s --netmask %s --gateway %s --output-dir %s-%s", var.nsip[count.index], var.nsip[count.index == 0 ? 1:0], var.subnetmask, var.gw_ip, var.iso_output_dir, count.index + 1)
  }
  count = 2
}

# Upload iso file to datastore

resource "vsphere_file" "upload_iso" {
  datacenter = var.datacenter_name
  datastore = var.datastore_name
  source_file = format("%s-%s.iso", var.iso_output_dir, count.index + 1)
  destination_file = format("%s/%s-%s.iso", var.iso_destination_folder, var.iso_output_dir, count.index + 1)
  create_directories = true

  count = 2

  depends_on = [null_resource.makeiso]
}

# https://registry.terraform.io/providers/hashicorp/vsphere/latest/docs/resources/virtual_machine
resource "vsphere_virtual_machine" "citrixVPX" {
  name             = format("%s-%s", var.virtual_machine_name, count.index + 1)
  resource_pool_id = data.vsphere_resource_pool.pool.id
  datastore_id     = data.vsphere_datastore.datastore.id
  # folder - (Optional) The path to the folder to put this virtual machine in, relative to the datacenter that the resource pool is in.
  # folder = "test-vapp"
  host_system_id             = data.vsphere_host.host.id
  wait_for_guest_net_timeout = 0
  wait_for_guest_ip_timeout  = 0
  datacenter_id              = data.vsphere_datacenter.dc.id

  network_interface {
    network_id = data.vsphere_network.nic_01.id
  }

  # Add more network_interface blocks here

  scsi_type = "lsilogic"

  num_cpus = var.num_cpus
  memory   = var.memory # in MBs

  # https://registry.terraform.io/providers/hashicorp/vsphere/latest/docs/resources/virtual_machine#creating-vm-from-deploying-a-ovfova-template
  ovf_deploy {
    remote_ovf_url           = var.remote_vpx_ovf_path
    enable_hidden_properties = true
    allow_unverified_ssl_cert = true

  }

  cdrom {
    datastore_id  = data.vsphere_datastore.datastore.id
    path = format("%s/%s-%s.iso", var.iso_destination_folder, var.iso_output_dir, count.index + 1)
  }
  
  count = 2
  depends_on = [vsphere_file.upload_iso]
}process_userdata.py #!/usr/bin/env python3

import argparse
import shutil
import os
import subprocess

nspreboot_str = '''\
&lt;NS-PRE-BOOT-CONFIG&gt;
    &lt;NS-CONFIG&gt;
        add route 0.0.0.0 0.0.0.0 %s
        add ha node 1 %s
    &lt;/NS-CONFIG&gt;
    &lt;NS-BOOTSTRAP&gt;
        &lt;SKIP-DEFAULT-BOOTSTRAP&gt;YES&lt;/SKIP-DEFAULT-BOOTSTRAP&gt;
        &lt;NEW-BOOTSTRAP-SEQUENCE&gt;YES&lt;/NEW-BOOTSTRAP-SEQUENCE&gt;

        &lt;MGMT-INTERFACE-CONFIG&gt;
            &lt;INTERFACE-NUM&gt;eth0&lt;/INTERFACE-NUM&gt;
            &lt;IP&gt;%s&lt;/IP&gt;
            &lt;SUBNET-MASK&gt;%s&lt;/SUBNET-MASK&gt;
        &lt;/MGMT-INTERFACE-CONFIG&gt;
    &lt;/NS-BOOTSTRAP&gt;
&lt;/NS-PRE-BOOT-CONFIG&gt;
'''

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('--nsip', required=True)
    parser.add_argument('--other-node-nsip', required=True)
    parser.add_argument('--netmask', default="255.255.255.0")
    parser.add_argument('--gateway', required=True)
    parser.add_argument('--output-dir', default="iso_preboot_config")
    
    args = parser.parse_args()
    output_str = nspreboot_str % (args.gateway, args.other_node_nsip, args.nsip, args.netmask)

    # Check if output directory exists
    if os.path.exists(args.output_dir):
        print('Deleting existing output dir %s', args.output_dir)
        shutil.rmtree(args.output_dir)

    os.makedirs(args.output_dir)
    output_file = os.path.join(args.output_dir, 'userdata')
    with open(output_file, 'w') as fh:
        fh.write(output_str)

    # Create iso file
    iso_output_file = '.'.join([args.output_dir,'iso'])
    cp = subprocess.run(['mkisofs', '-o', iso_output_file, args.output_dir])
    cp.check_returncode()

if __name__ == '__main__':
    main()
 Example: Deploying a Citrix ADC HA pair with an Azure Availability SetThis example shows a standardized deployment of Citrix ADCs in a High Availability set. It can serve as the baseline for all subsequent configurations and use cases. You can find the whole script and variables in the GitHub repository.  The following components will be configured: A Virtual Network with 3 subnets and associated security groups and routing tables Two Citrix ADC instances configured in a High Availability setup main.tf describes the actual config objects to be created. The attributes of these resources are either hard-coded or looked up from input variables in examples.tfvars.  Configuration DetailsThe Citrix ADC instances are deployed with 3 separate NICs each, in separate subnets. The ADC bootstrap code will assign IP addresses to each interface according to the IP addresses assigned by Azure. The management network interface is where the NSIP address is assigned. There are no additional security rules attached to the interface. This means that access is restricted only by the subnet-attached security group, which allows SSH, HTTP, and HTTPS from the controlling subnet. This interface has a public IP address, making it reachable from outside the Virtual Network. For enhanced security, one could remove the public IP. In such a case, the management interface will only be accessible from within the Virtual Network. This means that all SSH connections and NITRO API calls must go through the bastion host. The client network interface is where the VIP address is assigned. This private VIP address is not used for incoming traffic since we are in an HA setup. Instead, the IP address used for traffic is the public IP address of the Azure Load Balancer. This address must be assigned to a Vserver (LB/CS) with functioning backend services. From that point on, the ALB public IP address will start load-balancing the instances. You can find a sample LB configuration in this folder In the event of a failover, the ALB will detect via the probe that one node is going down and the secondary is taking over, at which point it will start sending traffic to the new primary node. Traffic may be impacted for a few seconds while the ALB probe determines the functional state of the HA pair. There are no additional security rules attached to the interface. This means access is restricted only by the subnet-attached security group, which allows HTTP and HTTPS traffic from anywhere. The server network interface is where the SNIP address is assigned. No public ip address is associated with this interface. There are no additional security rules attached to the interface. This means access is restricted only by the subnet's attached security group, which allows traffic only from the subnet itself. An SSH key is required when creating the ADC host. # Azure Resource Group
resource "azurerm_resource_group" "terraform-resource-group" {
  name     = var.resource_group_name
  location = var.location
}

resource "azurerm_availability_set" "terraform-availability-set" {
  name                = "terraform-availability-set"
  location            = var.location
  resource_group_name = azurerm_resource_group.terraform-resource-group.name

  platform_update_domain_count = 2
  platform_fault_domain_count  = 2

}

resource "azurerm_virtual_network" "terraform-virtual-network" {
  name                = "terraform-virtual-network"
  location            = var.location
  resource_group_name = azurerm_resource_group.terraform-resource-group.name
  address_space       = [var.virtual_network_address_space]
}

resource "azurerm_subnet" "terraform-management-subnet" {
  name                 = "terraform-management-subnet"
  resource_group_name  = azurerm_resource_group.terraform-resource-group.name
  virtual_network_name = azurerm_virtual_network.terraform-virtual-network.name
  address_prefixes     = [var.management_subnet_address_prefix]
}

resource "azurerm_subnet" "terraform-server-subnet" {
  name                 = "terraform-server-subnet"
  resource_group_name  = azurerm_resource_group.terraform-resource-group.name
  virtual_network_name = azurerm_virtual_network.terraform-virtual-network.name
  address_prefixes     = [var.server_subnet_address_prefix]
}

resource "azurerm_subnet" "terraform-client-subnet" {
  name                 = "terraform-client-subnet"
  resource_group_name  = azurerm_resource_group.terraform-resource-group.name
  virtual_network_name = azurerm_virtual_network.terraform-virtual-network.name
  address_prefixes     = [var.client_subnet_address_prefix]
}

resource "azurerm_subnet_network_security_group_association" "management-subnet-association" {
  subnet_id                 = azurerm_subnet.terraform-management-subnet.id
  network_security_group_id = azurerm_network_security_group.terraform-management-subnet-security-group.id
}

resource "azurerm_network_security_group" "terraform-management-subnet-security-group" {
  name                = "terraform-management-subnet-security-group"
  location            = var.location
  resource_group_name = azurerm_resource_group.terraform-resource-group.name
}

// Allow ssh, http and https from controlling subnet
resource "azurerm_network_security_rule" "terraform-allow-all-from-controlling-subnet" {
  name                        = "terraform-allow-all-from-controlling-subnet"
  priority                    = 1000
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_ranges     = ["22", "80", "443"]
  source_address_prefix       = var.controlling_subnet
  destination_address_prefix  = "*"
  resource_group_name         = azurerm_resource_group.terraform-resource-group.name
  network_security_group_name = azurerm_network_security_group.terraform-management-subnet-security-group.name
}

resource "azurerm_subnet_network_security_group_association" "client-subnet-association" {
  subnet_id                 = azurerm_subnet.terraform-client-subnet.id
  network_security_group_id = azurerm_network_security_group.terraform-client-subnet-security-group.id
}

resource "azurerm_network_security_group" "terraform-client-subnet-security-group" {
  name                = "terraform-client-subnet-security-group"
  location            = var.location
  resource_group_name = azurerm_resource_group.terraform-resource-group.name
}

// Allow http and https from everywhere
resource "azurerm_network_security_rule" "terraform-allow-client-http-from-internet" {
  name                        = "terraform-allow-client-http-from-internet"
  priority                    = 1000
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_ranges     = ["80", "443"]
  source_address_prefix       = "*"
  destination_address_prefix  = "*"
  resource_group_name         = azurerm_resource_group.terraform-resource-group.name
  network_security_group_name = azurerm_network_security_group.terraform-client-subnet-security-group.name
}

resource "azurerm_subnet_network_security_group_association" "server-subnet-association" {
  subnet_id                 = azurerm_subnet.terraform-server-subnet.id
  network_security_group_id = azurerm_network_security_group.terraform-server-subnet-security-group.id
}

resource "azurerm_network_security_group" "terraform-server-subnet-security-group" {
  name                = "terraform-server-subnet-security-group"
  location            = var.location
  resource_group_name = azurerm_resource_group.terraform-resource-group.name
}

// Next two rules: Allow server subnet to reply only inside its own subnet
resource "azurerm_network_security_rule" "terraform-server-allow-outbound" {
  name                   = "terraform-server-allow-subnet-outbound"
  priority               = 1000
  direction              = "Outbound"
  access                 = "Allow"
  protocol               = "*"
  source_port_range      = "*"
  destination_port_range = "*"
  source_address_prefix  = "*"
  destination_address_prefixes = [
    azurerm_subnet.terraform-server-subnet.address_prefixes[0],
  ]
  resource_group_name         = azurerm_resource_group.terraform-resource-group.name
  network_security_group_name = azurerm_network_security_group.terraform-server-subnet-security-group.name
}

resource "azurerm_network_security_rule" "terraform-server-deny-all-outbound" {
  name                        = "terraform-server-deny-all-outbound"
  priority                    = 1010
  direction                   = "Outbound"
  access                      = "Deny"
  protocol                    = "*"
  source_port_range           = "*"
  destination_port_range      = "*"
  source_address_prefix       = "*"
  destination_address_prefix  = "*"
  resource_group_name         = azurerm_resource_group.terraform-resource-group.name
  network_security_group_name = azurerm_network_security_group.terraform-server-subnet-security-group.name
}

resource "azurerm_public_ip" "terraform-ubuntu-public-ip" {
  name                = "terraform-ubuntu-public-ip"
  resource_group_name = azurerm_resource_group.terraform-resource-group.name
  location            = var.location
  allocation_method   = "Static"
}

resource "azurerm_network_interface" "terraform-ubuntu-management-interface" {
  name                = "terraform-ubuntu-management-interface"
  location            = var.location
  resource_group_name = azurerm_resource_group.terraform-resource-group.name

  ip_configuration {
    name                          = "management"
    subnet_id                     = azurerm_subnet.terraform-management-subnet.id
    private_ip_address_allocation = "Dynamic"
    public_ip_address_id          = azurerm_public_ip.terraform-ubuntu-public-ip.id
  }

  depends_on = [azurerm_subnet_network_security_group_association.management-subnet-association]
}

# ubuntu bastion host deployment
resource "azurerm_linux_virtual_machine" "terraform-ubuntu-machine" {
  name                = "terraform-ubuntu-bastion-machine"
  resource_group_name = azurerm_resource_group.terraform-resource-group.name
  location            = var.location
  size                = var.ubuntu_vm_size
  admin_username      = var.ubuntu_admin_user
  network_interface_ids = [
    azurerm_network_interface.terraform-ubuntu-management-interface.id
  ]

  admin_ssh_key {
    username   = var.ubuntu_admin_user
    public_key = file(var.ssh_public_key_file)
  }

  os_disk {
    caching              = "ReadWrite"
    storage_account_type = "Standard_LRS"
  }

  source_image_reference {
    publisher = "Canonical"
    offer     = "UbuntuServer"
    sku       = "18.04-LTS"
    version   = "latest"
  }
}

resource "azurerm_public_ip" "terraform-adc-management-public-ip" {
  name                = format("terraform-adc-management-public-ip-node-%v", count.index)
  resource_group_name = azurerm_resource_group.terraform-resource-group.name
  location            = var.location
  allocation_method   = "Static"

  count = 2
}

resource "azurerm_network_interface" "terraform-adc-management-interface" {
  name                = format("terraform-adc-management-interface-node-%v", count.index)
  location            = var.location
  resource_group_name = azurerm_resource_group.terraform-resource-group.name

  ip_configuration {
    name                          = "management"
    subnet_id                     = azurerm_subnet.terraform-management-subnet.id
    private_ip_address_allocation = "Dynamic"
    public_ip_address_id          = element(azurerm_public_ip.terraform-adc-management-public-ip.*.id, count.index)
  }
  depends_on = [azurerm_subnet_network_security_group_association.management-subnet-association]

  count = 2
}

resource "azurerm_network_interface" "terraform-adc-client-interface" {
  name                = format("terraform-adc-client-interface-node-%v", count.index)
  location            = var.location
  resource_group_name = azurerm_resource_group.terraform-resource-group.name

  ip_configuration {
    name                          = "client"
    subnet_id                     = azurerm_subnet.terraform-client-subnet.id
    private_ip_address_allocation = "Dynamic"
  }

  depends_on = [azurerm_subnet_network_security_group_association.client-subnet-association]

  count = 2
}

resource "azurerm_network_interface" "terraform-adc-server-interface" {
  name                = format("terraform-adc-server-interface-node-%v", count.index)
  location            = var.location
  resource_group_name = azurerm_resource_group.terraform-resource-group.name

  ip_configuration {
    name                          = "server"
    subnet_id                     = azurerm_subnet.terraform-server-subnet.id
    private_ip_address_allocation = "Dynamic"
  }

  depends_on = [azurerm_subnet_network_security_group_association.server-subnet-association]

  count = 2
}

# Primary Citrix ADC instance deployment
resource "azurerm_virtual_machine" "terraform-primary-adc-machine" {
  name                = "terraform-adc-machine-node-0"
  resource_group_name = azurerm_resource_group.terraform-resource-group.name
  location            = var.location
  vm_size             = var.adc_vm_size

  network_interface_ids = [
    azurerm_network_interface.terraform-adc-management-interface[0].id,
    azurerm_network_interface.terraform-adc-client-interface[0].id,
    azurerm_network_interface.terraform-adc-server-interface[0].id,
  ]

  primary_network_interface_id = azurerm_network_interface.terraform-adc-management-interface[0].id

  os_profile {
    computer_name  = "Citrix-ADC-VPX-node-0"
    admin_username = var.adc_admin_username
    admin_password = var.adc_admin_password
    custom_data = base64encode(&lt;&lt;-EOF
    &lt;NS-PRE-BOOT-CONFIG&gt;
      &lt;NS-CONFIG&gt;
        set systemparameter -promptString "%u@%s"
        add ns ip ${azurerm_network_interface.terraform-adc-client-interface[0].private_ip_address} ${cidrnetmask(var.client_subnet_address_prefix)} -type SNIP
        add ns ip ${azurerm_network_interface.terraform-adc-server-interface[0].private_ip_address} ${cidrnetmask(var.server_subnet_address_prefix)} -type SNIP
        add ha node 1 ${azurerm_network_interface.terraform-adc-management-interface[1].private_ip_address} -inc ENABLED
        set ns rpcNode ${azurerm_network_interface.terraform-adc-management-interface[0].private_ip_address} -password ${var.citrixadc_rpc_node_password} -secure YES
        set ns rpcNode ${azurerm_network_interface.terraform-adc-management-interface[1].private_ip_address} -password ${var.citrixadc_rpc_node_password} -secure YES
      &lt;/NS-CONFIG&gt;
    &lt;/NS-PRE-BOOT-CONFIG&gt;
  EOF
  )
  }

  availability_set_id = azurerm_availability_set.terraform-availability-set.id

  os_profile_linux_config {
    disable_password_authentication = false
    ssh_keys {
      key_data = file(var.ssh_public_key_file)
      path     = format("/home/%v/.ssh/authorized_keys", var.adc_admin_username)
    }
  }

  delete_os_disk_on_termination = true

  storage_os_disk {
    name              = "terraform-citrixadc-os-disk-node-0"
    caching           = "ReadWrite"
    managed_disk_type = "Standard_LRS"
    create_option     = "FromImage"
  }

  storage_image_reference {
    publisher = "citrix"
    offer     = "netscalervpx-130"
    sku       = "netscalerbyol"
    version   = "latest"
  }

  plan {
    name      = "netscalerbyol"
    publisher = "citrix"
    product   = "netscalervpx-130"
  }

  depends_on = [
    azurerm_subnet_network_security_group_association.server-subnet-association,
    azurerm_subnet_network_security_group_association.client-subnet-association,
    azurerm_subnet_network_security_group_association.management-subnet-association,
    azurerm_network_interface_backend_address_pool_association.tf_assoc,
    azurerm_lb_rule.allow_http,
  ]

}

# Secondary Citrix ADC instance deployment
resource "azurerm_virtual_machine" "terraform-secondary-adc-machine" {
  name                = "terraform-adc-machine-node-1"
  resource_group_name = azurerm_resource_group.terraform-resource-group.name
  location            = var.location
  vm_size             = var.adc_vm_size

  network_interface_ids = [
    azurerm_network_interface.terraform-adc-management-interface[1].id,
    azurerm_network_interface.terraform-adc-client-interface[1].id,
    azurerm_network_interface.terraform-adc-server-interface[1].id,
  ]

  primary_network_interface_id = azurerm_network_interface.terraform-adc-management-interface[1].id

  os_profile {
    computer_name  = "Citrix-ADC-VPX-node-1"
    admin_username = var.adc_admin_username
    admin_password = var.adc_admin_password
    custom_data = base64encode(&lt;&lt;-EOF
    &lt;NS-PRE-BOOT-CONFIG&gt;
      &lt;NS-CONFIG&gt;
        set systemparameter -promptString "%u@%s"
        add ns ip ${azurerm_network_interface.terraform-adc-client-interface[1].private_ip_address} ${cidrnetmask(var.client_subnet_address_prefix)} -type SNIP
        add ns ip ${azurerm_network_interface.terraform-adc-server-interface[1].private_ip_address} ${cidrnetmask(var.server_subnet_address_prefix)} -type SNIP
        add ha node 1 ${azurerm_network_interface.terraform-adc-management-interface[0].private_ip_address} -inc ENABLED
        set ns rpcNode ${azurerm_network_interface.terraform-adc-management-interface[0].private_ip_address} -password ${var.citrixadc_rpc_node_password} -secure YES
        set ns rpcNode ${azurerm_network_interface.terraform-adc-management-interface[1].private_ip_address} -password ${var.citrixadc_rpc_node_password} -secure YES
      &lt;/NS-CONFIG&gt;
    &lt;/NS-PRE-BOOT-CONFIG&gt;
  EOF
  )
  }

  availability_set_id = azurerm_availability_set.terraform-availability-set.id

  os_profile_linux_config {
    disable_password_authentication = false
    ssh_keys {
      key_data = file(var.ssh_public_key_file)
      path     = format("/home/%v/.ssh/authorized_keys", var.adc_admin_username)
    }
  }

  delete_os_disk_on_termination = true

  storage_os_disk {
    name              = "terraform-citrixadc-os-disk-node-1"
    caching           = "ReadWrite"
    managed_disk_type = "Standard_LRS"
    create_option     = "FromImage"
  }

  storage_image_reference {
    publisher = "citrix"
    offer     = "netscalervpx-130"
    sku       = "netscalerbyol"
    version   = "latest"
  }

  plan {
    name      = "netscalerbyol"
    publisher = "citrix"
    product   = "netscalervpx-130"
  }

  depends_on = [
    azurerm_subnet_network_security_group_association.server-subnet-association,
    azurerm_subnet_network_security_group_association.client-subnet-association,
    azurerm_subnet_network_security_group_association.management-subnet-association,
    azurerm_network_interface_backend_address_pool_association.tf_assoc,
    azurerm_lb_rule.allow_http,
    azurerm_virtual_machine.terraform-primary-adc-machine,
  ]
}

# Azure Load-balancer 
resource "azurerm_public_ip" "terraform-load-balancer-public-ip" {
  name                = "tf_lb_pubip"
  resource_group_name = azurerm_resource_group.terraform-resource-group.name
  location            = var.location
  allocation_method   = "Static"

}

resource "azurerm_network_interface_backend_address_pool_association" "tf_assoc" {
  network_interface_id    = element(azurerm_network_interface.terraform-adc-client-interface.*.id, count.index)
  ip_configuration_name   = "client"
  backend_address_pool_id = azurerm_lb_backend_address_pool.tf_backend_pool.id

  count = 2
}

resource "azurerm_lb_rule" "allow_http" {
  loadbalancer_id                = azurerm_lb.tf_lb.id
  name                           = "LBRule"
  protocol                       = "Tcp"
  frontend_port                  = 80
  backend_port                   = 80
  frontend_ip_configuration_name = "PublicIPAddress"
  enable_floating_ip             = true
  idle_timeout_in_minutes        = 4
  load_distribution              = "Default"
  probe_id                       = azurerm_lb_probe.tf_probe.id
  backend_address_pool_ids       = [azurerm_lb_backend_address_pool.tf_backend_pool.id]
}

resource "azurerm_lb_backend_address_pool" "tf_backend_pool" {
  loadbalancer_id = azurerm_lb.tf_lb.id
  name            = "BackEndAddressPool"
}

resource "azurerm_lb_probe" "tf_probe" {
  loadbalancer_id     = azurerm_lb.tf_lb.id
  name                = "http-probe"
  port                = 9000
  protocol            = "Tcp"
  interval_in_seconds = 5
  number_of_probes    = 2
}

resource "azurerm_lb" "tf_lb" {
  name                = "tf_lb"
  location            = var.location
  resource_group_name = azurerm_resource_group.terraform-resource-group.name
  sku                 = "Basic"

  frontend_ip_configuration {
    name                 = "PublicIPAddress"
    public_ip_address_id = azurerm_public_ip.terraform-load-balancer-public-ip.id
  }
} Example: Deploying a Citrix ADC HA pair as a NetScaler Gateway with LDAP and RADIUS authenticationThis example shows a standardized deployment of a Citrix ADC HA pair as a NetScaler Gateway with LDAP and RADIUS authentication.  Needed prerequisites: Two NetScaler ADCs are already provisioned in the same subnet All the necessary certificates for gateway configuration should be present in step2_gateway_ldap_radius folder Example code snippets: # Create a HA pair
# add ha node 1 &lt;netscaler2_nsip&gt;
resource "citrixadc_hanode" "netscaler1" {
  hanode_id = 1
  ipaddress = var.netscaler2_nsip
}

# add ha node 1 &lt;netscaler1_nsip&gt;
resource "citrixadc_hanode" "netscaler2" {
  provider  = citrixadc.netscaler2
  hanode_id = 1
  ipaddress = var.netscaler1_nsip

  depends_on = [citrixadc_hanode.netscaler1]
}

resource "citrixadc_systemparameter" "netscaler1_ns_prompt" {
  promptstring = "%u@%s"
}
resource "citrixadc_systemparameter" "netscaler2_ns_prompt" {
  provider     = citrixadc.netscaler2
  promptstring = "%u@%s"
}

# It is best practice to change the RPC node password
# set rpcnode &lt;netscaler1_nsip&gt; -password secretpassword -secure ON
resource "citrixadc_nsrpcnode" "netscaler1to1_rpc_node" {
  ipaddress = var.netscaler1_nsip
  password  = var.rpc_node_password
  secure    = "ON"

  depends_on = [citrixadc_hanode.netscaler1]
}

# It is best practice to change the RPC node password
resource "citrixadc_nsrpcnode" "netscaler1to2_rpc_node" {
  ipaddress = var.netscaler2_nsip
  password  = var.rpc_node_password
  secure    = "ON"

  depends_on = [citrixadc_hanode.netscaler1]
}

# It is best practice to change the RPC node password
resource "citrixadc_nsrpcnode" "netscaler2to1_rpc_node" {
  provider  = citrixadc.netscaler2
  ipaddress = var.netscaler1_nsip
  password  = var.rpc_node_password
  secure    = "ON"

  depends_on = [citrixadc_hanode.netscaler2]
}

# It is best practice to change the RPC node password
resource "citrixadc_nsrpcnode" "netscaler2to2_rpc_node" {
  provider  = citrixadc.netscaler2
  ipaddress = var.netscaler2_nsip
  password  = var.rpc_node_password
  secure    = "ON"

  depends_on = [citrixadc_hanode.netscaler2]
}HA pair creation is finished; proceed to the next step. This code snippet is quite long – we want to show a complex example of an IaC-based deployment. # Create the NS Gateway based on the HA pair created earlier

resource "citrixadc_nsvpxparam" "tf_nsvpxparam" {
  cpuyield = "YES"
}

# set HA node -failSafe ON -maxFlips 3 -maxFlipTime 1200 
resource "citrixadc_hanode" "local_node" {
  hanode_id   = 0 //the id of local_node is always 0
  failsafe    = "ON"
  maxflips    = 3
  maxfliptime = 1200
}

############################ 
# Upload SSL certificates 
############################# 
resource "citrixadc_systemfile" "ns_server_cert" {
  filename     = var.servercertfile_name
  filelocation = "/nsconfig/ssl/"
  filecontent  = file("${path.module}/${var.servercertfile_name}")
}
resource "citrixadc_systemfile" "ns_server_key" {
  filename     = var.servekeyfile_name
  filelocation = "/nsconfig/ssl/"
  filecontent  = file("${path.module}/${var.servekeyfile_name}")
}

#Intermediate certificates 
# scp remote_mycoolcompany_com.ca-bundle nsroot@10.0.10.10:/nsconfig/ssl/ 
# scp remote_mycoolcompany_com.ca-bundle_ic1 nsroot@10.0.10.10:/nsconfig/ssl/ 
resource "citrixadc_systemfile" "remote_mycoolcompany_com_ca-bundle" {
  filename     = var.intermediate_certificate1_name
  filelocation = "/nsconfig/ssl/"
  filecontent  = file("${path.module}/${var.intermediate_certificate1_name}")
}
resource "citrixadc_systemfile" "remote_mycoolcompany_com_ca-bundle_ic1" {
  filename     = var.intermediate_certificate2_name
  filelocation = "/nsconfig/ssl/"
  filecontent  = file("${path.module}/${var.intermediate_certificate2_name}")
}

#Gateway certificate and key 
# scp remote_mycoolcompany_com.crt nsroot@10.0.10.10:/nsconfig/ssl/ 
# scp "remote.mycoolcompany.com_key3.txt" nsroot@10.0.10.10:/nsconfig/ssl/ 
resource "citrixadc_systemfile" "remote_mycoolcompany_com_crt" {
  filename     = var.gateway_certfile_name
  filelocation = "/nsconfig/ssl/"
  filecontent  = file("${path.module}/${var.gateway_certfile_name}")
}
resource "citrixadc_systemfile" "remote_mycoolcompany_com_key" {
  filename     = var.gateway_keyfile_name
  filelocation = "/nsconfig/ssl/"
  filecontent  = file("${path.module}/${var.gateway_keyfile_name}")
}

############################# 
# Base configuration 
############################# 

#This command is only for the Primary node 
# set ns ip 10.0.10.10 -vServer DISABLED -gui SECUREONLY -mgmtAccess ENABLED -restrictAccess ENABLED 
#This command is only for the Secondary node 
# set ns ip 10.0.10.11 -vServer DISABLED -gui SECUREONLY -mgmtAccess ENABLED -restrictAccess ENABLED 

resource "citrixadc_nsip" "nsip_qenzcpieio" {
  ipaddress      = var.snip_ip_address
  netmask        = var.snip_netmask
  vserver        = "DISABLED"
  gui            = "SECUREONLY"
  mgmtaccess     = "ENABLED"
  restrictaccess = "ENABLED"
}

# No need as there will be default route associated
# resource "citrixadc_route" "route_zluebtokju" {
#   count   = length(var.route)
#   network = var.route[count.index]["network"]
#   netmask = var.route[count.index]["netmask"]
#   gateway = var.route[count.index]["gateway"]
#   depends_on = [
#     citrixadc_nsip.nsip_qenzcpieio
#   ]
# }

# #Add SSL certificates 
# add ssl certKey Intermediate_SSL_certificate -cert remote_mycoolcompany_com.ca-bundle 
# add ssl certKey Intermediate_SSL_certificate_ic1 -cert remote_mycoolcompany_com.ca-bundle_ic1 
# add ssl certKey remote.mycoolcompany.com -cert remote_mycoolcompany_com.crt -key "remote.mycoolcompany.com_key 3.txt" 

# #Link SSL certificates to intermediate certificates 
# link ssl certKey Intermediate_SSL_certificate Intermediate_SSL_certificate_ic1 
# link ssl certKey remote.mycoolcompany.com Intermediate_SSL_certificate 

# Above configuration is implemeted in below terraform resource block
# Need to check on link ssl certKey Intermediate_SSL_certificate Intermediate_SSL_certificate_ic1  
resource "citrixadc_sslcertkey" "sslcertkey_yactrltynp" { #Issuer certificate mismatch
  certkey = "Intermediate_SSL_certificate"
  cert    = "remote_mycoolcompany_com.ca-bundle"
  linkcertkeyname = citrixadc_sslcertkey.sslcertkey_todfwaybti.certkey
  depends_on = [
    citrixadc_systemfile.remote_mycoolcompany_com_ca-bundle
  ]
}
resource "citrixadc_sslcertkey" "sslcertkey_todfwaybti" {
  certkey = "Intermediate_SSL_certificate_ic1"
  cert    = "remote_mycoolcompany_com.ca-bundle_ic1"
  depends_on = [
    citrixadc_systemfile.remote_mycoolcompany_com_ca-bundle_ic1
  ]
}
# link ssl certKey remote.mycoolcompany.com Intermediate_SSL_certificate 
resource "citrixadc_sslcertkey" "tf_sslcertkey" {
  certkey = "remote.mycoolcompany.com"
  cert    = "remote_mycoolcompany_com.crt"
  key     = "remote.mycoolcompany.com_key3.txt"
  linkcertkeyname = citrixadc_sslcertkey.sslcertkey_yactrltynp.certkey
  depends_on = [
    citrixadc_systemfile.remote_mycoolcompany_com_crt,
    citrixadc_systemfile.remote_mycoolcompany_com_key
  ]
}

resource "citrixadc_nsfeature" "tf_nsfeature" {
  cs        = true
  lb        = true
  ssl       = true
  cmp       = true
  sslvpn    = true
  aaa       = true
  rewrite   = true
  responder = true
  wl        = false
}
resource "citrixadc_nsmode" "tf_nsmode" {
  l3   = false
  edge = false
}
resource "citrixadc_nshttpparam" "tf_nshttpparam" {
  dropinvalreqs   = "ON"
  markhttp09inval = "ON"
}
resource "citrixadc_nsparam" "tf_nsparam" {
  cookieversion = 1
}
resource "citrixadc_systemparameter" "tf_systemparameter" {
  promptstring = var.promptstring
  maxclient    = 40
}

resource "citrixadc_systemuser" "tf_systemuser" {
  username     = "ADM-Service-Account"
  password     = "securepassword"
  externalauth = "DISABLED"
}
# set system user nsroot -externalAuth DISABLED

resource "citrixadc_systemgroup" "tf_systemgroup1" {
  groupname = var.systemgroup1_name
  cmdpolicybinding {
    policyname = "read-only"
    priority   = 100
  }
}
resource "citrixadc_systemgroup" "tf_systemgroup2" {
  groupname = var.systemgroup2_name
  cmdpolicybinding {
    policyname = "operator"
    priority   = 100
  }
}
resource "citrixadc_systemgroup" "tf_systemgroup3" {
  groupname = var.systemgroup3_name
  cmdpolicybinding {
    policyname = "network"
    priority   = 100
  }
}
resource "citrixadc_systemgroup" "tf_systemgroup4" {
  groupname = var.systemgroup4_name
  cmdpolicybinding {
    policyname = "sysadmin"
    priority   = 100
  }
}
resource "citrixadc_systemgroup" "tf_systemgroup5" {
  groupname = var.systemgroup5_name
  cmdpolicybinding {
    policyname = "superuser"
    priority   = 100
  }
}

resource "citrixadc_authenticationldapaction" "tf_authenticationldapaction_Management_LDAP_Server" {
  name               = var.management_authenticationldapaction_name
  servername         = var.management_authenticationldapaction_servername
  serverport         = 636
  ldapbase           = var.management_authenticationldapaction_ldapbase
  ldapbinddn         = var.management_authenticationldapaction_ldapbinddn
  ldapbinddnpassword = var.management_authenticationldapaction_ldapbinddnpassword
  ldaploginname      = "sAMAccountName"
  searchfilter       = var.management_authenticationldapaction_searchfilter
  groupattrname      = "memberOf"
  subattributename   = "cn"
  sectype            = "SSL"
  passwdchange       = "ENABLED"
}
resource "citrixadc_authenticationpolicy" "tf_Management_LDAP_Policy" {
  name   = "Management_LDAP_Policy"
  rule   = "true"
  action = citrixadc_authenticationldapaction.tf_authenticationldapaction_Management_LDAP_Server.name
}
resource "citrixadc_systemglobal_authenticationpolicy_binding" "systemglobal__binding_gwtebwywgn" {
  policyname             = citrixadc_authenticationpolicy.tf_Management_LDAP_Policy.name
  priority               = 100
  gotopriorityexpression = "NEXT"
}
resource "citrixadc_nshostname" "tf_nshostname" {
  hostname = "HA-NetScaler-Pair"
}
resource "citrixadc_sslparameter" "sslparameter_zhfnbtqegq" {
  defaultprofile = "ENABLED"
}
resource "citrixadc_sslcipher" "sslcipher_eogidyupcx" {
  ciphergroupname = "SSL_Labs_Cipher_Group_Q4_2021"
  ciphersuitebinding {
    ciphername     = "TLS1.3-AES256-GCM-SHA384"
    cipherpriority = 1
  }
  ciphersuitebinding {
    ciphername     = "TLS1.3-AES128-GCM-SHA256"
    cipherpriority = 2
  }
  ciphersuitebinding {
    ciphername     = "TLS1.3-CHACHA20-POLY1305-SHA256"
    cipherpriority = 3
  }
  ciphersuitebinding {
    ciphername     = "TLS1.2-ECDHE-ECDSA-AES256-GCM-SHA384"
    cipherpriority = 4
  }
  ciphersuitebinding {
    ciphername     = "TLS1.2-ECDHE-ECDSA-AES128-GCM-SHA256"
    cipherpriority = 5
  }
  ciphersuitebinding {
    ciphername     = "TLS1.2-ECDHE-ECDSA-AES256-SHA384"
    cipherpriority = 6
  }
  ciphersuitebinding {
    ciphername     = "TLS1.2-ECDHE-RSA-AES256-GCM-SHA384"
    cipherpriority = 7
  }
}
resource "citrixadc_sslprofile" "sslprofile_nezvhaeqqj" {
  name             = "SSL_Labs_Profile_Q4_2021"
  tls1             = "DISABLED"
  tls11            = "DISABLED"
  tls12            = "ENABLED"
  tls13            = "ENABLED"
  denysslreneg     = "NONSECURE"
  hsts             = "ENABLED"
  maxage           = 157680000
  ecccurvebindings = ["P_256", "P_384", "P_224", "P_521"]
  depends_on       = [citrixadc_sslparameter.sslparameter_zhfnbtqegq]
}

# Replace the ciphers in the SSL profile 
# unbind ssl profile SSL_Labs_Profile_Q4_2021 -cipherName DEFAULT #not supported
# bind ssl profile SSL_Labs_Profile_Q4_2021 -cipherName SSL_Labs_Cipher_Group_Q4_2021 
resource "citrixadc_sslprofile_sslcipher_binding" "sslprofile__binding_bydrzyttal" {
  name           = citrixadc_sslprofile.sslprofile_nezvhaeqqj.name #"SSL_Labs_Profile_Q4_2021"
  ciphername     = citrixadc_sslcipher.sslcipher_eogidyupcx.ciphergroupname
  cipherpriority = 1
}

resource "citrixadc_snmpcommunity" "snmpcommunity_nasflquehd" {
  communityname = var.snmpcommunity_name
  permissions   = "ALL"
}
resource "citrixadc_snmpmanager" "snmpmanager_kbyotszmda" {
  ipaddress = var.snmpmanager1_ipaddress
  netmask   = "255.255.255.255"
}
resource "citrixadc_snmpmanager" "snmpmanager_znnvjqhxnl" {
  ipaddress = var.snmpmanager2_ipaddress
  netmask   = "255.255.255.255"
}
resource "citrixadc_snmpalarm" "snmpalarm_dhellhzjiq" {
  trapname       = "CPU-USAGE"
  thresholdvalue = 80
  normalvalue    = 35
  severity       = "Informational"
}
resource "citrixadc_snmpalarm" "snmpalarm_qiydzhgjub" {
  trapname = "HA-STATE-CHANGE"
  severity = "Critical"
}
resource "citrixadc_snmpalarm" "snmpalarm_jojguqjrzr" {
  trapname       = "MEMORY"
  thresholdvalue = 80
  normalvalue    = 35
  severity       = "Critical"
}
resource "citrixadc_snmpview" "snmpview_jwytycajii" {
  name    = "all_group_view"
  subtree = 1
  type    = "included"
}
resource "citrixadc_snmpgroup" "snmpgroup_poscxdyegh" {
  name          = "all_group"
  securitylevel = "authPriv"
  readviewname  = "all_group_view"
}
resource "citrixadc_snmpuser" "snmpuser_moivcqwmvb" {
  name       = var.snmpuser_name
  group      = "all_group"
  authtype   = "SHA"
  authpasswd = var.snmpuser_authpasswd
  privtype   = "AES"
  privpasswd = var.snmpuser_privpasswd
}

resource "citrixadc_snmptrap" "snmptrap_bpldwoskfn" {
  trapclass       = "generic"
  trapdestination = var.snmptrap1_trapdestination
  version         = "V3"
}
resource "citrixadc_snmptrap" "snmptrap_mquunqimgl" {
  trapclass       = "generic"
  trapdestination = var.snmptrap2_trapdestination
  version         = "V3"
}
resource "citrixadc_snmptrap_snmpuser_binding" "snmptrap__binding_rkcthyuxwg" {
  trapclass       = citrixadc_snmptrap.snmptrap_bpldwoskfn.trapclass
  trapdestination = citrixadc_snmptrap.snmptrap_bpldwoskfn.trapdestination
  username        = citrixadc_snmpuser.snmpuser_moivcqwmvb.name
  securitylevel   = "authPriv"
}
resource "citrixadc_snmptrap_snmpuser_binding" "snmptrap__binding_cwvuxvrjvn" {
  trapclass       = citrixadc_snmptrap.snmptrap_mquunqimgl.trapclass
  trapdestination = citrixadc_snmptrap.snmptrap_mquunqimgl.trapdestination
  username        = citrixadc_snmpuser.snmpuser_moivcqwmvb.name
  securitylevel   = "authPriv"
}

resource "citrixadc_auditsyslogaction" "auditsyslogaction_mgiickzjzf" {
  name                = var.auditsyslogaction_name
  serverip            = var.auditsyslogaction_serverip
  loglevel            = ["EMERGENCY", "ALERT", "CRITICAL", "ERROR", "WARNING"]
  acl                 = "ENABLED"
  timezone            = "LOCAL_TIME"
  userdefinedauditlog = "YES"
}
resource "citrixadc_auditsyslogpolicy" "auditsyslogpolicy_itefscdolv" {
  name   = "Syslog-Policy-1" #Syslog Policy 1
  rule   = "true"
  action = citrixadc_auditsyslogaction.auditsyslogaction_mgiickzjzf.name
}

# #Globally bind the Syslog Policy 
resource "citrixadc_auditsyslogglobal_auditsyslogpolicy_binding" "auditsyslogglobal__binding_kqxnpyfgzd" {
  policyname = citrixadc_auditsyslogpolicy.auditsyslogpolicy_itefscdolv.name
  priority   = 100
}

############################# 
# Create Load Balancers and configure DNS 
############################# 

resource "citrixadc_server" "server_sqztnaumda" {
  name      = var.dns_server1_ipaddress
  ipaddress = var.dns_server1_ipaddress
}
resource "citrixadc_server" "server_wsadkcqflz" {
  name      = var.dns_server2_ipaddress
  ipaddress = var.dns_server2_ipaddress
}
resource "citrixadc_server" "server_pfutrwnpwf" {
  name      = var.server3_ipaddress
  ipaddress = var.server3_ipaddress
}
resource "citrixadc_server" "server_pxjminslbl" {
  name      = var.storefront_server1_ipaddress
  ipaddress = var.storefront_server1_ipaddress
}
resource "citrixadc_server" "server_zbsxvzdore" {
  name      = var.storefront_server2_ipaddress
  ipaddress = var.storefront_server2_ipaddress
}
resource "citrixadc_server" "server_jynqmiwzru" {
  name      = var.ldap_server1_ipaddress
  ipaddress = var.ldap_server1_ipaddress
}
resource "citrixadc_server" "server_airvursjkh" {
  name      = var.ldap_server2_ipaddress
  ipaddress = var.ldap_server2_ipaddress
}
resource "citrixadc_server" "server_hjbevwqkdv" {
  name      = var.radius_server_ipaddress
  ipaddress = var.radius_server_ipaddress
}
resource "citrixadc_servicegroup" "servicegroup_eumxhmjuac" {
  servicegroupname = "DNS_TCP_SVG"
  servicetype      = "DNS_TCP"
  maxclient        = "0"
  maxreq           = "0"
  cip              = "DISABLED"
  usip             = "NO"
  useproxyport     = "YES"
  clttimeout       = 180
  svrtimeout       = 360
  cka              = "NO"
  tcpb             = "NO"
  cmp              = "NO"
}
resource "citrixadc_servicegroup" "servicegroup_chxikudgsk" {
  servicegroupname = "DNS_UDP_SVG"
  servicetype      = "DNS"
  maxclient        = "0"
  maxreq           = "0"
  cip              = "DISABLED"
  usip             = "NO"
  useproxyport     = "NO"
  clttimeout       = 120
  svrtimeout       = 120
  cka              = "NO"
  tcpb             = "NO"
  cmp              = "NO"
}
resource "citrixadc_servicegroup" "servicegroup_ahpzkekxep" {
  servicegroupname = "StoreFront_SVG"
  servicetype      = "SSL"
  maxclient        = "0"
  maxreq           = "0"
  cip              = "DISABLED"
  usip             = "NO"
  useproxyport     = "YES"
  clttimeout       = 180
  svrtimeout       = 360
  cka              = "NO"
  tcpb             = "NO"
  cmp              = "YES"
}
resource "citrixadc_servicegroup" "servicegroup_vvvkkztink" {
  servicegroupname = "LDAP_SVG"
  servicetype      = "TCP"
  maxclient        = "0"
  maxreq           = "0"
  cip              = "DISABLED"
  usip             = "NO"
  useproxyport     = "YES"
  clttimeout       = 9000
  svrtimeout       = 9000
  cka              = "NO"
  tcpb             = "NO"
  cmp              = "NO"
}
resource "citrixadc_servicegroup" "servicegroup_ndsbbzqxlp" {
  servicegroupname = "LDAP_TLS_Offload_SVG"
  servicetype      = "SSL_TCP"
  maxclient        = "0"
  maxreq           = "0"
  cip              = "DISABLED"
  usip             = "NO"
  useproxyport     = "YES"
  clttimeout       = 9000
  svrtimeout       = 9000
  cka              = "NO"
  tcpb             = "NO"
  cmp              = "NO"
}
resource "citrixadc_servicegroup" "servicegroup_pkmvqajaxw" {
  servicegroupname = "RADIUS_SVG"
  servicetype      = "RADIUS"
  maxclient        = "0"
  maxreq           = "0"
  cip              = "DISABLED"
  usip             = "NO"
  useproxyport     = "NO"
  clttimeout       = 120
  svrtimeout       = 120
  cka              = "NO"
  tcpb             = "NO"
  cmp              = "NO"
}
resource "citrixadc_lbvserver" "dns_tcp_lbvserver" {
  name            = var.dns_tcp_lbvserver_name
  servicetype     = var.dns_tcp_lbvserver_servicetype
  ipv46           = var.dns_tcp_lbvserver_ipv46
  persistencetype = "NONE"
  clttimeout      = 180
}
resource "citrixadc_lbvserver" "dns_udp_lbvserver" {
  name            = var.dns_udp_lbvserver_name
  servicetype     = var.dns_udp_lbvserver_servicetype
  ipv46           = var.dns_udp_lbvserver_ipv46
  persistencetype = "NONE"
  clttimeout      = 120
}

resource "citrixadc_lbvserver" "lbvserver_ymgredddqh" {
  name            = var.storefront_lbvserver_name
  servicetype     = var.storefront_lbvserver_servicetype
  ipv46           = var.storefront_lbvserver_ipv46
  port            = 443
  persistencetype = "COOKIEINSERT"
  clttimeout      = 180
}
resource "citrixadc_lbvserver" "lbvserver_pfxwimkyah" {
  name            = var.ldap_lbvserver_name
  servicetype     = var.ldap_lbvserver_servicetype
  ipv46           = var.ldap_lbvserver_ipv46
  port            = 636
  persistencetype = "SOURCEIP"
  clttimeout      = 9000
}
resource "citrixadc_lbvserver" "lbvserver_aretuuhedg" {
  name            = var.ldap_tls_offload_lbvserver_name
  servicetype     = var.ldap_tls_offload_lbvserver_servicetype
  ipv46           = var.ldap_tls_offload_lbvserver_ipv46
  port            = 637
  persistencetype = "SOURCEIP"
  clttimeout      = 9000
}
resource "citrixadc_lbvserver" "lbvserver_ewlwbfnjnk" {
  name            = var.radius_lbvserver_name
  servicetype     = var.radius_lbvserver_servicetype
  ipv46           = var.radius_lbvserver_ipv46
  port            = 1812
  persistencetype = "RULE"
  lbmethod        = "TOKEN"
  rule            = "CLIENT.UDP.RADIUS.USERNAME"
  clttimeout      = 120
}

resource "citrixadc_lbvserver_servicegroup_binding" "lbvserver__binding_wegcwdmyfm" {
  name             = citrixadc_lbvserver.dns_tcp_lbvserver.name                      # "DNS_TCP_LB"
  servicegroupname = citrixadc_servicegroup.servicegroup_eumxhmjuac.servicegroupname #"DNS_TCP_SVG"
}
resource "citrixadc_lbvserver_servicegroup_binding" "lbvserver__binding_knazcgzimi" {
  name             = citrixadc_lbvserver.dns_udp_lbvserver.name                      #"DNS_UDP_LB"
  servicegroupname = citrixadc_servicegroup.servicegroup_chxikudgsk.servicegroupname #"DNS_UDP_SVG"
}
resource "citrixadc_lbvserver_servicegroup_binding" "lbvserver__binding_nlsolummnp" {
  name             = citrixadc_lbvserver.lbvserver_ymgredddqh.name                   #"StoreFront_LB"
  servicegroupname = citrixadc_servicegroup.servicegroup_ahpzkekxep.servicegroupname #"StoreFront_SVG"
}
resource "citrixadc_lbvserver_servicegroup_binding" "lbvserver__binding_kiccugrtpf" {
  name             = citrixadc_lbvserver.lbvserver_pfxwimkyah.name                   #"LDAP_LB"
  servicegroupname = citrixadc_servicegroup.servicegroup_vvvkkztink.servicegroupname #"LDAP_SVG"
}
resource "citrixadc_lbvserver_servicegroup_binding" "lbvserver__binding_pzxtoylktl" {
  name             = citrixadc_lbvserver.lbvserver_aretuuhedg.name                   #"LDAP_TLS_Offload_LB"
  servicegroupname = citrixadc_servicegroup.servicegroup_ndsbbzqxlp.servicegroupname #"LDAP_TLS_Offload_SVG"
}
resource "citrixadc_lbvserver_servicegroup_binding" "lbvserver__binding_lkhrkfmmmm" {
  name             = citrixadc_lbvserver.lbvserver_ewlwbfnjnk.name                   #"RADIUS_LB"
  servicegroupname = citrixadc_servicegroup.servicegroup_pkmvqajaxw.servicegroupname #"RADIUS_SVG"
}

resource "citrixadc_lbmonitor" "lbmonitor_hcfaaeslbw" {
  monitorname = var.dns_tcp_lbmonitor_name
  type        = var.dns_tcp_lbmonitor_type
  query       = var.dns_tcp_lbmonitor_query
  querytype   = "Address"
  lrtm        = "DISABLED"
  interval    = 6
  resptimeout = 3
  downtime    = 20
  destport    = 53
}
resource "citrixadc_lbmonitor" "lbmonitor_chovwassas" {
  monitorname = var.dns_udp_lbmonitor_name
  type        = var.dns_udp_lbmonitor_type
  query       = var.dns_udp_lbmonitor_query
  querytype   = "Address"
  lrtm        = "DISABLED"
  interval    = 6
  resptimeout = 3
  downtime    = 20
  destport    = 53
}
resource "citrixadc_lbmonitor" "lbmonitor_trnqrgntrg" {
  monitorname    = var.storefront_lbmonitor_name
  type           = var.storefront_lbmonitor_type
  scriptname     = "nssf.pl"
  dispatcherip   = var.storefront_lbmonitor_dispatcherip
  dispatcherport = 3013
  lrtm           = "DISABLED"
  interval       = 30
  resptimeout    = 5
  downtime       = 20
  secure         = "YES"
  storename      = var.storefront_lbmonitor_storename
}
resource "citrixadc_lbmonitor" "lbmonitor_uizgmebaiu" {
  monitorname    = var.ldap_lbmonitor_name
  type           = var.ldap_lbmonitor_type
  scriptname     = "nsldap.pl"
  dispatcherip   = var.ldap_lbmonitor_dispatcherip
  dispatcherport = 3013
  password       = var.ldap_lbmonitor_password
  secure         = "YES"
  basedn         = var.ldap_lbmonitor_basedn
  binddn         = var.ldap_lbmonitor_binddn
  filter         = var.ldap_lbmonitor_filter
}
resource "citrixadc_lbmonitor" "lbmonitor_swzbkbrwrr" {
  monitorname = var.radius_lbmonitor_name
  type        = var.radius_lbmonitor_type
  respcode    = ["3"]
  username    = var.radius_lbmonitor_username
  password    = var.radius_lbmonitor_password
  radkey      = var.radius_lbmonitor_radkey
  lrtm        = "DISABLED"
}

resource "citrixadc_servicegroup_servicegroupmember_binding" "servicegroup__binding_hxwtyofwtm" {
  servicegroupname = citrixadc_servicegroup.servicegroup_eumxhmjuac.servicegroupname #"DNS_TCP_SVG"
  ip               = citrixadc_server.server_sqztnaumda.ipaddress                    #"8.8.8.8"
  port             = 53
  weight           = "10"
}
resource "citrixadc_servicegroup_servicegroupmember_binding" "servicegroup__binding_pzsbmgfxsq" {
  servicegroupname = citrixadc_servicegroup.servicegroup_eumxhmjuac.servicegroupname #"DNS_TCP_SVG"
  ip               = citrixadc_server.server_wsadkcqflz.ipaddress                    #"1.1.1.1"
  port             = 53
  weight           = "10"
}
resource "citrixadc_servicegroup_lbmonitor_binding" "servicegroup__binding_encckgfcuq" {
  servicegroupname = citrixadc_servicegroup.servicegroup_eumxhmjuac.servicegroupname #"DNS_TCP_SVG"
  monitorname      = citrixadc_lbmonitor.lbmonitor_hcfaaeslbw.monitorname            #"DNS_TCP_monitor"
  weight           = "80"
}
resource "citrixadc_servicegroup_servicegroupmember_binding" "servicegroup__binding_dunulodvsu" {
  servicegroupname = citrixadc_servicegroup.servicegroup_chxikudgsk.servicegroupname #"DNS_UDP_SVG"
  ip               = citrixadc_server.server_sqztnaumda.ipaddress                    #"8.8.8.8"
  port             = 53
  weight           = "10"
}
resource "citrixadc_servicegroup_servicegroupmember_binding" "servicegroup__binding_ddikktifmu" {
  servicegroupname = citrixadc_servicegroup.servicegroup_chxikudgsk.servicegroupname #"DNS_UDP_SVG"
  ip               = citrixadc_server.server_wsadkcqflz.ipaddress                    #"1.1.1.1"
  port             = 53
  weight           = "10"
}
resource "citrixadc_servicegroup_lbmonitor_binding" "servicegroup__binding_dbwtlpttjz" {
  servicegroupname = citrixadc_servicegroup.servicegroup_chxikudgsk.servicegroupname #"DNS_UDP_SVG"
  monitorname      = citrixadc_lbmonitor.lbmonitor_chovwassas.monitorname            #"DNS_UDP_monitor"
  weight           = "80"
}
resource "citrixadc_servicegroup_servicegroupmember_binding" "servicegroup__binding_jfgnghruur" {
  servicegroupname = citrixadc_servicegroup.servicegroup_ahpzkekxep.servicegroupname #"StoreFront_SVG"
  ip               = citrixadc_server.server_zbsxvzdore.ipaddress                    #"192.168.3.31"
  port             = 443
}
resource "citrixadc_servicegroup_servicegroupmember_binding" "servicegroup__binding_oejyktuylb" {
  servicegroupname = citrixadc_servicegroup.servicegroup_ahpzkekxep.servicegroupname #"StoreFront_SVG"
  ip               = citrixadc_server.server_pxjminslbl.ipaddress                    #"192.168.3.30"
  port             = 443
}
resource "citrixadc_servicegroup_lbmonitor_binding" "servicegroup__binding_oaemujffdq" {
  servicegroupname = citrixadc_servicegroup.servicegroup_ahpzkekxep.servicegroupname #"StoreFront_SVG"
  monitorname      = citrixadc_lbmonitor.lbmonitor_trnqrgntrg.monitorname            #"StoreFront_monitor"
}
resource "citrixadc_servicegroup_servicegroupmember_binding" "servicegroup__binding_uqjotkmwph" {
  servicegroupname = citrixadc_servicegroup.servicegroup_vvvkkztink.servicegroupname #"LDAP_SVG"
  ip               = citrixadc_server.server_airvursjkh.ipaddress                    #"192.168.3.11"
  port             = 636
}
resource "citrixadc_servicegroup_servicegroupmember_binding" "servicegroup__binding_ebmkappfpg" {
  servicegroupname = citrixadc_servicegroup.servicegroup_vvvkkztink.servicegroupname #"LDAP_SVG"
  ip               = citrixadc_server.server_jynqmiwzru.ipaddress                    #"192.168.3.10"
  port             = 636
}
resource "citrixadc_servicegroup_lbmonitor_binding" "servicegroup__binding_apzkavrvpq" {
  servicegroupname = citrixadc_servicegroup.servicegroup_vvvkkztink.servicegroupname #"LDAP_SVG"
  monitorname      = citrixadc_lbmonitor.lbmonitor_uizgmebaiu.monitorname            #"LDAP_MON"
}

resource "citrixadc_servicegroup_servicegroupmember_binding" "servicegroup__binding_sdfgertyui" {
  servicegroupname = citrixadc_servicegroup.servicegroup_ndsbbzqxlp.servicegroupname #"LDAP_TLS_Offload_SVG"
  ip               = citrixadc_server.server_airvursjkh.ipaddress                    #"192.168.3.11"
  port             = 636
}
resource "citrixadc_servicegroup_servicegroupmember_binding" "servicegroup__binding_sertfergwt" {
  servicegroupname = citrixadc_servicegroup.servicegroup_ndsbbzqxlp.servicegroupname #"LDAP_TLS_Offload_SVG"
  ip               = citrixadc_server.server_jynqmiwzru.ipaddress                    #"192.168.3.10"
  port             = 636
}
resource "citrixadc_servicegroup_lbmonitor_binding" "servicegroup__binding_qlcdvznpfp" {
  servicegroupname = citrixadc_servicegroup.servicegroup_ndsbbzqxlp.servicegroupname #"LDAP_TLS_Offload_SVG"
  monitorname      = citrixadc_lbmonitor.lbmonitor_uizgmebaiu.monitorname            #"LDAP_MON"
}
resource "citrixadc_servicegroup_servicegroupmember_binding" "servicegroup__binding_xiwzfksboa" {
  servicegroupname = citrixadc_servicegroup.servicegroup_pkmvqajaxw.servicegroupname #"RADIUS_SVG"
  ip               = citrixadc_server.server_hjbevwqkdv.ipaddress                    #"192.168.3.20"
  port             = 1812
}
resource "citrixadc_servicegroup_lbmonitor_binding" "servicegroup__binding_kyofxsqqgl" {
  servicegroupname = citrixadc_servicegroup.servicegroup_pkmvqajaxw.servicegroupname #"RADIUS_SVG"
  monitorname      = citrixadc_lbmonitor.lbmonitor_swzbkbrwrr.monitorname            #"RADIUS_MON"
}

resource "citrixadc_sslvserver_sslcertkey_binding" "sslvserver__binding_cavebdpwll" {
  vservername = citrixadc_lbvserver.lbvserver_ymgredddqh.name #"StoreFront_LB"
  certkeyname = citrixadc_sslcertkey.tf_sslcertkey.certkey    #"remote.mycoolcompany.com"
}

resource "citrixadc_dnsnameserver" "dnsnameserver_qxduquwhhh" {
  dnsvservername = citrixadc_lbvserver.dns_udp_lbvserver.name #"DNS_UDP_LB"
}
resource "citrixadc_dnsnameserver" "dnsnameserver_ufzjtabuhu" {
  dnsvservername = citrixadc_lbvserver.dns_tcp_lbvserver.name #"DNS_TCP_LB"
  type           = "TCP"
}

############################# 
# Create the AAA and Gateway vServers 
############################# 

resource "citrixadc_ntpserver" "tf_ntpserver0" {
  servername = "0.pool.ntp.org"
}
resource "citrixadc_ntpserver" "tf_ntpserver1" {
  servername = "1.pool.ntp.org"
}

resource "citrixadc_ntpsync" "tf_ntpsync" {
  state = "ENABLED"
}

resource "citrixadc_authenticationauthnprofile" "authenticationauthnprofile_ktfhymohui" {
  name        = "Gateway_Auth_vServer_Profile"
  authnvsname = citrixadc_authenticationvserver.authenticationvserver_ztlrjkpdpv.name #"Gateway_Auth_vServer"
}
resource "citrixadc_authenticationvserver" "authenticationvserver_ztlrjkpdpv" {
  name        = "Gateway_Auth_vServer"
  servicetype = "SSL"
  ipv46       = "0.0.0.0"
}
resource "citrixadc_sslvserver_sslcertkey_binding" "sslvserver__binding_rzcbmamxes" {
  vservername = citrixadc_authenticationvserver.authenticationvserver_ztlrjkpdpv.name #"Gateway_Auth_vServer"
  certkeyname = citrixadc_sslcertkey.tf_sslcertkey.certkey                            #"remote.mycoolcompany.com"
}

#Apply default AAA vServer policies 
# These are the default bindings that are already present in NetScaler,  So we can't implement it
# resource "citrixadc_authenticationvserver_cachepolicy_binding" "authenticationvserver__binding_rvxuncxaaq" {
#   name                   = citrixadc_authenticationvserver.authenticationvserver_ztlrjkpdpv.name #"Gateway_Auth_vServer"
#   policy                 = "_cacheTCVPNStaticObjects"
#   priority               = "10"
#   gotopriorityexpression = "END"
#   bindpoint              = "REQUEST"
# }
# resource "citrixadc_authenticationvserver_cachepolicy_binding" "authenticationvserver__binding_linqrlpevp" {
#   name                   = citrixadc_authenticationvserver.authenticationvserver_ztlrjkpdpv.name #"Gateway_Auth_vServer"
#   policy                 = "_cacheOCVPNStaticObjects"
#   priority               = "20"
#   gotopriorityexpression = "END"
#   bindpoint              = "REQUEST"
# }
# resource "citrixadc_authenticationvserver_cachepolicy_binding" "authenticationvserver__binding_wgwcqnznie" {
#   name                   = citrixadc_authenticationvserver.authenticationvserver_ztlrjkpdpv.name #"Gateway_Auth_vServer"
#   policy                 = "_cacheVPNStaticObjects"
#   priority               = "30"
#   gotopriorityexpression = "END"
#   bindpoint              = "REQUEST"
# }
# resource "citrixadc_authenticationvserver_cachepolicy_binding" "authenticationvserver__binding_pakbnjojvp" {
#   name                   = citrixadc_authenticationvserver.authenticationvserver_ztlrjkpdpv.name #"Gateway_Auth_vServer"
#   policy                 = "_cacheWFStaticObjects"
#   priority               = "10"
#   gotopriorityexpression = "END"
#   bindpoint              = "RESPONSE"
# }
# resource "citrixadc_authenticationvserver_cachepolicy_binding" "authenticationvserver__binding_asdfghjwer" {
#   name                   = citrixadc_authenticationvserver.authenticationvserver_ztlrjkpdpv.name #"Gateway_Auth_vServer"
#   policy                 = "_mayNoCacheReq"
#   priority               = "40"
#   gotopriorityexpression = "END"
#   bindpoint              = "RESPONSE"
# }
# resource "citrixadc_authenticationvserver_cachepolicy_binding" "authenticationvserver__binding_rtyughjvbn" {
#   name                   = citrixadc_authenticationvserver.authenticationvserver_ztlrjkpdpv.name #"Gateway_Auth_vServer"
#   policy                 = "_noCacheRest"
#   priority               = "20"
#   gotopriorityexpression = "END"
#   bindpoint              = "RESPONSE"
# }

resource "citrixadc_authenticationldapaction" "authenticationldapaction_opktkswlqh" {
  name               = var.gateway_authenticationldapaction_name
  servername         = var.gateway_authenticationldapaction_servername
  serverport         = 637
  ldapbase           = var.gateway_authenticationldapaction_ldapbase
  ldapbinddn         = var.gateway_authenticationldapaction_ldapbinddn
  ldapbinddnpassword = var.gateway_authenticationldapaction_ldapbinddnpassword
  ldaploginname      = "sAMAccountName"
  groupattrname      = "memberOf"
  subattributename   = "cn"
  ssonameattribute   = "UserPrincipalName"
  passwdchange       = "ENABLED"
}
resource "citrixadc_authenticationradiusaction" "authenticationradiusaction_cqxcxdvlum" {
  name       = var.authenticationradiusaction_name
  serverip   = var.authenticationradiusaction_serverip
  serverport = 1812
  radkey     = var.authenticationradiusaction_radkey
}
resource "citrixadc_authenticationpolicy" "authenticationpolicy_bttsismyrq" {
  name   = "Gateway_RADIUS_Policy"
  rule   = "true"
  action = citrixadc_authenticationradiusaction.authenticationradiusaction_cqxcxdvlum.name #"Gateway_RADIUS_Server"
}
resource "citrixadc_authenticationpolicy" "authenticationpolicy_kzfdrxegbm" {
  name   = "Gateway_LDAP_Policy"
  rule   = "true"
  action = citrixadc_authenticationldapaction.authenticationldapaction_opktkswlqh.name #"Gateway_LDAP_Server"
}
resource "citrixadc_authenticationpolicylabel" "authenticationpolicylabel_dedfgsgtiy" {
  labelname   = "Gateway_LDAP_Policy_Label"
  loginschema = citrixadc_authenticationloginschema.authenticationloginschema_uzyekmkszy.name #LSCHEMA_INT_new # created new login schema
}

resource "citrixadc_authenticationvserver_authenticationloginschemapolicy_binding" "authenticationvserver__binding_czgdiinyld" {
  name                   = citrixadc_authenticationvserver.authenticationvserver_ztlrjkpdpv.name                 #"Gateway_Auth_vServer"
  policy                 = citrixadc_authenticationloginschemapolicy.lschema_dual_factor_builtin_new_policy.name #"lschema_dual_factor_builtin_new" # created new login schema
  priority               = "100"
  gotopriorityexpression = "END"
}
resource "citrixadc_authenticationpolicylabel_authenticationpolicy_binding" "authenticationpolicylabel__binding_ikyceqhvtp" {
  labelname              = citrixadc_authenticationpolicylabel.authenticationpolicylabel_dedfgsgtiy.labelname #"Gateway_LDAP_Policy_Label"
  policyname             = citrixadc_authenticationpolicy.authenticationpolicy_kzfdrxegbm.name                #"Gateway_LDAP_Policy"
  priority               = "100"
  gotopriorityexpression = "NEXT"
}
resource "citrixadc_authenticationvserver_authenticationpolicy_binding" "citrixadc_authenticationvserver__binding_fgghhnbvcc" {
  name                   = citrixadc_authenticationvserver.authenticationvserver_ztlrjkpdpv.name #"Gateway_Auth_vServer"
  policy                 = citrixadc_authenticationpolicy.authenticationpolicy_bttsismyrq.name   #"Gateway_RADIUS_Policy"
  priority               = "100"
  gotopriorityexpression = "NEXT"
  nextfactor             = citrixadc_authenticationpolicylabel.authenticationpolicylabel_dedfgsgtiy.labelname #Gateway_LDAP_Policy_Label
}
# set authentication loginSchema LSCHEMA_INT -authenticationSchema noschema -passwdExpression AAA.LOGIN.PASSWORD
resource "citrixadc_authenticationloginschema" "authenticationloginschema_uzyekmkszy" {
  name                 = "LSCHEMA_INT_new"
  authenticationschema = "noschema"
  passwdexpression     = "AAA.LOGIN.PASSWORD"
}

# set authentication loginSchema lschema_dual_factor_builtin -authenticationSchema "LoginSchema/DualAuth.xml" -passwdExpression AAA.LOGIN.PASSWORD2 
resource "citrixadc_authenticationloginschema" "authenticationloginschema_pdnomdjvyw" {
  name                 = "lschema_dual_factor_builtin_new"
  authenticationschema = "LoginSchema/DualAuth.xml"
  passwdexpression     = "AAA.LOGIN.PASSWORD2"
}
resource "citrixadc_authenticationloginschemapolicy" "lschema_dual_factor_builtin_new_policy" {
  name   = "lschema_dual_factor_builtin_new"
  rule   = "true"
  action = citrixadc_authenticationloginschema.authenticationloginschema_pdnomdjvyw.name
}

resource "citrixadc_vpnvserver" "vpnvserver_rwcwpgwfgn" {
  name           = var.gateway_vpnvserver_name
  servicetype    = var.gateway_vpnvserver_servicetype
  ipv46          = var.gateway_vpnvserver_ipv46
  port           = 443
  dtls           = "OFF"
  downstateflush = "DISABLED"
  listenpolicy   = "NONE"
  authnprofile   = citrixadc_authenticationauthnprofile.authenticationauthnprofile_ktfhymohui.name #"Gateway_Auth_vServer_Profile"
  depends_on = [
    citrixadc_sslprofile.sslprofile_nezvhaeqqj
  ]
}

# set ssl vserver Gateway_vServer -sslProfile SSL_Labs_Profile_Q4_2021 
resource "citrixadc_sslvserver" "sslvserver_izprawjoeg" {
  vservername = citrixadc_vpnvserver.vpnvserver_rwcwpgwfgn.name #"Gateway_vServer"
  sslprofile  = citrixadc_sslprofile.sslprofile_nezvhaeqqj.name #"SSL_Labs_Profile_Q4_2021"
}
resource "citrixadc_sslvserver_sslcertkey_binding" "sslvserver__binding_gyfoaqaitg" {
  vservername = citrixadc_sslvserver.sslvserver_izprawjoeg.vservername #"Gateway_vServer"
  certkeyname = citrixadc_sslcertkey.tf_sslcertkey.certkey             #"remote.mycoolcompany.com"
}

resource "citrixadc_vpnsessionaction" "vpnsessionaction_vjgcvhrdrz" {
  name                       = var.vpnsessionaction1_name
  defaultauthorizationaction = "ALLOW"
  sso                        = "ON"
  ssocredential              = "PRIMARY"
  icaproxy                   = "ON"
  wihome                     = var.vpnsessionaction1_wihome
  storefronturl              = var.vpnsessionaction1_storefronturl
}
resource "citrixadc_vpnsessionaction" "vpnsessionaction_pwvhwkswnw" {
  name                       = var.vpnsessionaction2_name
  defaultauthorizationaction = "ALLOW"
  sso                        = "ON"
  ssocredential              = "PRIMARY"
  icaproxy                   = "ON"
  wihome                     = var.vpnsessionaction2_wihome
}
resource "citrixadc_vpnsessionpolicy" "vpnsessionpolicy_scsugkktws" {
  name   = var.vpnsessionpolicy1_name
  rule   = var.vpnsessionpolicy1_rule
  action = citrixadc_vpnsessionaction.vpnsessionaction_vjgcvhrdrz.name #"Native_Profile"
}
resource "citrixadc_vpnsessionpolicy" "vpnsessionpolicy_kssyvgzcxs" {
  name   = var.vpnsessionpolicy2_name
  rule   = var.vpnsessionpolicy2_rule
  action = citrixadc_vpnsessionaction.vpnsessionaction_pwvhwkswnw.name #"Web_Profile"
}
resource "citrixadc_vpnvserver_staserver_binding" "vpnvserver__binding_uxhsocvixy" {
  count     = length(var.vpnvserver_staserver)
  name      = citrixadc_sslvserver.sslvserver_izprawjoeg.vservername #"Gateway_vServer"
  staserver = var.vpnvserver_staserver[count.index]
}

# These are the default bindings that are already present in NetScaler, So we can't implement it
# resource "citrixadc_vpnvserver_cachepolicy_binding" "vpnvserver__binding_bwkemtdbxa" {
#   name                   = citrixadc_sslvserver.sslvserver_izprawjoeg.vservername #"Gateway_vServer"
#   policy                 = "_cacheTCVPNStaticObjects"
#   priority               = "10"
#   gotopriorityexpression = "END"
#   bindpoint              = "REQUEST"
# }
# resource "citrixadc_vpnvserver_cachepolicy_binding" "vpnvserver__binding_ybeqyvijdg" {
#   name                   = citrixadc_sslvserver.sslvserver_izprawjoeg.vservername #"Gateway_vServer"
#   policy                 = "_cacheOCVPNStaticObjects"
#   priority               = "20"
#   gotopriorityexpression = "END"
#   bindpoint              = "REQUEST"
# }
# resource "citrixadc_vpnvserver_cachepolicy_binding" "vpnvserver__binding_xolgcloktl" {
#   name                   = citrixadc_sslvserver.sslvserver_izprawjoeg.vservername #"Gateway_vServer"
#   policy                 = "_cacheVPNStaticObjects"
#   priority               = "30"
#   gotopriorityexpression = "END"
#   bindpoint              = "REQUEST"
# }
# resource "citrixadc_vpnvserver_cachepolicy_binding" "vpnvserver__binding_qvwjfdjbsf" {
#   name                   = citrixadc_sslvserver.sslvserver_izprawjoeg.vservername #"Gateway_vServer"
#   policy                 = "_mayNoCacheReq"
#   priority               = "40"
#   gotopriorityexpression = "END"
#   bindpoint              = "REQUEST"
# }
# resource "citrixadc_vpnvserver_cachepolicy_binding" "vpnvserver__binding_cudcutilur" {
#   name                   = citrixadc_sslvserver.sslvserver_izprawjoeg.vservername #"Gateway_vServer"
#   policy                 = "_cacheWFStaticObjects"
#   priority               = "10"
#   gotopriorityexpression = "END"
#   bindpoint              = "RESPONSE"
# }
# resource "citrixadc_vpnvserver_cachepolicy_binding" "vpnvserver__binding_uanbnbckso" {
#   name                   = citrixadc_sslvserver.sslvserver_izprawjoeg.vservername #"Gateway_vServer"
#   policy                 = "_noCacheRest"
#   priority               = "20"
#   gotopriorityexpression = "END"
#   bindpoint              = "RESPONSE"
# }

resource "citrixadc_vpnvserver_vpnsessionpolicy_binding" "vpnvserver__binding_koafozsiot" {
  name                   = citrixadc_sslvserver.sslvserver_izprawjoeg.vservername      #"Gateway_vServer"
  policy                 = citrixadc_vpnsessionpolicy.vpnsessionpolicy_scsugkktws.name #"Native_Policy"
  priority               = "10"
  gotopriorityexpression = "NEXT"
  bindpoint              = "REQUEST"
}
resource "citrixadc_vpnvserver_vpnsessionpolicy_binding" "vpnvserver__binding_pqoxtwuojy" {
  name                   = citrixadc_sslvserver.sslvserver_izprawjoeg.vservername      #"Gateway_vServer"
  policy                 = citrixadc_vpnsessionpolicy.vpnsessionpolicy_kssyvgzcxs.name #"Web_Policy"
  priority               = "20"
  gotopriorityexpression = "NEXT"
  bindpoint              = "REQUEST"
}
resource "citrixadc_vpnvserver" "vpnvserver_toqdaseefd" {
  name           = var.gateway_dtls_vpnvserver_name
  servicetype    = var.gateway_dtls_vpnvserver_servicetype
  ipv46          = var.gateway_dtls_vpnvserver_ipv46
  port           = 443
  downstateflush = "DISABLED"
}
resource "citrixadc_sslvserver_sslcertkey_binding" "sslvserver__binding_ekimchexvk" {
  vservername = citrixadc_vpnvserver.vpnvserver_toqdaseefd.name #"Gateway_DTLS_vServer"
  certkeyname = citrixadc_sslcertkey.tf_sslcertkey.certkey      #"remote.mycoolcompany.com"
}
 Citrix ADC &amp; Citrix ADM Ansible ModulesThe collection provides Ansible modules for configuring and managing NetScaler ADC appliances. The modules are written using the NITRO API. The modules are idempotent and can be used to declaratively configure NetScaler ADC appliances. All Citrix ADC form factors are supported. Note You can find lots of examples of using Ansible for configuring basic ADC operations or ADC use cases in the GitHub repository https://github.com/netscaler/ansible-collection-netscaleradc/tree/citrix.adc.  Important The Ansible modules are intended for configuring existing ADC instances, not for deploying them. The ADC deployment can be achieved using Terraform. List of all the resources and whether they are supported or not: Total number of resources: 947 Total number of supported resources: 947 Total number of unsupported resources: 0 Percentage of supported resources: 100.0% Playbook DetailsThis is how a typical playbook in the netscaler.adc collection looks like: - name: Name of the playbook
  hosts: demo_netscalers

  gather_facts: false # Since the module runs on localhost, we don't need to gather facts

  module_defaults: # If we are using `module_defaults`, then these common arguments (`nsip`, `nitro_user`, etc) can be skipped from the tasks below.
    group/netscaler.adc.default_args:
      nsip: 10.10.10.10
      nitro_user: nsroot
      nitro_pass: verysecretpassword
      nitro_protocol: http
      validate_certs: false
      save_config: false
      netscaler_console_as_proxy_server: true
      managed_netscaler_instance_name: my_instance
      managed_netscaler_instance_id: 1234
      managed_netscaler_instance_ip: 1.1.1.1
      managed_netscaler_instance_username: my_user
      managed_netscaler_instance_password: my_password

  tasks:
    - name: Name of the task
      delegate_to: localhost # Since the module runs on localhost, we need to delegate the task to localhost
      netscaler.adc.service: # Name of the module. We recommend to use the fully qualified name of the module
        # The following are the NITRO authentication parameters. Refer to the module documentation for the list of supported parameters.
        nsip: 10.0.0.1 # This can also be given via NETSCALER_NSIP environment variable
        nitro_user: nitrouser # This can also be given via NETSCALER_NITRO_USER environment variable
        nitro_pass: verysecretpassword # This can also be given via NETSCALER_NITRO_PASS environment variable
        nitro_protocol: https # This can also be given via NETSCALER_NITRO_PROTOCOL environment variable
        validate_certs: false # This can also be given via NETSCALER_VALIDATE_CERTS environment variable

        # NetScaler Console as an API Proxy Server. This is optional. Default is false.
        # Refer: https://docs.netscaler.com/en-us/netscaler-application-delivery-management-software/current-release/adm-as-api-proxy-server.html
        netscaler_console_as_proxy_server: true # This can also be given via NETSCALER_CONSOLE_AS_PROXY_SERVER environment variable
        # When you want NetScaler Console to forward a request to a managed netscaler instance, add ANY ONE of the below parameters.
        managed_netscaler_instance_name: my_instance # This can also be given via NETSCALER_MANAGED_NETSCALER_INSTANCE_NAME environment variable
        managed_netscaler_instance_id: 1234 # This can also be given via NETSCALER_MANAGED_NETSCALER_INSTANCE_ID environment variable
        managed_netscaler_instance_ip: 1.1.1.1 # This can also be given via NETSCALER_MANAGED_NETSCALER_INSTANCE_IP environment variable
        # In Settings &gt; Administration &gt; System Configurations &gt; Basic Settings, if you select Prompt Credentials for Instance Login, ensure to configure user name and password of a managed instance. Alternatively, you can also specify the instance session ID.
        managed_netscaler_instance_username: my_user # This can also be given via NETSCALER_MANAGED_NETSCALER_INSTANCE_USERNAME environment variable
        managed_netscaler_instance_password: my_password # This can also be given via NETSCALER_MANAGED_NETSCALER_INSTANCE_PASSWORD environment variable

        # Should the module save the config after making the changes. This is optional. Default is false.
        save_config: false # This can also be given via NETSCALER_SAVE_CONFIG environment variable

        state: present # This is the desired state of the resource. The module will make sure that the resource is in this state. Valid values are `present`, `absent`, `enabled`, `disabled`, `imported`, `unset`, `created`, `flushed`, `switched`. However, not all modules support all the states. Refer to the module documentation for the supported states.

        # The following are the module parameters. Refer to the module documentation for the list of supported parameters.
        name: s1
        ipaddress: 10.10.10.10
        servicetype: HTTP
        port: 80All NetScaler resources can be managed by the Netscaler Ansible collection.  Let´s have a look at some day-to-day-used resources: Example: Configuring an LB vserverThis example shows all the needed Ansible modules for configuring a new LB vserver. It can serve as the baseline for all subsequent configurations and use cases. You can find the whole script and variables in the GitHub repository.  lbserver.yaml ---
- name: Sample lbvserver playbook
  hosts: demo_netscalers
  gather_facts: false
  tasks:
    - name: Configure lbvserver
      delegate_to: localhost
      netscaler.adc.lbvserver:
        state: present
        name: lb_dns_01
        servicetype: DNS
        ipv46: 169.254.100.2
        port: 53
        persistencetype: NONE
        clttimeout: 120lbserverservicebinding.yaml ---
- name: Sample lbvserver_service_binding playbook
  hosts: demo_netscalers
  gather_facts: false
  tasks:
    - name: Configure lbvserver_service_binding
      delegate_to: localhost
      netscaler.adc.lbvserver_service_binding:
        state: present
        name: lb_dns_01
        servicename: dns_svc_03lbserverservicegroupbinding.yaml ---
- name: Sample lbvserver_servicegroup_binding playbook
  hosts: demo_netscalers
  gather_facts: false
  tasks:
    - name: Configure lbvserver_servicegroup_binding
      delegate_to: localhost
      netscaler.adc.lbvserver_servicegroup_binding:
        state: present
        name: CR_cachev21
        servicename: CR_SVGlbserver_responderpolicy_binding.yaml ---
- name: Sample lbvserver_responderpolicy_binding playbook
  hosts: demo_netscalers
  gather_facts: false
  tasks:
    - name: Configure lbvserver_responderpolicy_binding
      delegate_to: localhost
      netscaler.adc.lbvserver_responderpolicy_binding:
        state: present
        name: v666
        policyname: P1
        priority: '5'
        bindpoint: REQUESTlbserverrewritepolicybinding.yaml ---
- name: Sample lbvserver_rewritepolicy_binding playbook
  hosts: demo_netscalers
  gather_facts: false
  tasks:
    - name: Configure lbvserver_rewritepolicy_binding
      delegate_to: localhost
      netscaler.adc.lbvserver_rewritepolicy_binding:
        state: present
        name: LB_DIA_vs2
        policyname: rw_diam_pol
        priority: '10'
        gotopriorityexpression: END
        bindpoint: REQUESTlbservertmtrafficpolicybinding.yaml ---
- name: Sample lbvserver_tmtrafficpolicy_binding playbook
  hosts: demo_netscalers
  gather_facts: false
  tasks:
    - name: Configure lbvserver_tmtrafficpolicy_binding
      delegate_to: localhost
      netscaler.adc.lbvserver_tmtrafficpolicy_binding:
        state: present
        name: lbvs
        policyname: ia_tmtrafpol1
        priority: '1' Example: Configuring an Authentication vserver with LDAP and RADIUS policiesThis example shows all the needed Ansible modules for configuring a new AUTH vserver. You can find the whole script and variables in the GitHub repository. authenticationvserver.yml ---
- name: Sample authenticationvserver playbook
  hosts: demo_netscalers
  gather_facts: false
  tasks:
    - name: Configure authenticationvserver
      delegate_to: localhost
      netscaler.adc.authenticationvserver:
        state: present
        name: Citrix_AAA_vServer
        servicetype: SSL
        ipv46: 0.0.0.0authenticationpolicy.yaml ---
- name: Sample authenticationpolicy playbook
  hosts: demo_netscalers
  gather_facts: false
  tasks:
    - name: Configure authenticationpolicy
      delegate_to: localhost
      netscaler.adc.authenticationpolicy:
        state: present
        name: OKta_SAML_Internal_ZS_SubnetsMisc
        rule: Sub_58220950_24 || Sub_941881310_25 || Sub_1242481410_24 || Sub_1281771250_24

          || Sub_137831540_24 || Sub_154113230_24 || Sub_197982010_24 || Sub_211144190_24
          || Sub_213521020_24
        action: prod_oktaauthenticationldappolicy.yaml ---
- name: Sample authenticationldappolicy playbook
  hosts: demo_netscalers
  gather_facts: false
  tasks:
    - name: Configure authenticationldappolicy
      delegate_to: localhost
      netscaler.adc.authenticationldappolicy:
        state: present
        name: LDAP_Basic_Policy
        rule: ns_true
        reqaction: ldap_mgmt_actauthenticationldappolicy.yaml ---
- name: Sample authenticationldappolicy playbook
  hosts: demo_netscalers
  gather_facts: false
  tasks:
    - name: Configure authenticationldappolicy
      delegate_to: localhost
      netscaler.adc.authenticationldappolicy:
        state: present
        name: LDAP_Basic_Policy
        rule: ns_true
        reqaction: ldap_mgmt_actauthenticationldapaction.yaml ---
- name: Sample authenticationldapaction playbook
  hosts: demo_netscalers
  gather_facts: false
  tasks:
    - name: Configure authenticationldapaction
      delegate_to: localhost
      netscaler.adc.authenticationldapaction:
        state: present
        name: ldap_userextraction_External_noauth
        servername: awsdcs-amer.blackstone.com
        serverport: 636
        ldapbase: OU=New AD Structure,DC=Blackstone,DC=com
        ldapbinddn: CN=GD-CITRIXADC-P-APP,OU=Service Accounts,OU=Elevated,OU=New AD
          Structure,DC=Blackstone,DC=com
        ldapbinddnpassword: REQ_PASSWORD
        ldaploginname: sAMAccountName
        groupattrname: memberOf
        subattributename: cn
        sectype: SSL
        authentication: DISABLED
        passwdchange: ENABLED
        nestedgroupextraction: 'ON'
        groupnameidentifier: sAMAccountName
        groupsearchattribute: sAMAccountName
        defaultauthenticationgroup: Externalauthenticationradiuspolicy.yaml ---
- name: Sample authenticationradiuspolicy playbook
  hosts: demo_netscalers
  gather_facts: false
  tasks:
    - name: Configure authenticationradiuspolicy
      delegate_to: localhost
      netscaler.adc.authenticationradiuspolicy:
        state: present
        name: test_radius
        rule: ns_true
        reqaction: test_radius_actauthenticationradiusaction.yaml ---
- name: Sample authenticationradiusaction playbook
  hosts: demo_netscalers
  gather_facts: false
  tasks:
    - name: Configure authenticationradiusaction
      delegate_to: localhost
      netscaler.adc.authenticationradiusaction:
        state: present
        name: RADIUS_10.102.222.187
        serverip: 10.102.222.187
        serverport: 1812
        authtimeout: '3'
        radkey: freebsd
        radnasip: DISABLED
        passencoding: pap
        ipvendorid: '0'
        accounting: 'ON'
        callingstationid: DISABLED
 Example: Configuring a REWRITE policy and actionThis example shows all the needed Ansible modules for configuring a new rewrite policy and action. You can find the whole script and variables in the GitHub repository. rewritepolicy.yaml ---
- name: Sample rewritepolicy playbook
  hosts: demo_netscalers
  gather_facts: false
  tasks:
    - name: Configure rewritepolicy
      delegate_to: localhost
      netscaler.adc.rewritepolicy:
        state: present
        name: rw_diam_pol
        rule: diameter.req.avp(264).value.eq("host.xyz.net")
        action: rw_act_insert_after_diameter_avprewriteaction.yaml ---
- name: Sample rewriteaction playbook
  hosts: demo_netscalers
  gather_facts: false
  tasks:
    - name: Configure rewriteaction
      delegate_to: localhost
      netscaler.adc.rewriteaction:
        state: present
        name: rw_act_insert_after_diameter_avp
        type: insert_after
        target: diameter.req.avp(345678)
        stringbuilderexpr: diameter.new_avp(3110, "This is now inserted…") Example: Configuring an NS Gateway server (vpnvserver)This example shows Ansible modules for configuring a new NetScaler Gateway server. You can find the whole script and variables in the GitHub repository. vpnvserver.yaml ---
- name: Sample vpnvserver playbook
  hosts: demo_netscalers
  gather_facts: false
  tasks:
    - name: Configure vpnvserver
      delegate_to: localhost
      netscaler.adc.vpnvserver:
        state: present
        name: CitrixAccessCallback
        servicetype: SSL
        ipv46: 10.189.130.19
        port: 443
        downstateflush: DISABLED
        listenpolicy: NONEvpnsessionpolicy.yaml ---
- name: Sample vpnsessionpolicy playbook
  hosts: demo_netscalers
  gather_facts: false
  tasks:
    - name: Configure vpnsessionpolicy
      delegate_to: localhost
      netscaler.adc.vpnsessionpolicy:
        state: present
        name: External_receiver_session_pol
        rule: HTTP.REQ.HEADER("User-Agent").CONTAINS("CitrixReceiver")
        action: External_receiver_session_profvpnsessionaction.yaml ---
- name: Sample vpnsessionaction playbook
  hosts: demo_netscalers
  gather_facts: false
  tasks:
    - name: Configure vpnsessionaction
      delegate_to: localhost
      netscaler.adc.vpnsessionaction:
        state: present
        name: External_receiver_session_prof
        sesstimeout: 20
        transparentinterception: 'OFF'
        defaultauthorizationaction: ALLOW
        clientidletimeout: 20
        sso: 'ON'
        ssocredential: SECONDARY
        icaproxy: 'ON'
        wihome: https://ac.the-austrian-citrix-guy.at/Citrix/StoreExternal
        clientlessvpnmode: 'ON'vpnstaserver.yaml ---
- name: Sample vpnglobal_staserver_binding playbook
  hosts: demo_netscalers
  gather_facts: false
  tasks:
    - name: Configure vpnglobal_staserver_binding
      delegate_to: localhost
      netscaler.adc.vpnglobal_staserver_binding:
        state: present
        staserver: https://ddc1.the-austrian-citrix-guy.atvpndomain.yaml ---
- name: Sample vpnglobal_domain_binding playbook
  hosts: demo_netscalers
  gather_facts: false
  tasks:
    - name: Configure vpnglobal_domain_binding
      delegate_to: localhost
      netscaler.adc.vpnglobal_domain_binding:
        state: present
        intranetdomain: '*.the-austrian-citrix-guy.at'vpnclientlessaccesspolicy.yaml ---
- name: Sample vpnclientlessaccesspolicy playbook
  hosts: demo_netscalers
  gather_facts: false
  tasks:
    - name: Configure vpnclientlessaccesspolicy
      delegate_to: localhost
      netscaler.adc.vpnclientlessaccesspolicy:
        state: present
        name: ia_cltlsacspol1
        rule: 'true'
        profilename: ia_cltlsacsprof1vpnclientlessaccessprofile.yaml ---
- name: Sample vpnclientlessaccessprofile playbook
  hosts: demo_netscalers
  gather_facts: false
  tasks:
    - name: Configure vpnclientlessaccessprofile
      delegate_to: localhost
      netscaler.adc.vpnclientlessaccessprofile:
        state: present
        profilename: ia_cltlsacsprof1vpnldappolicy.yaml ---
- name: Sample vpnvserver_authenticationldappolicy_binding playbook
  hosts: demo_netscalers
  gather_facts: false
  tasks:
    - name: Configure vpnvserver_authenticationldappolicy_binding
      delegate_to: localhost
      netscaler.adc.vpnvserver_authenticationldappolicy_binding:
        state: present
        name: ns.pcoip.net
        policy: ldap24
        priority: '100'vpnradiuspolicy.yaml ---
- name: Sample vpnvserver_authenticationradiuspolicy_binding playbook
  hosts: demo_netscalers
  gather_facts: false
  tasks:
    - name: Configure vpnvserver_authenticationradiuspolicy_binding
      delegate_to: localhost
      netscaler.adc.vpnvserver_authenticationradiuspolicy_binding:
        state: present
        name: nsgw_voila_direct2dll_com
        policy: auth_pol_radius_azure_mfa2_nonmobile
        priority: '120'
        secondary: truevpnicapolicy.yaml ---
- name: Sample vpnvserver_icapolicy_binding playbook
  hosts: demo_netscalers
  gather_facts: false
  tasks:
    - name: Configure vpnvserver_icapolicy_binding
      delegate_to: localhost
      netscaler.adc.vpnvserver_icapolicy_binding:
        state: present
        name: wionnsvs
        policy: icapol1
        priority: '3'… and many more…  Designing an Environment for Automation Architectural OverviewThe architecture of an IaC deployment is straightforward – you need a machine with all the IaC frameworks installed.  This machine or machine cluster then contacts all your targets using remoting protocols like WinRM or SSH. You can find a thorough installation description for an Ubuntu-based machine earlier in this guide.  Core PrinciplesThe core principles of a best-practise-based architecture for using Infrastructure-as-code are essential to remember: Idempotency:  Applying the same code multiple times should not change the system state  Declarative Approach:  Define what the infrastructure should look like, not how to create it  Modularity:  Break down resources into reusable modules  Version Control:  All IaC code is stored in Git or a similar VCS  Security &amp; Compliance:  Integrate secrets management, policy enforcement, and scanning  High-Level ArchitectureIt is best to think about the architecture in different stages:  Source Control Layer: Git repositories for:    Modules (reducible building blocks) Environment Configurations (dev, staging, prod) Pipeline Definitions Branching strategy: main for production, dev, test, feature branches for changes  CI/CD Pipeline Layer: Continuous Integration: Linting (e.g., terraform fmt ) Static analysis (e.g., tfsec) Unit tests for modules Continuous Deployment: Plan → Approve → Apply Automated rollback on failure Tools: GitHub Actions, Azure Pipelines, GitLab CI, or Jenkins  State Management Layer: Remote State Backend Terraform: S3, DynamoDB, Azure Storage, GCS, … Locking is enabled to prevent race conditions State Encryption: Enable encryption at rest and in transit  Secrets Management Layer: Use Vault, AWS Secrets Manager, or Azure Key Vault Never hardcode secrets in IaC  Policy Enforcement Layer: OPA (Open Policy Agent) or Terraform Sentinel for compliance Enforce tagging, resource limits, and security baselines        Execution Layer: IaC tools: Terraform, Ansible, Packer  Immutable Infrastructure principle: Replace rather than mutate resources where possible  Security Best PracticesEnable least privilege for IaC execution roles Scan IaC code for vulnerabilities before deployment Implement drift detection (e.g., terraform plan in scheduled tasks or cron jobs)  Using scheduled tasks or cron jobs, along with an advanced notification mechanism, lets you detect infrastructure changes and respond promptly. Example for combining the check for changes and the CVAD/DaaS feature Configuration Logging. It enables you to get an overview of who did what when, making it easier to track. We created a simple script to get a notification out via email and on a specific Teams channel:   Observability &amp; AuditingLog all IaC actions (plan, apply) in a centralized system Integrate with monitoring tools (Prometheus, Grafana, Splunk, …) Audit trails for compliance You can also use Terraform to keep a record of your infrastructure for audits, as IaC creates every entity, and the Terraform state file represents your current infrastructure state. You will learn more about the importance of the state file later in this handbook.  Component InteractionIn a full-blown IaC architecture, many components must interact with one another to enable successful IaC and CI/CD. Here are some examples for distinct roles: Packer Purpose: Build immutable machine images (VM templates, AMIs, managed images) with base OS and common dependencies Outputs: Versioned images referenced later by Terraform Auth/Secrets: Pulls credentials (cloud API keys, provisioner passwords) from Vault at build time  Terraform Purpose: Provision infrastructure resources (compute, network, storage, IAM) and publish state to Azure Storage (remote backend) Outputs: Resource attributes and connection info for Ansible (e.g., IPs, hostnames)  Ansible Purpose: Post‑provision configuration management on the provisioned hosts (packages, services, app deploys) Connectivity: Uses SSH (22) for Unix/Linux; WinRM HTTP (5985) or WinRM HTTPS (5986) for Windows Auth/Secrets: Retrieves credentials (SSH keys, WinRM user/pass, certificates) from Vault just‑in‑time  Vault (Secrets Management) Stores: Cloud provider creds, SSH private keys, WinRM users/passwords, TLS certificates Access: IaC machine and CI agents authenticate (OIDC/JWT, AppRole, or Azure auth methods) and request short‑lived tokens  Azure Storage (Terraform State) Backend: Terraform remote state with state locking (via storage leases) and encryption at rest Usage: The IaC machine (or CI agents) reads/writes state to avoid local state conflicts  CI Triggers (GitHub Actions, Jenkins, Azure Pipelines) Function: Kick off Packer → Terraform → Ansible workflows on code changes or scheduled runs. Security: Pipelines fetch ephemeral secrets from Vault; never store secrets in repo.  Typical End-to-End FlowLet´s look at a typical flow – starting from scratch until the desired end state of our infrastructure. CI Trigger &amp; Preparation: Designer commits changes (Packer templates, Terraform modules, Ansible playbooks) to the repository GitHub Actions / Jenkins / Azure Pipelines start the workflow on push/PR or schedule Pipeline agent (or the dedicated IaC machine) authenticates to Vault, obtains short‑lived tokens, and required secrets  Image Build (Packer) Packer runs: Authenticates to cloud (e.g., Azure, AWS) using creds from Vault Optionally runs provisioning steps (shell/Ansible) inside the image Produces versioned image artifacts (e.g., Azure Managed Image ID) The Image IDs are exported as pipeline artifacts or Terraform variables for further usage  Infrastructure Provision (Terraform) terraform init uses Azure Storage as the backend: Saves remote state; acquires lock terraform plan computes changes; policy checks run (if you use OPA/Sentinel) terraform apply provisions: Compute: VM/Scale Sets using the Packer-created Image. Network: VNet/Subnets, NSGs, firewalls (open only required ports 22, 5985/5986 from the IaC network) Outputs: Host inventories (IP/hostname), and connection details required by Ansible State updated in Azure Storage; lock released  Configuration &amp; App Deploy (Ansible) Ansible inventory is assembled from Terraform outputs (dynamic inventory or generated files) Ansible connects to targets: Linux: SSH over TCP port 22 Windows: WinRM over: HTTP (TCP port 5985) for initial setup prior to domain-join and GPO download HTTPS (TCP port 5986) for production/hardened environments High-level overview of the flow:   Design Decisions Terraform State File LocationTerraform stores information about your infrastructure and configuration in its State. It primarily maps deployed resources to the infrastructure and maintains the required metadata. The State is stored by default in a local file named"terraform.tfstate". Terraform uses the State to determine which changes must be made to your infrastructure. Terraform automatically refreshes the state with the existing infrastructure before any alterations are initiated. Important Be very careful with a locally saved .tfstate file—it is recommended that you store it in a safe, encrypted manner with limited access and use a versioning control system.  Caution Terraform´s .tfstate file can contain sensitive data such as database passwords, user passwords, or private keys. If you use locally stored State files (=Local State), they are stored on your machine as plain-text JSON files. If you use remote state files (= Remote State), the State is transferred from the remote backend to memory only when Terraform uses it. Be aware that the transport can be unencrypted. The .tfstate file can grow quite large—for example, in our demo environment, it exceeds 5MB. Example of a representation of a Machine Catalog in a locally stored .tfstate file: ...,
 {
      "mode": "managed",
      "type": "citrix_machine_catalog",
      "name": "machine_catalog_0",
      "provider": "provider[\"registry.terraform.io/citrix/citrix\"]",
      "instances": [
        {
          "schema_version": 0,
          "attributes": {
            "allocation_type": "Random",
            "built_in_scopes": [
              "00000000-0000-0000-0000-000000000000"
            ],
            "description": "Machine Catalog on Azure based on Windows 11 Single-Session  for TMM-Gerhard",
            "id": "95d6xxxx-xxxx-xxxx-xxxx-xxxxxxxxdc35",
            "inherited_scopes": [],
            "is_power_managed": null,
            "is_remote_pc": null,
            "machine_accounts": null,
            "machine_catalog_folder_path": null,
            "metadata": null,
            "minimum_functional_level": "L7_34",
            "name": "MC-TMM-GK-AZURE-W11-SS",
            "provisioning_scheme": {
              "availability_zones": [
                "1"
              ],
              "aws_machine_config": null,
              "azure_machine_config": {
                "azure_master_image": {
                  "container": null,
                  "gallery_image": null,
                  "master_image": "TMM-GK-W11-SS-M_OsDisk_1_7d8xxxxxxxx",
                  "resource_group": "tmm_gerhard_westeurope",
                  "shared_subscription": null,
                  "storage_account": null
                },
                "azure_pvs_config": null,
                "disk_encryption_set": null,
                "enroll_in_intune": null,
                "image_update_reboot_options": null,
                "license_type": "Windows_Client",
                "machine_profile": {
                  "machine_profile_resource_group": "tmm_gerhard_westeurope",
                  "machine_profile_template_spec_name": null,
                  "machine_profile_template_spec_version": null,
                  "machine_profile_vm_name": "TMM-GK-W11-SS-M"
                },
                "master_image_note": "",
                "service_offering": "Standard_D2s_v5",
                "storage_type": "Premium_LRS",
                "use_azure_compute_gallery": null,
                "use_managed_disks": true,
                "vda_resource_group": "tmm_gerhard_westeurope",
                "writeback_cache": null
              },
              "custom_properties": null,
              "gcp_machine_config": null,
              "hypervisor": "bec2xxxx-xxxx-xxxx-xxxx-xxxxxxxxf8f0",
              "hypervisor_resource_pool": "2c70xxxx-xxxx-xxxx-xxxx-xxxxxxxx87cb",
              "identity_type": "ActiveDirectory",
              "machine_account_creation_rules": {
                "naming_scheme": "TMM-GK-W11-SS-#",
                "naming_scheme_type": "Numeric"
              },
              "machine_domain_identity": {
                "domain": "democloud.corp",
                "domain_ou": "OU=_AZURE,OU=_WORKER,OU=_CITRIX,OU=TACG,OU=EBC-TMM,DC=xxxxxxxxxxxxxxxxxxxxxxxx,DC=xxxxxxxxxxxxxxxxxxxxxxxx",
                "service_account": "xxxxxxxxxxxxxxxxxxxxxxxx",
                "service_account_password": "xxxxxxxxxxxxxxxxxxxxxxxx"
              },
              "metadata": null,
              "network_mapping": null,
              "number_of_total_machines": 1,
              "nutanix_machine_config": null,
              "scvmm_machine_config": null,
              "vsphere_machine_config": null,
              "xenserver_machine_config": null
            },
            "provisioning_type": "MCS",
            "remote_pc_ous": null,
            "scopes": [],
            "session_support": "SingleSession",
            "tags": null,
            "tenants": null,
            "vda_upgrade_type": null,
            "zone": "4050xxxx-xxxx-xxxx-xxxx-xxxxxxxxc507"
          },
          "sensitive_attributes": [
            [
              {
                "type": "get_attr",
                "value": "provisioning_scheme"
              },
              {
                "type": "get_attr",
                "value": "machine_domain_identity"
              },
              {
                "type": "get_attr",
                "value": "service_account_password"
              }
            ]
          ]
        }
      ]
    },Keeping the State safeThe recommended way to keep the State secure is not to use Local State but Remote State. The State is written to a remote data store. Terraform supports HCP Terraform, HashiCorp Consul, Amazon S3, Azure Blob Storage, Google Cloud Storage, and more. As mentioned, Terraform will not persist the State anywhere on the local disk. It will write the State locally only in the case of a non-recoverable error, when writing to the backend fails. This is to prevent data loss. We use an Azure Storage Account to store the State in this example. You can use Terraform to create the needed Storage Account: data "azurerm_resource_group" "GetAzureRG" {
  name = "TMM-Gerhard-WestEurope"
}
 
resource "azurerm_storage_account" "AzureStorageAccountForTerraform" {
  name                              = "${var.TF-StorageAccount-Name}"
  resource_group_name               = data.azurerm_resource_group.GetAzureRG.name
  location                          = data.azurerm_resource_group.GetAzureRG.location
  account_tier                      = "Standard"
  account_replication_type          = "LRS"
  allow_nested_items_to_be_public   = false
 
  tags                              = {
                                            environment  = "TMM-Gerhard"
  }
}
 
resource "azurerm_storage_container" "AzureStorageAccountContainerForTerraform" {
    depends_on = [ azurerm_storage_account.AzureStorageAccountForTerraform ]
  name                              = "${var.TF-StorageAccountContainer-Name}"
  storage_account_name              = azurerm_storage_account.AzureStorageAccountForTerraform.name
  container_access_type             = "private"
}Or you can use PowerShell to create the needed Storage Account: # Get Azure Resource Group
$ResourceGroup = Get-AzResourceGroup -Name $RESOURCE_GROUP_NAME

# Create Azure Storage Account
$StorageAccount = New-AzStorageAccount -ResourceGroupName $ResourceGroup.Name -Name $TF_StorageAccount_Name -SkuName Standard_LRS -Location $ResourceGroup.Location -AllowBlobPublicAccess $false
# Create Azure Blob Container
New-AzStorageContainer -Name $TF_StorageAccountContainer_Name -Context $StorageAccount.context
# Retrieve Storage Container Access Key
$SC_Access_Key=(Get-AzStorageAccountKey -ResourceGroupName $ResourceGroup.Name -Name $StorageAccount.Name)[0].valueNow you can configure Terraform to use Remote State using a backend configuration: terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "=&gt;4.9"
    }
  }
 
  backend "azurerm" {
    resource_group_name  = "XxXxXxXxXxX"
    storage_account_name = "XxXxXxXxXxX"
    container_name       = "terraform"
    key                  = "terraform.tfstate"
    use_oidc             = true
    client_id            = "a99fxXxX-xXxX-xXxX-xXxX-xXxXxXxXxd2de"
    client_secret        = "xXxX-xXxX-xXxX-xXxX-xXxXxXxXx"
    subscription_id      = "893axXxX-xXxX-xXxX-xXxX-xXxXxXxXx2de793"
    tenant_id            = "db32xXxX-xXxX-xXxX-xXxX-xXxXxXxXx2de49f"
  }
}
 
provider "azurerm" {
  features {}
} Caution You cannot use variables in the backend configuration part. You need to define the settings in clear text, including all sensitive settings.  Important Azure Storage blobs are automatically locked before any operation writes to the state. This prevents concurrent state operations.  All Data stored in an Azure blob is encrypted. Terraform retrieves the State from the backend and stores it in local memory. Transport is usually encrypted, as accessing the Azure Storage Container requires a TLS-capable connection. Terraform initializes successfully using Remote State: PS C:\_TERRAFORM\_BACKEND&gt; terraform init
Initializing the backend...

Successfully configured the backend "azurerm"! Terraform will automatically
use this backend unless the backend configuration changes.
Initializing provider plugins...
- Finding hashicorp/azurerm versions matching "&gt;= 4.6.0"...
- Finding citrix/citrix versions matching "&gt;= 1.0.5"...
- Installing hashicorp/azurerm v4.10.0...
- Installed hashicorp/azurerm v4.10.0 (signed by HashiCorp)
- Installing citrix/citrix v1.0.7...
- Installed citrix/citrix v1.0.7 (self-signed, key ID BD4BD0E690CB7D88)
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.You can see the .tfstate file in the Azure Storage Container:   Storing SecretsAs we have seen, secrets should not be stored in a ready-to-use format or even hardcoded into the code. Use Vault or Azure Key Vault for secrets Fetch secrets dynamically during runtime (CI/CD pipeline or IaC execution) Use short-lived tokens and role-based access control Enable audit logging for secret access Never store secrets in terraform .tfvars or Ansible inventory files in plain text Examples of data that are considered sensible: Cloud Provider Credentials API keys, access tokens, client secrets for AWS, Azure, GCP Example: ARM_CLIENT_SECRET for Azure or AWS_SECRET_ACCESS_KEY  SSH Keys Private keys used for connecting to Linux servers Public keys can be stored in code, but private keys must be secured  WinRM Credentials Username and password for WinRM Certificates for WinRM over HTTPS (port 5986)  Database Credentials Connection strings, usernames, and passwords for SQL/NoSQL databases  TLS/SSL Certificates Private keys and certificate files for HTTPS endpoints  API Tokens Tokens for third-party services (monitoring, logging, CI/CD integrations)  Sensitive Configuration Values Encryption keys, JWT signing secrets, OAuth client secrets  Terraform Backend Credentials Storage account keys or SAS tokens for remote state (e.g., Azure Storage)  Ansible Vault Password If using Ansible Vault for encrypted variables  Packer Builder Credentials Cloud Image builder credentials (Azure Managed Image, AWS AMI) For example, you can define variables as sensitive in Terraform, so that Terraform does not show the content during its run: variable "vsphere_admin_username" {
  type        = string
  sensitive   = true
  description = "REQUIRED: vSphere-Admin-Username"
}

variable "vsphere_admin_pw" {
  type        = string
  sensitive   = true
  description = "REQUIRED: vSphere-Admin-Password"
}If you run the code, Terraform does not list the content: # citrix_vsphere_hypervisor.TACG-TMM-CVAD-V-HypConn will be created
  + resource "citrix_vsphere_hypervisor" "TACG-TMM-CVAD-V-HypConn" {
      + addresses                                = [
          + (sensitive value),
        ]
      + id                                       = (known after apply)
      + max_absolute_active_actions              = 20
      + max_absolute_new_actions_per_minute      = 10
      + max_power_actions_percentage_of_machines = 20
      + name                                     = "TACG-TMM-CVAD-VSP-Cluster"
      + password                                 = (sensitive value)
      + password_format                          = "PlainText"
      + scopes                                   = []
      + tenants                                  = (known after apply)
      + username                                 = (sensitive value)Terraform helps to keep the sensitive values “secret”. Another way to keep sensitive values secret is to use HashiCorp Vault, Azure Key Vault, or AWS Secrets Manager. Example Terraform code that retrieves secrets dynamically without hardcoding them and avoids storing them in the .tfstate file: # Configure Vault provider
provider "vault" {
  address = "https://vault.example.com"
  token   = var.vault_token  # Use short-lived token from CI/CD pipeline
}

# Retrieve secret from Vault
data "vault_generic_secret" "db" {
  path = "secret/data/db"
}

# Use the secret in a resource without storing it in state
resource "null_resource" "configure" {
  provisioner "local-exec" {
    command = &lt;&lt;EOT
      ansible-playbook -i inventory site.yml \
      --extra-vars "db_password=${data.vault_generic_secret.db.data["password"]}"
    EOT
  }

  # Prevent storing secret in state
  lifecycle {
    ignore_changes = [provisioner]
  }
}

# Sensitive output (hidden in CLI)
output "db_password" {
  value     = data.vault_generic_secret.db.data["password"]
  sensitive = true
}Another example of securing sensitive data is storing it in GitHub´s vault:  HIGHEST CAUTION Remember to make sure that forking your public GitHub repository does not reveal sensitive data.   Storing and Reusing Components/Scripts on Central RepositoriesIn every organization, every team might build infrastructures its own way. One team keeps Terraform scripts on a local laptop, another uses Ansible playbooks stored in a shared drive, and someone else has Packer templates buried in an email attachment. A developer updates a security group in staging, but production doesn’t match. Someone accidentally deletes a resource because they didn’t know about a recent change. Compliance audits become nightmares because there’s no clear history of who changed what.  Secrets leak because they were hardcoded in scripts. The result is drift, downtime, and risk. Adopting centralized Infrastructure as Code repositories solves these problems.  All Terraform modules, Ansible roles, and Packer templates live in a single source of truth—version-controlled, secure, and accessible. Every change is tracked, reviewed, and approved through pull requests. CI/CD pipelines automatically test and deploy infrastructure changes. Secrets are injected securely from Vault or Azure Key Vault, never hardcoded. Teams reuse modules instead of reinventing the wheel, ensuring consistency across environments. When an auditor asks, “Who changed the firewall rules last month?”—you can answer in seconds. When a new team joins, they clone the repo and follow documented workflows. Disaster recovery? Just redeploy from the repo.  Security? Git hooks and scanning tools catch exposed secrets before they hit production. Possible Repository OptionsGitHub Widely used, integrates with GitHub Actions for CI/CD  GitLab Built-in CI/CD and strong DevSecOps features  Azure Repos (Azure DevOps) Ideal for organizations using Azure services  Bitbucket Integrates with Atlassian tools like Jira  AWS CodeCommit Native AWS solution for Git repositories  Self-hosted Git (e.g., Gitea, Gerrit) For organizations requiring on-premises control Example: Using an Azure Storage VaultAzure Storage provides Blob Containers that can act as a secure, scalable repository for storing: Terraform modules and IaC snippets (e.g., reusable VPC, VM, or security group templates). Ansible playbooks and roles for configuration management. Packer templates for image building. These files are uploaded to a central container, organized by environment and purpose. Access is controlled via Azure RBAC and Managed Identities, ensuring only authorized users and CI/CD pipelines can read or write. We recommend enabling soft-delete, blob-versioning, and change feed as further necessary settings. Example structure of our in-use Azure Storage Vault: Directories in container 'iac-repo':

ansible
dev	
	playbooks
	tasks
	roles
	prod
	playbooks
	tasks
	roles
	test
	playbooks
	tasks
	roles
terraform
dev	
	modules
		compute
			hyperscaler
				aws
				azure
					gcp
				hypervisor
					nutanix
					vsphere
					xenserver
			other
prod	
	modules
		compute
			hyperscaler
				aws
				azure
					gcp
				hypervisor
					nutanix
					vsphere
					xenserver
			other
test	
	modules
		compute
			hyperscaler
				aws
				azure
					gcp
				hypervisor
					nutanix
					vsphere
					xenserver
			other
packer
	dev
	hyperscaler
			aws
			azure
				gcp
			hypervisor
				nutanix
				vsphere
				xenserver
prod
	hyperscaler
			aws
			azure
				gcp
			hypervisor
				nutanix
				vsphere
				xenserver

	test
	hyperscaler
			aws
			azure
				gcp
			hypervisor
				nutanix
				vsphere
				xenserverThis file structure enables structured, shared access to all relevant snippets, organized by target entities. The division into dev, test, and prod also makes it easy to implement CI/CD standards. Example: Using GitHubWe also use GitHub Actions as our CI/CD framework, along with webhooks and REST API calls to trigger deployments. We created a similar structure on our private GitHub repository to allow collaboration. All Terraform modules, Ansible playbooks, and Packer templates are stored in a secure, version-controlled GitHub repository. Every change is tracked, reviewed, and approved through pull requests. Security GitHub integrates with enterprise authentication (SSO, MFA) and secret scanning tools to prevent leaks  Scalability Repositories can host hundreds of modules and playbooks, organized by environment and purpose  Collaboration  Designers, operations managers, and security teams work together through branches and pull requests As always, it is up to you which central repository you want to use – but it is recommended to use one, for sure.  Deployment TipsStart Small, Grow SlowlyImplementing Infrastructure as Code (IaC) is a transformative step for IT organizations, enabling automation, consistency, and scalability. However, for teams new to IaC, starting small and growing gradually is not just a best practice—it’s a strategic imperative. Reduce Risk and Complexity IaC introduces new paradigms in infrastructure management, including version control, declarative configurations, and automated deployments. Jumping into full-scale implementation without experience can lead to misconfigurations, outages, and security gaps. Starting with a limited scope—such as automating a single environment or a non-critical workload—allows teams to learn without jeopardizing business continuity  Build Skills and Confidence IaC requires proficiency in tools (Terraform, Ansible, Packer, Jenkins, GitHub, Azure Pipelines, etc.), coding practices, and DevOps principles. A phased approach gives teams time to: Understand syntax and workflows Develop troubleshooting skills Establish coding standards and governance This incremental learning curve fosters confidence and competence before scaling to mission-critical systems  Establish Governance Early Starting small enables organizations to define policies, compliance checks, and approval workflows before complexity grows. This prevents “IaC sprawl” and ensures security and operational standards are baked in from the beginning. Deployment of ComponentsThe following proposal outlines a phased approach for implementing Infrastructure as Code (IaC) using Terraform and Ansible to deploy a Citrix Virtual Apps and Desktops workspace environment. A crawl -&gt; walk -&gt; run -&gt; evolve model reduces risk, improves predictability, and creates reusable automation assets. By starting small and scaling systematically, the organization ensures repeatable, secure, and resilient Citrix deployments. Phase 1 — Foundations (“Crawl”) Duration: 4–8 weeks Goal: Establish a stable IaC foundation before automating Citrix-specific components 1. Governance &amp; Architecture Setup Define IaC standards: naming, tagging, directory structure Establish Git workflows and PR-based change control Design CI/CD pipeline architecture for Terraform and Ansible  Deliverables IaC governance playbook Repository structure for modules &amp; roles Pipeline architecture  Success Metric A simple Terraform deployment runs through the pipeline successfully and adheres to standards  2. Terraform Baseline Infrastructure Build, for example, compute and network foundations based on the used Hypervisor/Hyperscaler Create foundational modules for reuse across Citrix tiers  Deliverables Production-ready infrastructure modules Automated creation of network and compute components  Success Metric Foundation-level components deploy 100% automatically  3.    Ansible Baseline Configuration Automate simple OS configurations Automate OS hardening and security baselines Automate domain-join and install common agents (monitoring, logging) Prepare configuration roles for future Citrix nodes  Deliverables Configured and hardened VM via Ansible Configured domain-joined servers  Success Metric VMs deployed via Terraform are fully configured by Ansible with no manual steps  4.    Pipeline &amp; Secrets Integration and Configuration Implement secure pipelines for plan/apply actions Integrate with Vault, Key Vault, or KMS  Success Metric All credentials managed via secrets vault; no manual deployment needed  Phase 2 — Core Services Automation (“Walk”) Duration: 8–12 weeks Goal: Automate core services required for Citrix, but not yet the Citrix entities 1. Platform Infrastructure for Citrix Deploy SQL/RDS, license servers, file servers, and AD components Standardize these components as modules  Deliverables Highly available platform services via Terraform and Ansible Infrastructure is ready for Citrix installation  Success Metric Core dependencies deploy in less than one hour  2. Citrix - Network &amp; ADC foundations (optional) Automate NetScaler/ADC provisioning (VPX, HA pairs) Configure SNIPs, VIPs, VLANs, and basic load-balancing  Deliverables Terraform and Ansible modules for ADC Automated network constructs required for Citrix Gateway and StoreFront  Success Metric ADC HA pair deploys cleanly through the pipeline  3. Image &amp; VDA Preparation Build golden images via Packer Deploy Ansible roles for OS tuning, configuration of prerequisites, installation of mainstream software packages, and Citrix VDA installation  Deliverables End-to-end automated base image Reusable Software package installation and configuration role Reusable VDA configuration role  Success Metric New VDA templates are generated automatically and consistently  4. Testing &amp; Quality Add testing and quality assurance validation actions Integrate security scans and linting  Success Metric IaC quality and security checks run automatically on every PR  Phase 3 — Full Citrix Automation (“Run”) Duration: 10–16 weeks Goal:  Fully automate Citrix control plane, access layer, and worker node deployment 1. Delivery Controller Automation Deploy Delivery Controllers via Terraform Configure all needed features with Ansible   Success Metric Controllers deploy production-ready without human intervention  2. StoreFront &amp; Gateway Automation Automate StoreFront installation and store creation Integrate with Citrix Gateway Automate Gateway authentication, ICA proxy, and session policies  Success Metric Functional Citrix access layer validated via test connections  3. Machine Catalogs, Delivery Groups &amp; Policies Automate MCS/PVS catalog creation Deploy Delivery Groups and publish apps/desktops Apply centralized GPOs or Studio-based Citrix Policies automatically  Success Metric Full Citrix site deploys in less than four hours  4. Enterprise Scaling &amp; Governance Implement auto-remediation and drift detection Build multi-region or multi-environment topologies Create runbooks and handoff documentation  Success Metric IaC-managed Citrix platform moves into steady-state operation  Phase 4 — Optimization &amp; Self-Service (“Evolve”) Duration: Ongoing Goal: Increase automation maturity and operational efficiency 1. Self-Service Provisioning Integrate with ServiceNow, Azure DevOps, or portals Enable RBAC-driven automated provisioning of new workloads  2. Automated Scaling &amp; Cost Optimization Deploy auto-scale logic for VDA worker nodes Implement shutdown/start schedules and cost dashboards  3. Continuous Improvements Update Terraform/Ansible modules as Citrix releases evolve Enhance observability, logging, and failure reporting Maintain automated testing and security posture Timeline OverviewPhase Duration Core Deliverables 1 – Foundations 4–8 weeks IaC standards, Production-ready Terraform and Ansible modules, VM deployment without manual interaction 2 – Core Services 8–12 weeks Core services like SQL, licensing, and basic ADC functions deploy automatically, and worker templates create and deploy automatically 3 – Full Citrix Stack 10–16 weeks All Citrix entities deploy automatically, and a fully functional environment is deployed using IaC 4 – Optimization Ongoing Continuous improvement and optimization of IaC-based deployment This phased approach ensures that the organization adopts Terraform and Ansible in a structured, low-risk manner while creating a fully automated, scalable Citrix environment. By starting with foundational IaC components, your team builds reusable modules that accelerate later phases and reduce long-term operational overhead.  Using CI/CD MethodologiesThe following proposal outlines a phased, low-risk approach to implementing CI/CD automation using GitHub Actions, Azure Pipelines, or Jenkins. The strategy follows a crawl -&gt; walk -&gt; run -&gt; evolve maturity model designed to minimize disruption, build confidence, and establish a scalable automation foundation. Implementation begins with version control governance and simple pipelines, then gradually expands into reusable templates, environment automation, and enterprise-level continuous delivery. Phase 1 — Foundations (“Crawl”) Duration: 4–8 weeks Goal: Establish core CI/CD governance, version control structure, and basic automation workflows. This phase intentionally avoids production-impacting automation so teams can learn tooling safely. 1. Repository &amp; Branching Standards Objectives Standardize Git repository layout and naming conventions Define branching model (e.g., Feature Branch) Enable mandatory PR reviews and protections  Deliverables Repository templates Branching and tagging standards PR and approval policies  Success Metric Teams consistently follow the repository standard; PR workflows run smoothly  2.  Initial CI Pipeline Objectives Build simple, non-invasive pipelines to validate tooling and workflows Provide predictable feedback loops without touching deployment systems  Deliverables A basic pipeline for: Linting code Running basic tests Artifact packaging (optional)  Platform examples GitHub Actions: Basic workflow YAML (lint → test → build)  Azure Pipelines: YAML pipeline or classic build pipeline  Jenkins: Freestyle job or scripted Jenkinsfile pipeline  Success Metric Receive automated feedback on commits  3. Secrets &amp; Credential Management Objectives Ensure pipelines never expose credentials Set up secure integration with identity systems  Deliverables Secure secrets stored in: GitHub Secrets Azure Key Vault + Library variables in Azure Pipelines Jenkins Credentials Store Documented policies for secret rotation  Success Metric All pipelines consume secrets securely without hardcoded credentials  4. Build Agent Strategy Objectives Decide between hosted agents or self-hosted runners Establish patching and security requirements for agents Integrate with Vault, Key Vault, or KMS  Deliverables Documented agent strategy Configured runners or agents with access controls  Success Metric CI jobs run reliably and safely on approved agents  Phase 2 — Expanded CI/CD Capabilities (“Walk”) Duration: 8–12 weeks Goal: Mature CI/CD by introducing reusable templates, test automation, artifact management, and controlled deployments 1. Reusable Pipeline Templates Objectives Reduce duplication and increase consistency across teams  Deliverables Shared workflow templates for GitHub Reusable YAML templates for Azure Pipelines Shared Jenkins pipeline libraries  Success Metric ≥50% of repositories adopt centralized templates  2. Environment Deployment and Promotion Objectives Introduce a deployment automation in a controlled, low-risk manner  Deliverables Dev → Test → Prod pipelines Manual approvals, checks Structured release process  Success Metric Deployments become predictable, repeatable, and logged automatically  3. Pipeline Observability &amp; Reporting Objectives Add transparency, logging, and quality metrics  Deliverables Notifications to Teams/Slack/e-mail  Success Metric Teams can monitor pipeline performance and spot failures quickly  Phase 3 — Full CI/CD (“Run”) Duration: 10–16 weeks Goal: Automate complete delivery cycles, from commit to production, with governance and compliance controls 1. Automated Deployments to Production Objectives Expand CI/CD to orchestrate full deployments  Deliverables Deployment pipelines for cloud, on-prem, or hybrid  Success Metric Production deployments executed reliably through pipelines  2. Finalization of Infrastructure-Oriented Pipelines Objectives Finalize the integration of CI/CD with IaC workflows  Deliverables Pipelines that orchestrate terraform plan/apply in production Ansible execution jobs for configuration changes Change approvals integrated into platform workflows  Success Metric Infrastructure deployments and changes in production become stable and repeatable  3. Enterprise-Wide Template Adoption Objectives Standardize pipeline patterns across teams for efficiency and operational consistency  Deliverables Notifications to Teams/Slack/eEnterprise pipeline library Shared GitHub/DevOps/Jenkins templates Security-validated build blocks  Success Metric 80%+ of pipelines adopt standardized building blocks  Phase 4 — Optimization &amp; Self-Service (“Evolve”) Duration: Ongoing Goal: Move CI/CD from an engineering tool to a scalable platform service used across the organization 1. Self-Service Pipelines Portals or templates to use ready-made pipelines Automated onboarding for new workloads  2. Advanced Deployment Strategies Canary releases Progressive delivery frameworks  3. Continuous Improvements Update pipelines as Citrix releases and infrastructure environments evolve Automated cleanup of old artifacts  Timeline OverviewPhase Duration Core Deliverables 1 – Foundations 4–8 weeks Repos, branching standards, simple CI, secrets management 2 – Expanded CI/CD Capabilities 8–12 weeks Increased template adoption, Deployments become predictable and repeatable 3 – Full CI/CD Capabilities 10–16 weeks Full automated deployments, governance &amp; IaC integration 4 – Optimization Ongoing Self-service CI/CD, canary releases, continuous improvement This phased roadmap ensures safe, predictable, and scalable adoption of CI/CD platforms—whether GitHub Actions, Azure Pipelines, or Jenkins. By starting with governance and simple pipelines, the organization builds the knowledge and infrastructure needed to support enterprise-grade automation, reduce deployment risk, and accelerate delivery cycles.  Reproducibility in the case of ErrorsReproducible errors are the fastest path to root cause.  For IaC tools, this hinges on: Deterministic inputs (version pinning, immutable artifacts) Controlled environments (containers, isolated accounts/workspaces) Captured context (state, vars, logs, tool/provider versions) Consistent execution (CI pipelines, seed data, parallelism) Disciplined debugging (isolate, instrument, iterate) Let's look deeper at how to set up reproducibility, configure high-value logging, and use systematic debugging strategies for Terraform, Ansible, and Packer. Deterministic inputsPin tool versions: Terraform: Use only a strictly defined version by setting a required_version Terraform providers/modules: use required_providers terraform {
    required_version = "= 1.8.4"

  required_providers {
    vsphere = {
      source  = "hashicorp/vsphere"
      version = "= 2.7.0"
    }

    citrix = {
      source  = "citrix/citrix"
      version = "=1.0.30"
    }
  }
} Ansible: pin role/collection versions in requirements.yml ---
roles:
  # Install a role from Ansible Galaxy.
  - name: rolenametobeused
    version: "1.9.6" 

collections:
  # Install a collection from Ansible Galaxy.
  - name: community.general
    version: "=7.0.0"
    source: https://galaxy.ansible.com Packer: pin specific version in required_plugins   packer {
  required_version = "= 1.9.0"
  required_plugins {
    vsphere = {
      version = "= v1.4.2"
      source  = "github.com/hashicorp/vsphere"
    }
  }
  required_plugins {
    windows-update = {
      version = "= 0.16.8"
      source  = "github.com/rgl/windows-update"
    }
  }
}Immutable artifacts: Reference specific AMI IDs/Azure Image version; avoid “latest” Snapshot base images and tag them Controlled environmentsThe recommended way is to use a controlled, reproducible environment. Only then can you be sure that your debugging environment is always built on the same foundation. Run in containers with pinned OS + toolchain Isolate accounts/projects for non-prod testing (e.g., separate cloud account, Terraform workspaces) Use remote state with Terraform with locking (e.g., Consul), one state per environment Use fixed regions + network (avoid region drift, ensure reproducible network routes) Capture Full Run ContextFor reproducibility, it is essential to capture all settings, versions, variables, etc. Try to save: Tool versions (terraform/packer/ansible and provider/plugin versions) Input variables (*.tfvars, group/host vars for Ansible), inventory snapshot State snapshot (Terraform state, Packer builder outputs, Ansible task results) Environment variables and flags used for the run Commit SHA / build number linking code to artifacts Consistent Execution PathsTry always to use consistent execution paths: CI pipelines to run on a standard machine image Parallelism controls: lower parallelism to expose ordering issues Seeding / test data: fixed seeds for randomized elements, mock inputs where possible Let´s look at some tool-specific best-practice settings for debugging: Terraform — Logging &amp; DebuggingSet the log level accordingly (from most to least level): # Deep logging
export TF_LOG=TRACE        # TRACE|DEBUG|INFO|WARN|ERROR
export TF_LOG_PATH=./terraform.log

# Mark that Terraform is running under automation (less interactive)
export TF_IN_AUTOMATION=1Use validation &amp; plan capture: terraform fmt -check
terraform validate
terraform init -upgrade=false             # Avoid unpredictable upgrades
terraform plan -out=tfplan
terraform show -json tfplan &gt; tfplan.json # Machine-readable diff for reviewUse workspace isolation and targeted runs: terraform workspace new repro-test        # Use dedicated workspace
terraform plan -target=module.name        # Isolate a suspect module (use sparingly)
terraform apply -parallelism=1            # Reduce concurrency to expose ordering issues
terraform plan -refresh-only              # Focus on drift without applying changesUse deeper, interactive inspection possibilities: terraform state list
terraform console                         # Inspect variables/expressions interactively Ansible — Logging &amp; DebuggingSet the verbosity level accordingly: ansible-playbook site.yml -v                 # Least level of verbosity
ansible-playbook site.yml -vv
ansible-playbook site.yml -vvv
ansible-playbook site.yml -vvvv              # Highest level of verbosity
ansible-playbook site.yml -v --check         # Running in Check mode: execute a playbook without applying any alterations to your systems. Use check mode to test playbooks before running them in a production environment.
ansible-playbook site.yml -v --diff --check  # Running in Diff mode, the changes made are reported or if used with --check, the changes that would have been made. Structured logging via callback plugins by configuring ansible.cfg: [defaults]
inventory = inventories/dev
stdout_callback = yaml
# Enable useful callbacks for profiling &amp; logging
callbacks_enabled = profile_tasks, timer, log_plays
host_key_checking = True
forks = 10     # Consider lowering to reproduce ordering issuesIn-playbook instrumentation: - name: Debug variables and task context
  ansible.builtin.debug:
    msg:
      - "host={{ inventory_hostname }}"
      - "var1={{ var1 }}"
      - "ansible_facts.os={{ ansible_facts.os_family }}Isolation &amp; Determinism: Inventory snapshots: Export the inventory used (ansible-inventory --list &gt; inventory.json) ansible-galaxy install -r requirements.yml --force
Serialize execution to reproduce ordering-sensitive issues - hosts: windows
  serial: 1
  strategy: linear
  tasks:
    ...Artifacts &amp; Re-runs: Use ansible-runner to generate structured event logs (if available) Capture stdout/stderr to files per run  Packer — Logging &amp; DebuggingDeep logging + Debug Mode: export PACKER_LOG=1
export PACKER_LOG_PATH=./packer.log

packer validate template.pkr.hcl
packer inspect template.pkr.hcl
packer build -debug template.pkr.hcl      # Interactively step through builders/provisioners
Deep logging + Debug Mode: packer {
  required_plugins {
    amazon = {
      source  = "github.com/hashicorp/amazon"
      version = "&gt;= 1.2.5, &lt; 1.3.0"
    }
  }
}

build {
  name     = "app-image"
  on_error = "abort"     # or "cleanup" / "continue"
  sources  = ["source.amazon-ebs.app"]
}Determinism: Use fixed base AMI IDs, Image IDs, and fixed regions Tag images with e.g., git_sha, build_number, and environment Archive the provisioner scripts used in the image build Other proven strategiesInstrument &amp; Observe: Add debug outputs near suspected failure points Turn on TRACE/DEBUG logs for the specific tool Capture timestamps and host/resource identifiers to correlate events across tools Separate “drift” from “bug”: Terraform: -refresh-only to see drift without applying changes If drift exists, reconcile and re-test. If not, focus on config logic Create Minimal Repro Cases: Smaller templates/playbooks that reproduce the issue quickly are gold for root cause analysis Create a per-run folder, e.g., artifacts/2025-12-05T09-00-00-&lt;commit_SHA&gt;/ containing: versions.txt — terraform/ansible/packer versions, provider/plugin versions env.txt — all relevant environment variables and flags inputs/ — .tfvars, Ansible var files, inventories, Packer HCL state/ — Terraform state snapshot (read-only copy), Packer outputs, Ansible run events logs/ — terraform.log, ansible.log (callback output), packer.log plan/ — terraform plan + plan.json notes.md — brief description of failure, steps taken, following hypotheses Use CI to standardize environments and capture artifacts – GitHub example workflow: name: IaC Repro Run

on:
  workflow_dispatch:
    inputs:
      env:
        description: "Environment to test (dev/stage)"
        required: true
        default: "dev"

jobs:
  repro:
    runs-on: ubuntu-latest
    env:
      TF_IN_AUTOMATION: "1"
      TF_LOG: "TRACE"
      PACKER_LOG: "1"
    steps:
      - uses: actions/checkout@v4

      - name: Set up Terraform (pinned)
        uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: 1.6.6

      - name: Show tool versions
        run: |
          terraform -version | tee versions.txt
          ansible --version | tee -a versions.txt
          packer -version | tee -a versions.txt

      - name: Terraform init + plan
        run: |
          terraform init -upgrade=false
          terraform plan -out=tfplan -var-file="vars/${{ github.event.inputs.env }}.tfvars"
          terraform show -json tfplan &gt; plan/tfplan.json

      - name: Ansible run (structured output)
        run: |
          ansible-playbook site.yml -vvv --inventory inventories/${{ github.event.inputs.env }} --check --diff | tee logs/ansible.log

      - name: Packer validate + build (debug)
        run: |
          packer validate template.pkr.hcl
          PACKER_LOG_PATH=logs/packer.log packer build -debug template.pkr.hcl

      - name: Upload artifacts
        uses: actions/upload-artifact@v4
        with:
          name: iac-repro-${{ github.sha }}
          path: |
            versions.txt
            plan/tfplan.json
            logs/
            vars/
            inventories/Reference configurations – Terraform: capture plans for reproducibility: terraform init -upgrade=false
terraform plan -out=tfplan -var-file="vars/dev.tfvars"
terraform show -json tfplan &gt; plan/tfplan.json
Reference configurations – Ansible: ansible.cfg: [defaults]
inventory = inventories/dev
stdout_callback = yaml
callbacks_enabled = profile_tasks, timer, log_plays
host_key_checking = True
forks = 10
retry_files_enabled = False Reference configurations – Use proven Packer template snippets: variable "Azure_ClientID" {
  type        = string
  description = "Azure Service Principal App ID"
  sensitive   = true
}

variable "Azure_ClientSecret" {
  type        = string
  description = "Azure Service Principal Secret"
  sensitive   = true
}

variable "Azure_SubscriptionID" {
  type        = string
  description = "Azure Subscription ID"
  sensitive   = true
}

variable "Azure_TenantID" {
  type        = string
  description = "Azure Tenant ID"
  sensitive   = true
}

variable "Azure_RG" {
  type        = string
  description = "Packer Artifacts Resource Group"
}

variable "Azure_TempRG" {
  type        = string
  description = "Packer Build Resource Group"
}

variable "Azure_ImgPublisher" {
  type        = string
  description = "Windows Image Publisher"
}

variable "Azure_ImgOffer" {
  type        = string
  description = "Windows Image Offer"
}

variable "Azure_ImgSKU" {
  type        = string
  description = "Windows Image SKU"
}

variable "Azure_ImgVersion" {
  type        = string
  description = "Windows Image Version"
}

variable "Azure_ManagedImgName" {
  type        = string
  description = "FInalized Windows Image Name"
}

variable "Azure_SIG-ImgVersion" {
  type        = string
  description = "FInalized Windows Image Name"
}

variable "Azure_SIG-Name" {
  type        = string
  description = "FInalized Windows Image Name"
}

variable "Azure_SIG-ImgName" {
  type        = string
  description = "FInalized Windows Image Name"
}

variable "Azure_VMSize" {
  type        = string
  description = "FInalized Windows Image Name"
}

packer {
  required_plugins {
    azure = {
      source  = "github.com/hashicorp/azure"
      version = "~&gt; 2"
    }
  }
}

source "azure-arm" "W11MIWitSWPackagesWithoutVDA" {
  # Tagging
  azure_tags = {
    environment = "TMM",
    environment-entity ="GK-TF",
    usage ="Prod"
  }
  # WinRM Communicator
  communicator   = "winrm"
  winrm_use_ssl  = true
  winrm_insecure = true
  winrm_timeout  = "5m"
  winrm_username = "packer"

  # Service Principal Authentication
  client_id       = var.Azure_ClientID
  client_secret   = var.Azure_ClientSecret
  subscription_id = var.Azure_SubscriptionID
  tenant_id       = var.Azure_TenantID

  # Source Image
  os_type         = "Windows"
  image_publisher = var.Azure_ImgPublisher
  image_offer     = var.Azure_ImgOffer
  image_sku       = var.Azure_ImgSKU
  image_version   = var.Azure_ImgVersion

  # Destination Image
  managed_image_resource_group_name = var.Azure_RG
  managed_image_name                = var.Azure_ManagedImgName

  # Packer Computing Resources
  build_resource_group_name = var.Azure_TempRG
  vm_size                   = var.Azure_VMSize
}

build {
  source "azure-arm.W11MIWithSWPackagesWithoutVDA" {}

  # Install Chocolatey: https://chocolatey.org/install#individual
  provisioner "powershell" {
    inline = ["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'))"]
  }

  # Install Chocolatey packages
  provisioner "file" {
    source      = "./SWPackagesToInstall.config"
    destination = "D:/SWPackagesToInstall.config"
  }

  provisioner "powershell" {
    inline = ["choco install --confirm D:/SWPackagesToInstall.config"]
    # See https://docs.chocolatey.org/en-us/choco/commands/install#exit-codes
    valid_exit_codes = [0, 3010]
  }

  provisioner "windows-restart" {}

  # Generalize image using Sysprep
  # See https://www.packer.io/docs/builders/azure/arm#windows
  # See https://docs.microsoft.com/en-us/azure/virtual-machines/windows/build-image-with-packer#define-packer-template
  provisioner "powershell" {
    inline = [
      "while ((Get-Service RdAgent).Status -ne 'Running') { Start-Sleep -s 5 }",
      "while ((Get-Service WindowsAzureGuestAgent).Status -ne 'Running') { Start-Sleep -s 5 }",
      "&amp; $env:SystemRoot\\System32\\Sysprep\\Sysprep.exe /oobe /generalize /quiet /quit /mode:vm",
      "while ($true) { $imageState = Get-ItemProperty HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State | Select ImageState; if($imageState.ImageState -ne 'IMAGE_STATE_GENERALIZE_RESEAL_TO_OOBE') { Write-Output $imageState.ImageState; Start-Sleep -s 10  } else { break } }"
    ]
  }
} ChecklistsImportant Implement proven checklists and stick to them to minimize bugs or problems.  Reproducible Run Checklist Tool versions pinned (Terraform, Ansible, Packer, providers/plugins) Deterministic inputs captured (tfvars, inventories, HCL templates) Isolated environment/workspace/account used Remote state with locking enabled Logs and plans captured (tfplan.json, terraform.log, packer.log, Ansible callback output) Artifact folder archived under unique run ID (commit SHA / timestamp) Concurrency controlled (serial/forks/parallelism) Drift assessed (refresh-only / inventory validation) Minimal repro case available (if needed)  Debugging Strategy Checklist Reproduce in container/CI Reduce scope (module/tag/builder) Instrument with debug outputs Review logs at TRACE/DEBUG with timestamps Separate drift vs config bug Apply binary search on the configuration Capture state/image/inventory snapshots Add regression test post-fix  Triad VersioningCaution Major problems may arise if you change the versions of each triad member tool without testing. Version pinning and lock files are not optional—they are the foundation for reproducibility, security, and stability in IaC workflows. Let´s look at some caveats if the mentioned best practices are ignored: Non-Reproducible Builds Different environments resolve different versions → infrastructure drift Example: Terraform provider defaults change → resource attributes differ between runs  Breaking Changes on Upgrade Unpinned versions allow automatic major upgrades → syntax changes or deprecated arguments break pipelines  Security Risks Pulling the latest versions without review may introduce vulnerabilities or untested features Example: A provider update enabling insecure defaults  CI/CD Failures Pipeline uses an older version than local → plan/apply fails due to syntax differences Example: Terraform 1.6 vs 1.4 handling of moved blocks  Hidden Behavioral Changes Providers often change defaults silently (e.g., Azure storage replication defaults) Without version pinning, behavior changes without code changes → hard-to-debug issues  Drift Between Environments Dev uses updated Ansible collection, prod uses older → tasks behave differently Leads to inconsistent infrastructure and unpredictable deployments  Increased Debugging Complexity When errors occur, reproducing them is nearly impossible if versions differ Logs and state files may not match the code semantics of the current version  Repetitive TasksRepetitive tasks are not just a convenience—they are the gold standard because they enable automation, reliability, and governance. They transform infrastructure from a manual, error-prone process into a predictable, scalable system. Important Infrastructure as Code (IaC) is about predictability, consistency, and automation.  Repetitive tasks—those that can be executed identically across environments—are the foundation of these principles Let´s look deeper: Predictable Outcomes: Repetition ensures that the same code produces the same infrastructure every time.  This predictability reduces risk and accelerates troubleshooting because the behavior is deterministic  Consistency Across Environments: By automating repetitive tasks (e.g., provisioning VMs and configuring networks), IaC ensures that dev, test, and production environments are aligned.  This eliminates configuration drift and deployment surprises  Scalability and Efficiency: Repetitive tasks are easy to parameterize and scale. Instead of manually configuring 100 servers, you define one template and apply it repeatedly—saving time and reducing human error  Auditability and Compliance: When repetitive tasks are codified, they become traceable and version-controlled.  This makes compliance checks straightforward and ensures that every change is documented  Foundation for Continuous Delivery: IaC pipelines rely on repeatable steps to integrate seamlessly with CI/CD.  Without repeatability, automation breaks down, and deployments become unreliable.  Tip Try to follow all your familiar and standardized workflows.  Especially in complex environments, a familiar workflow ensures a fast, reliable implementation.  That completes Part 2 of the Citrix Automation Handbook. In the first part of the Citrix Automation Handbook, we discuss the Citrix Automation story and the basics of Infrastructure-as-Code, of Continuous Integration/Continuous Deployment (CI/CD) strategies, and our main IaC tools: Packer, Terraform, and Ansible. In this part of the Citrix Automation Handbook, we focused on: Applying automation and infrastructure-as-code in the Citrix universe. We discuss all relevant components and provide code examples from real environments Best practises for designing an environment for automation and IaC use, and discuss deployment tips In the third part of the Citrix Automation Handbook, we focus on common and special use cases, as well as complete deployment examples from the field. In the fourth part of the Citrix Automation Handbook, we cover creating a Citrix Virtual Apps and Desktops 2507 LTSR deployment on XenServer 8.4. In the fifth part of the Citrix Automation Handbook, we cover creating a Citrix DaaS deployment on Azure. In the sixth part of the Citrix Automation Handbook, we cover some final topics.  DisclaimerMost important All code snippets mentioned in this handbook were thoroughly tested and run in a sandbox environment. All shown Packer, Terraform, Ansible Playbooks, and PowerShell scripts were tailored exclusively for the environment used for this handbook. You need to adjust all settings, values, snippets, etc., in the most suitable way tailored to your requirements, regulations, and existing environments. All settings, scripts, and code examples listed in this document are intended only to illustrate the possibilities and serve as a basis for your own deployment. We tried to show different ways, even if they were not always in line with best-practice guidelines, such as unencrypted WinRM communication for demonstration purposes. Using all the provided code snippets is at your own risk – please read the disclaimer below for further information.   EXCEPT WHERE EXPRESSLY PROVIDED OTHERWISE BY CLOUD SOFTWARE GROUP, THE CONTENT IN THIS DOCUMENT IS PROVIDED “AS IS” AND CLOUD SOFTWARE GROUP HEREBY DISCLAIMS ALL EXPRESS OR IMPLIED REPRESENTATIONS, WARRANTIES, GUARANTIES, AND CONDITIONS, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES OR CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD TO BE LEGALLY INVALID. CLOUD SOFTWARE GROUP MAKES NO REPRESENTATIONS, WARRANTIES, GUARANTIES, OR CONDITIONS AS TO THE QUALITY, SUITABILITY, TRUTH, ACCURACY, OR COMPLETENESS OF ANY OF THE CONTENT CONTAINED ON THE WEBSITE. The above disclaimer applies to any damages, liability, or injuries caused by any failure of performance, error, omission, interruption, deletion, defect, delay in operation or transmission, computer virus, communication line failure, theft or destruction of or unauthorized access to, alteration of, or use, whether for breach of contract, tort, negligence or any other cause of action.]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2026_01/image.png.63c9eca4863bf7d87230a5b0e68b6e12.png" length="60259" type="image/png"/><pubDate>Wed, 21 Jan 2026 09:12:09 +0000</pubDate></item><item><title>The Citrix Automation Handbook 2601</title><link>https://community.stage.citrix.com/tech-zone/automation/automation-handbook-2601/</link><description><![CDATA[The Citrix Automation Handbook - The Story and StrategyCitrix platforms (e.g., Citrix Desktops as a Service, Citrix Virtual Apps and Desktops, Citrix Cloud, NetScaler span hybrid infrastructures, multiple operating systems, and a diverse set of configuration surfaces—from golden images and machine catalogs (MCS/PVS) to StoreFront, profiles, policies, and ADC traffic management. Without a shared, codified approach, teams face configuration drift, slow and inconsistent deployments, brittle change processes, and avoidable downtime. The Citrix Automation Handbook should be seen as a concise, practical technical handbook discussing the common language, patterns, and guardrails needed to scale Citrix reliably through automation and Infrastructure as Code (IaC). Goals of the handbookDefine standards for IaC across Citrix:  Naming conventions, repository structure, module boundaries, and branching strategies  Provide reference architectures (on‑prem, cloud, and hybrid) covering CVAD resource locations, image pipelines (PVS/MCS), StoreFront, WEM, and ADC/Global Server Load Balancing  Document tooling choices and integration patterns: Terraform for underlying cloud/on‑prem infrastructure (networks, load balancers, compute) PowerShell/DSC, Ansible, and Citrix SDK/APIs for Citrix components and Windows configuration CI/CD (GitHub Actions/Azure DevOps) for build, test, validate, and deploy  Establish operational workflows:  Environment promotion, change review, testing (unit/integration), drift detection, rollback, and incident playbooks  Codify security practices:  Secrets management, least‑privilege service principals, artifact signing, and policy-as-code  Enable onboarding &amp; knowledge transfer:  Checklists, runbooks, templates, and examples that accelerate new projects and new team members.   Who should use itPlatform engineers, Citrix admins, network/security teams, and operations staff who build, run, and secure Citrix services—plus application owners who depend on predictable delivery.  How it will be usedAs the single source of truth for designing, implementing, and operating automated Citrix environments—referenced in daily work, embedded in pipelines, and updated through peer review as the platform evolves.  StructureIn the first part of the Citrix Automation Handbook, we discuss the Citrix Automation story and the basics of Infrastructure-as-Code, of Continuous Integration/Continuous Deployment (CI/CD) strategies, and our main IaC tools: Packer, Terraform, and Ansible. In the second part of the Citrix Automation Handbook, we focus on: Applying automation and infrastructure-as-code in the Citrix universe. We discuss all relevant components and provide code examples from real environments Best practises for designing an environment for automation and IaC use, and discuss deployment tips In the third part of the Citrix Automation Handbook, we focus on common and special use cases, as well as complete deployment examples from the field. In the fourth part of the Citrix Automation Handbook, we cover creating a Citrix Virtual Apps and Desktops 2507 LTSR deployment on XenServer 8.4. In the fifth part of the Citrix Automation Handbook, we cover creating a Citrix DaaS deployment on Azure. In the sixth part of the Citrix Automation Handbook, we cover some final topics.  Part 1 Why is Automation essential for Citrix?Citrix’s goal is to help customers automate their environments with minimal friction.  We support multiple automation interfaces, REST APIs, PowerShell SDKs, and Terraform providers, so customers can choose the tools that fit their workflows. Internally, we use the same automation interfaces that customers do. This helps us validate our APIs, catch bugs early, and understand how customers are using our products in real-world scenarios. It also means that when a customer reports an issue, we’re often able to reproduce and fix it quickly. Automation also enhances Citrix’s ability to deliver repeatable, scalable, and secure deployments across hybrid and multi-cloud environments. It supports Citrix’s broader goals of reducing time-to-value, improving customer satisfaction, and deeply integrating into customer ecosystems.   Why is Automation essential for Customers?Automation shifts the customer experience from manual, error-prone configuration to a model that’s repeatable, testable, and centrally managed. Once infrastructure is defined as code, customers can version-control their environments, audit changes, and prevent drift. This is especially important in regulated industries or large-scale deployments where consistency and traceability are critical. There are already regulations in place that are becoming mandatory to meet compliance and audit requirements for customer verticals: Vertical Key Regulations / Standards IaC Compliance Benefits Financial Services (Banking, Insurance, FinTech) PCI DSS (Payment Card Industry Data Security Standard) SOX (Sarbanes-Oxley Act) EBA Guidelines (EU) FFIEC (US) Automated, auditable infrastructure changes Enforces encryption, access controls Reduces human error in sensitive systems Healthcare &amp; Life Sciences HIPAA (US) HITECH (US) GDPR (EU) ISO 27799 (Health informatics) Consistent HIPAA-compliant environments Version-controlled infrastructure for audits Data protection by design Government &amp; Public Sector FedRAMP (US) FISMA (US) NIST 800-53 ENS (Spain) Standardized, secure deployments Automated compliance baselines Rapid patching and updates Energy &amp; Utilities NERC CIP (North America) ISO 27019 IEC 62443 Reproducible, validated infrastructure Automated enforcement of security controls Reduced risk of misconfiguration Telecommunications ETSI Standards (EU) FCC Regulations (US) ISO 27001 Automated provisioning across regions Built-in security and monitoring Compliance baked into templates Retail &amp; eCommerce PCI DSS GDPR (EU) CCPA (California) Enforces PCI compliance for payment systems Consistent data protection policies Audit-ready infrastructure Pharmaceuticals &amp; Biotech GxP (Good Practice guidelines) 21 CFR Part 11 (US FDA) ICH Guidelines Validated, reproducible environments Automated audit trails Supports clinical trial compliance Technology &amp; SaaS Providers SOC 2 / SOC 3 ISO 27001 CSA STAR Secure multi-tenant environments Automated evidence collection for audits Faster compliance reporting   With automation in place, customers can build pipelines around their infrastructure to facilitate compliance checks, disaster recovery workflows, or image update cycles. For example, monthly patching becomes a predictable, low-effort process when Terraform, Packer, and Ansible are chained together. The most advanced customers are building custom workflows that fully automate onboarding, image updates, and policy enforcement. These pipelines reduce manual toil and improve reliability across the board. Automation also improves security posture. By storing configuration in source control and enforcing policy-as-code, customers can ensure that only approved changes are deployed. This makes it easier to pass audits and respond to incidents. Finally, automation scales. Whether a customer manages a few hundred VDAs or tens of thousands across hybrid environments, automation ensures operational overhead doesn’t grow linearly with the environment.  Which Automation possibilities does Citrix offer?Citrix offers many ways to automate your Citrix environment. You can use any Automation strategy you are familiar with, or that best suits your overall strategy. Of course, not all functionalities are achievable with all Automation toolkits. Each toolkit has its own strengths and weaknesses. You need to decide for yourself what to use for which deployment. PowerShell SDKs:  Automate granular management tasks for Citrix Virtual Apps™ and Desktops™, NetScaler®, and Cloud  Citrix DaaS™ REST API:  API-driven operations for resource management, data retrieval, and configuration changes  Citrix Hypervisor™ CLI:  Automate VM lifecycle and network management in Citrix Hypervisor environments  Ansible:  Orchestrate and manage complex Citrix environments with agentless automation  Terraform:  Deploy and manage Citrix infrastructure with infrastructure-as-code (IaC) principles  Packer:  Create, manage, and deploy Master Images for Citrix infrastructures based on infrastructure-as-code (IaC) principles  Suitable Use Cases for Automation (examples): Infrastructure Provisioning:  Automate the setup of servers, networks, and storage using tools like Terraform or Ansible  Deployment Pipelines:  CI/CD pipelines that automate the build, test, and deployment process  Monitoring and Alerts:  Automatically check your infrastructure for unwanted changes and trigger alerts when changes are detected - you can even remediate them automatically using the right toolkit  Security Compliance:  Automate security checks and compliance validations  Limitations of Automation (examples): Complex Decision-Making:  Tasks requiring human judgment, especially in ambiguous situations  Creative and Innovative Processes:  Automation cannot replace creativity, brainstorming, or strategy development  Highly Variable Processes:  Tasks with too much variability or a lack of standard procedures are challenging to automate  Which Components can be automated?Here is a matrix showcasing which components can be automated. Note This matrix will be constantly reviewed and edited as products evolve. Overview:  Green means Automation using this toolkit/framework is possible, Yellow means partially possible, Red means not possible. DaaS-specific:  Green means Automation using this toolkit/framework is possible, Yellow means partially possible, Red means not possible. Software Package-specific:   Green means Automation using this toolkit/framework is possible, Yellow means partially possible, Red means not possible.  Administration-specific:   Green means Automation using this toolkit/framework is possible, Yellow means partially possible, Red means not possible.  Why should you use Automation with the Triad over REST API, PowerShell, or GUI?In all our guides and documentation, we use 3 distinct toolkits for all our core Automation tasks. Let´s call these the “Triad”. Hashicorp Packer for Master Image Creation and Image Handling Hashicorp Terraform for the deployment of the Infrastructure components and as the backbone of the Automation task RedHat Ansible for System configurations and the deployment, installation, and configuration of Applications A detailed description of all three toolkits is available later in this document. Let´s have a look at the main differences between the toolkits for Infrastructure-as-Code: Comparison of PowerShell vs Rest API vs Terraform PowerShell REST API Terraform Use Case Automating tasks in Citrix Cloud, Citrix Virtual Apps and Desktops, and NetScaler environments via cmdlets Direct programmatic access to Citrix Cloud, Citrix Virtual Apps and Desktops, and NetScaler, enabling custom integrations Infrastructure as Code (IaC) for Citrix Cloud, Citrix Virtual Apps and Desktops, and NetScaler Automation Scope Task-level automation (e.g., managing user sessions, configuring virtual servers, resource provisioning) API-driven operations for resource management, data retrieval, and configuration changes Full infrastructure lifecycle management (e.g., provisioning, updating, scaling infrastructure) Ease of Use High for Citrix admins with PowerShell knowledge. Easy scripting with cmdlets Low learning curve: Administrators should have some experience with Windows/PowerShell Moderate: Requires knowledge of API endpoints and coding (e.g., Python, JavaScript) Moderate learning curve: Requires learning the Citrix API and handling RESTful interactions Moderate: Requires knowledge of HCL (HashiCorp Configuration Language) for defining infrastructure Moderate learning curve: Requires learning Terraform language and understanding IaC principles Configuration Type Script-based: Commands specify exactly how to perform tasks Programmatic: Directly interacting with APIs to perform tasks Declarative: Define the desired end-state of infrastructure, and Terraform figures out the steps to achieve it Setup Complexity Low: No additional setup required apart from installing Citrix PowerShell SDKs Moderate: Requires understanding of Citrix API documentation and authentication (OAuth tokens, API keys) Moderate: Requires provider configuration (Citrix Cloud, Citrix Virtual Apps and Desktops) and Terraform installation Scalability Suitable for small to medium-scale automation tasks (one-time or scheduled scripts) High: Can be used in custom applications for large-scale integration, but requires handling concurrency, rate limits High: Scalable and reusable infrastructure configurations. Excellent for large, complex environments State Management No state management; scripts must be run manually or via automation tools (e.g., Scheduled Tasks) No inherent state management; the developer needs to handle state in applications Managed via state files, allowing tracking of infrastructure over time and ensuring consistent environments Version Control Possible to store scripts in version control, but no native versioning for tasks or infrastructure Version control through the code used to interact with the API, but no inherent versioning of infrastructure Native version control through configuration files, with tracking of infrastructure changes over time Integration with other Tools Integrates well with Windows-based automation tools and systems (e.g., Windows Task Scheduler, Jenkins) Can be integrated into custom software or DevOps pipelines (e.g., via CI/CD tools like Jenkins) Natively integrates with CI/CD tools (e.g., GitLab CI, Jenkins, Azure DevOps) for continuous delivery of infrastructure Best Usage Scenarios Managing Citrix sessions and desktops Configuring NetScaler traffic policies One-time tasks and small-scale automation Custom applications for managing Citrix resources Real-time data queries API-driven automation  Automating Citrix infrastructure provisioning and scaling Managing complex, multi-cloud or multi-environment Citrix deployments Some thoughts about choosing the right toolkit: PowerShell:  Best for administrators needing detailed control of Citrix components  REST API:  Best for developing custom applications for managing Citrix resources  Terraform:  Ideal for teams practicing infrastructure-as-code with a focus on scalability  Ansible:  Useful for automating post-install configurations, network, and application delivery configurations in a declarative manner Look at a comparison of creating the same Machine Catalog using Terraform, REST-API, and PowerShell: Terraform – Declarative Approach: resource "citrix_daas_machine_catalog" "TF-CC-MC" {
  depends_on = [ citrix_daas_hypervisor_resource_pool.TF-CC-HYPV-POOL ]
    name                                    = "${var.cc-mc-name}"
    description                             = "${var.cc-mc-description}"
    zone                                    = citrix_daas_hypervisor.TF-CC-HYPV.zone
    service_account                         = "${var.azurerm-vm-domainAdminUsernameWithoutDomain}"
    service_account_password                = "${var.azurerm-vm-domainAdminPassword}"
    allocation_type                         = "${var.cc-mc-allocationtype}" # "Random"
    session_support                         = "${var.cc-mc-sessiontype}" # "SingleSession"
    provisioning_scheme                     = {
        machine_config                      =  {
           hypervisor                       =  citrix_daas_hypervisor.TF-CC-HYPV.id
           hypervisor_resource_pool         =  citrix_daas_hypervisor_resource_pool.TF-CC-HYPV-POOL.id
            service_offering                = "${var.cc-mc-service_offering}"
            resource_group                  = "${var.azurerm-resource_group_name}"
            master_image                    = "${var.azurerm-masterimage-name}"
                                               }
        network_mapping =                      {
            network_device                  = "0"
            network                         = "${var.azurerm-subnet-name}"
                                               }
        number_of_total_machines            = "${var.cc-mc-machine_count}"
        machine_account_creation_rules      =  {
            naming_scheme                   = "${var.cc-mc-naming_scheme_name}"
            naming_scheme_type              = "${var.cc-mc-naming_scheme_type}" 
            domain                          = "${var.azurerm-vm-domainname}"
                                               }           
        os_type                             = "${var.cc-mc-os_type}" 
        storage_type                        = "${var.cc-mc-storage_type}" 
        use_managed_disks                   = "${var.cc-mc-use_managed_disks}" 
    }
}
REST-API – Declarative Approach, more complicated syntax due to strict formatting rules: POST https://api-eu.cloud.com/cvad/manage/MachineCatalogs?async=true 
Authorization: Bearer Citrix-CustomerId: Citrix-InstanceID: X-AdminCredential: Basic MjFceDIx… 

Body: 
{ "Name": "MC-AZ-HIB-NAME", "AllocationType":"Static", "MinimumFunctionalLevel":"LMAX", "PersistUserChanges":"OnLocal", "ProvisioningType":"MCS", "IsPowerManaged": true, "SessionSupport":"SingleSession", "Scopes":["00000000-0000-0000-0000-000000000000"], "Tenants":[], "Zone":"The Austrian Citrix Guy - AzHib", "VdaUpgradeType":"NotSet", "ProvisioningScheme":{ "MasterImagePath":"XDHyp:\\HostingUnits\\NW-TACG-XXXXX\\image.folder\\CTX-Hibtest2.resourcegroup\\CTX-HibTest-M_OsDisk.manageddisk", "CpuCount":null, "MemoryMB":null, "UseWriteBackCache":false, "NumTotalMachines":1, "NetworkMapping":[ {"DeviceNameOrId":"default","NetworkDeviceNameOrId":"0", "NetworkPath":"XDHyp:\\HostingUnits\\NW-TACG-XXXXX\\virtualprivatecloud.folder\\East US.region\\virtualprivatecloud.folder\\CTX-Hibtest2.resourcegroup\\TACG-HibTest-DCCC-vnet.virtualprivatecloud\\default.network" }], "Metadata": [ { "Name": "SupportsHibernation", "Value": "True" } ], "IdentityType":"ActiveDirectory", "MachineAccountCreationRules":{ "NamingScheme":"MCS-REST-W11-#","NamingSchemeType":"Numeric","Domain":"hib.the-austrian-citrix-guy.at","OU":"CN=Computers,DC=hib,DC=the-austrian-citrix-guy,DC=at"}, "MachineProfilePath":"XDHyp:\\HostingUnits\\NW-TACG-XXXXX\\machineprofile.folder\\CTX-Hibtest2.resourcegroup\\CTX-HibTest-M.vm", "PrepareImage":true, "DedicatedTenancy":false, "SecurityGroups":null, "UseFullDiskCloneProvisioning":false,"CustomProperties":[{"Name":"UseManagedDisks","Value":"true"},{"Name":"OsType","Value":"Windows"},{"Name":"StorageType","Value":"Premium_LRS"},{"Name":"LicenseType","Value":"Windows_Server"},{"Name":"Zones","Value":"1"}], "ServiceOfferingPath":"XDHyp:\\HostingUnits\\NW-TACG-XXXXX\\serviceoffering.folder\\Standard_D2s_v5.serviceoffering"}, "AdminFolder":"0" } 
PowerShell – Imperative Approach, the most complicated, but most flexible way: [CmdletBinding()] param ( [Parameter(Mandatory=$true)] [string] $hostingUnitPath, [Parameter(Mandatory=$true)] [string] $catalogName, [string] $catalogDescription, [Parameter(Mandatory=$true)] [int] $numVmsToCreate, [string] $adminAddress, [Parameter(Mandatory=$true)] [string] $namingScheme, [string] $OU, [Parameter(Mandatory=$true)] [string] $domain, [Parameter(Mandatory=$true)] [string] $masterImagePath ) 

Set-HypAdminConnection -AdminAddress $adminAddress 
$hostingUnit = get-item $hostingUnitPath 
$hostConnection = $hostingUnit.hypervisorConnection $brokerHypConnection = Get-BrokerHypervisorConnection -HypHypervisorConnectionUid $hostConnection.HypervisorConnectionUid 

$catalog = New-BrokerCatalog -AllocationType 'Permanent' -Description $catalogDescription -IsRemotePC $False ` -MinimumFunctionalLevel 'L7' -Name $catalogName -PersistUserChanges 'OnPvd' -ProvisioningType 'MCS' ` -Scope @() -SessionSupport 'SingleSession' -LoggingId $loggingId -AdminAddress $adminAddress 

$adPool = New-AcctIdentityPool -IdentityPoolName $catalogName -NamingScheme $namingScheme ` -NamingSchemeType 'Numeric' -OU $OU -Domain $domain -AllowUnicode ` -LoggingId $loggingId -AdminAddress $adminAddress Set-BrokerCatalogMetadata -CatalogId $catalog.Uid -Name 'Citrix_DesktopStudio_IdentityPoolUid' ` -Value $adPool.IdentityPoolUid -LoggingId $loggingId -AdminAddress $adminAddress

$provSchemeTaskID = New-ProvScheme -ProvisioningSchemeName $catalogName -HostingUnitUID $hostingUnit.HostingUnitUID ` -IdentityPoolUID $adpool.IdentityPoolUid -CleanOnBoot -MasterImageVM $masterImagePath -UsePersonalVDiskStorage ` -RunAsynchronously -LoggingId $loggingId -AdminAddress $adminAddress 

$ProvTask = get-provTask -TaskID $provSchemeTaskID -AdminAddress $adminAddress $taskProgress = 0 write-host "Creating New ProvScheme" while ($provTask.Active -eq $true) 

try {$totalPercent = if ($provTask.TaskProgress){$provTask.TaskProgress} else {0}} catch {} 
sleep 30 
$ProvTask = get-provTask -TaskID $provSchemeTaskID -AdminAddress $adminAddress } 

$provScheme = get-provScheme -ProvisioningSchemeUID $provTask.ProvisioningSchemeUid $controllers = Get-BrokerController | select DNSName Add-ProvSchemeControllerAddress -ProvisioningSchemeUID $provScheme.ProvisioningSchemeUID -ControllerAddress $controllers ` -LoggingId $loggingId -AdminAddress $adminAddress 

Set-BrokerCatalog -InputObject $catalog -ProvisioningSchemeId $provTask.ProvisioningSchemeUid -LoggingId $loggingId -AdminAddress $adminAddressDecide for yourself which option is most convenient for you.  Automating the Citrix PlatformAutomated deployments and configurations, along with Infrastructure-as-Code (IaC), are essential components of modern IT infrastructure management. These practices streamline operations, reduce human error, and enhance consistency across environments. In regulated industries such as finance, healthcare, and government, adopting IaC is not just beneficial but often mandatory due to compliance and audit requirements. What is Infrastructure-as-Code (IaC)Infrastructure as Code (IaC) represents a contemporary methodology for overseeing and provisioning computing resources through the use of machine-readable definition files, rather than relying on physical hardware setups or interactive configuration tools. Traditionally, setting up infrastructure involves manual processes, such as connecting servers, installing operating systems, and configuring networks. However, with the advent of cloud computing, there has been an increasing necessity for scalable, repeatable, and automated infrastructure solutions. IaC was developed to fulfill this requirement. Early tools like Puppet and Chef laid the groundwork for declarative configuration management, while subsequent advancements such as Terraform and AWS CloudFormation revolutionized IaC by enabling entire infrastructure environments to be defined and deployed through code.  Key PrinciplesDeclarative Configuration:  Define the desired state of infrastructure, and the tool ensures it matches  Version Control:  Infrastructure definitions are stored in version control systems like Git  Automation:  Infrastructure is provisioned automatically without manual intervention  Idempotency:  Applying the same configuration multiple times results in the same infrastructure state  Core PrerequisitesOne of the most important prerequisites is a functioning communication channel between the machines hosting the usual frameworks (e.g. Terraform, Packer, Ansible, PowerShell) and the Windows/Linux machines to be edited. Important There are two main ways of running commands remotely - WinRM and SSH. SSH is primarily used in the Linux world, while WinRM is used in the Windows world. Be sure to enable at least one communication path. After enabling, ensure the communication works as intended – you can use the short Terraform snippet mentioned later. Communication Flow between Terraform, Ansible, Packer, and the Target MachinesAll communication between Terraform, Packer, and Ansible on the IaC-VM and the target VMs on the Hypervisors or HyperScalers uses SSH and/or WinRM. WinRM (Windows Remote Management) is a Microsoft protocol that enables remote management of Windows systems by allowing access to remote computers to perform management tasks. It is Microsoft's implementation of the WS-Management (Web Services Management Protocol) standard.  It often uses SOAP (Simple Object Access Protocol) for communication over HTTP or HTTPS, using ports 5985 (HTTP) and 5986 (HTTPS) by default.  WinRM provides the basis for remote management with PowerShell and is used for tasks such as running remote scripts, automating, configuring, and performing system inventory. Therefore, WinRM and SSH must be configured correctly. Important We emphasize ensuring the communication flow works as intended from the outset of the pre-domain-joined stage; otherwise, Terraform, Ansible, and Packer will fail to configure the VMs and create the images. During the pre-domain-join stage, you can configure these settings using the local Group Policy Editor on the VM or in the autounattend.xml file if you use Packer to create the Master Image. After a successful domain join, you can set all relevant settings using GPOs. To provide maximum flexibility and a fallback mechanism, you can use both listeners—HTTP and HTTPS. You can check all available listeners using PowerShell: PS &gt; winrm e winrm/config/listener
Listener [Source="GPO"]
    Address = *
    Transport = HTTP
    Port = 5985
    Hostname
    Enabled = true
    URLPrefix = wsman
    CertificateThumbprint
    ListeningOn = 10.10.119.13, 127.0.0.1, ::1, fe80::287c:216e:9bd9:18e5%7

Listener
    Address = *
    Transport = HTTPS
    Port = 5986
    Hostname
    Enabled = true
    URLPrefix = wsman
    CertificateThumbprint = e3bf89dd77c95dd6c0f1b94480ea63782df4279e
    ListeningOn = 10.10.119.13, 127.0.0.1, ::1, fe80::287c:216e:9bd9:18e5%7WinRM Secure Enablement ChecklistEnabling WinRM must be carefully planned, as it exposes potential attack vectors on Windows machines. Caution Be sure to enable WinRM in the most secure, least-privileged way possible. Look at an example checklist for enabling WinRM: Control Area Action / Best Practice Domain-Joined Workgroup Compliance Mapping Preparation Confirm OS support, patch systems, plan firewall rules (5985/5986), decide authentication model ✅ ✅ CIS 18.9.84, NIST SI‑2, ISO A.12.6.1 Enablement Enable WinRM (via GPO in domain, winrm quickconfig in workgroup) ✅ (GPO) ✅ (Manual) CIS 18.9.84, NIST CM‑7, ISO A.9.2.3 Authentication Use Kerberos (preferred), avoid NTLM; configure HTTPS with certs ✅ ⚠️ NTLM fallback NIST IA‑2, ISO A.9.4.2, CIS 18.9.84 TrustedHosts Configure only specific hosts; avoid * N/A ✅ NIST AC‑3, ISO A.9.1.2 Certificates Deploy enterprise PKI certs (auto-enrollment) or self-signed/internal CA in workgroup ✅ ✅ NIST SC‑12, ISO A.10.1.2 Firewall Open 5985/5986; enforce via GPO or manual rules ✅ ✅ CIS 9.1, NIST SC‑7, ISO A.13.1.1 Access Control Restrict WinRM to admin/security groups; audit via Event Logs ✅ ✅ NIST AC‑6, ISO A.9.2.3, CIS 18.9.84 HTTPS Binding Bind cert to listener (winrm create winrm/config/Listener…) ✅ ✅ NIST SC‑12, ISO A.10.1.2 Disable HTTP Remove HTTP listener if HTTPS is enforced ✅ ✅ CIS 18.9.84, NIST SC‑8, ISO A.13.2.3 Session Tuning Adjust MaxMemoryPerShellMB, MaxShellsPerUser, IdleTimeout for automation scale ✅ ✅ NIST CM‑6, ISO A.12.1.3 Least Privilege Implement JEA (Just Enough Administration) endpoints ✅ ✅ NIST AC‑6, ISO A.9.1.2 Monitoring &amp; Logging Enable detailed WinRM logging; forward to SIEM ✅ ✅ NIST AU‑6, ISO A.12.4.1, CIS 6.2 Maintenance Patch regularly, rotate certificates, review TrustedHosts, audit access ✅ ✅ NIST SI‑2, ISO A.12.6.1, CIS 3.4  Example Flow for Enabling Secure WinRM CommunicationThis is an example of enabling Secure WinRM communication using PowerShell - please adapt to your regulations and needs: Enable WinRM Service: Enable-PSRemoting -Force
Set-Service -Name WinRM -StartupType Automatic Create a Self‑Signed Certificate: $CertCN = $env:COMPUTERNAME
$cert = New-SelfSignedCertificate -DnsName $CertCN -CertStoreLocation Cert:\LocalMachine\My
$thumbprint = $cert.Thumbprint Configure HTTPS Listener: winrm delete winrm/config/Listener?Address=*+Transport=HTTPS 2&gt;$null
winrm create winrm/config/Listener?Address=*+Transport=HTTPS "@{Hostname=`"$CertCN`"; CertificateThumbprint=`"$thumbprint`"}" Configure TrustedHosts: $TrustedHosts = "192.168.0.100,192.168.0.101"   # Replace with your management servers
Set-Item WSMan:\localhost\Client\TrustedHosts -Value $TrustedHosts -Force Configure Firewall: If (-Not (Get-NetFirewallRule -DisplayName "WINRM HTTPS" -ErrorAction SilentlyContinue)) {
    New-NetFirewallRule -Name "WINRM_HTTPS" -DisplayName "WINRM HTTPS" -Protocol TCP -LocalPort 5986 -Action Allow
} Testing WinRM Communication FlowAfter enabling WinRM communication, test the flow. Therefore, use the following Terraform snippet: Unsecure Communication - connecting to the HTTP listener – TestWinRM-Unsecure.tf resource "null_resource" "UploadTestFileToVMToTest" {
  connection {
    type     = var.Provisioner_Type
    user     = var.Provisioner_Admin-Username
    password = var.Provisioner_Admin-Password
    host     = var.Provisioner_ToTest-IP
    timeout  = var.Provisioner_Timeout

  }

  ###### Upload Test script to VMToTest
  provisioner "file" {
    source      = "${path.module}/data/Test-Script.ps1"
    destination = "c:/temp/Test-Script.ps1"

  }
} 

resource "null_resource" "InstallOnVMToTest" {
  depends_on = [null_resource.UploadTestFileToVMToTest]
  connection {
    type     = var.Provisioner_Type
    user     = var.Provisioner_Admin-Username
    password = var.Provisioner_Admin-Password
    host     = var.Provisioner_ToTest-IP
    timeout  = var.Provisioner_Timeout

  }
  provisioner "remote-exec" {
    inline = [
      "powershell -File c:/temp/Test-Script.ps1"
    ]
  }
}
Secure Communication - connecting to the HTTPS listener – TestWinRM-Secure.tf resource "null_resource" "UploadTestFileToIP1" {
 connection {
    type            = var.Provisioner_Type
    user            = var.Provisioner_SecureAdmin-Username
    password        = var.Provisioner_SecureAdmin-Password
    host            = var.Provisioner_ToTest-IP
    port            = var.Provisioner_Port
    https           = var.Provisioner_HTTPS
    timeout         = var.Provisioner_Timeout

  }

###### Upload Test script to IP1
  provisioner "file" {
     source      = "${path.module}/DATA/Test-Script.ps1"
    destination = "c:/temp/Test-Script.ps1"
    
  } 
}

resource "null_resource" "InstallonIP1" {
depends_on = [ null_resource.UploadTestFileToIP1 ]
connection {
    type            = var.Provisioner_Type
    user            = var.Provisioner_SecureAdmin-Username
    password        = var.Provisioner_SecureAdmin-Password
    host            = var.Provisioner_ToTest-IP
    port            = var.Provisioner_Port
    https           = var.Provisioner_HTTPS
    timeout         = var.Provisioner_Timeout

  }
 provisioner "remote-exec" {
    inline = [
      "powershell -File c:/temp/Test-Script.ps1"
    ]
  }
}TestWinRM-auto.tfvars.json  {
    "Provisioner_Type":"winrm",
    "Provisioner_Admin-Username":"XxXxXxX",
    "Provisioner_Admin-Password":"XxXxXxXxX",
    "Provisioner_SecureAdmin-Username":"XxXxXxX",
    "Provisioner_SecureAdmin-Password":"XxXxXxXxX",
    "Provisioner_ToTest-IP":"10.53.16.101",
    "Provisioner_Port":443,
    "Provisioner_HTTPS":true,	
    "Provisioner_Timeout":"20s"
}These snippets try to upload a script from the Terraform host to a Virtual Machine and to execute the script. If you can see “Connected” in the following output, you can be sure that the communication between the Terraform (or Packer or Ansible) host works as intended: Unsecure connection established: XxXxXxXxX@devops-automation:/etc/terraform$ terraform apply

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:

  # null_resource.InstallOnVMToTest will be created
  + resource "null_resource" "InstallOnVMToTest" {
      + id = (known after apply)
    }

  # null_resource.UploadTestFileToVMToTest will be created
  + resource "null_resource" "UploadTestFileToVMToTest" {
      + id = (known after apply)
    }

Plan: 2 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.UploadTestFileToVMToTest: Creating...
null_resource.UploadTestFileToVMToTest: Provisioning with 'file'...
#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadTestFileToVMToTest: Creation complete after 2s [id=XxXxX]
null_resource.InstallOnVMToTest: Creating...
null_resource.InstallOnVMToTest: Provisioning with 'remote-exec'...
null_resource.InstallOnVMToTest (remote-exec): Connecting to remote host via WinRM...
null_resource.InstallOnVMToTest (remote-exec):   Host: 10.53.16.101
null_resource.InstallOnVMToTest (remote-exec):   Port: 5985
null_resource.InstallOnVMToTest (remote-exec):   User: XxXxXxX
null_resource.InstallOnVMToTest (remote-exec):   Password: true
null_resource.InstallOnVMToTest (remote-exec):   HTTPS: false
null_resource.InstallOnVMToTest (remote-exec):   Insecure: true
null_resource.InstallOnVMToTest (remote-exec):   NTLM: false
null_resource.InstallOnVMToTest (remote-exec):   CACert: false
null_resource.InstallOnVMToTest (remote-exec): Connected!
#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;
null_resource.InstallOnVMToTest (remote-exec): C:\Users\azadmin&gt;powershell -File c:/temp/Test-Script.ps1
#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.InstallOnVMToTest: Creation complete after 3s [id=XxXxX]

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
XxXxXxXxX@devops-automation:/etc/terraform$Secure connection established: XxXxXxXxX@devops-automation:/etc/terraform$ terraform apply

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:

  # null_resource.InstallOnVMToTest will be created
  + resource "null_resource" "InstallOnVMToTest" {
      + id = (known after apply)
    }

  # null_resource.UploadTestFileToVMToTest will be created
  + resource "null_resource" "UploadTestFileToVMToTest" {
      + id = (known after apply)
    }

Plan: 2 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.UploadTestFileToVMToTest: Creating...
null_resource.UploadTestFileToVMToTest: Provisioning with 'file'...
#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadTestFileToVMToTest: Creation complete after 2s [id=XxXxX]
null_resource.InstallOnVMToTest: Creating...
null_resource.InstallOnVMToTest: Provisioning with 'remote-exec'...
null_resource.InstallOnVMToTest (remote-exec): Connecting to remote host via WinRM...
null_resource.InstallOnVMToTest (remote-exec):   Host: 10.53.16.101
null_resource.InstallOnVMToTest (remote-exec):   Port: 5986
null_resource.InstallOnVMToTest (remote-exec):   User: XxXxXxX
null_resource.InstallOnVMToTest (remote-exec):   Password: true
null_resource.InstallOnVMToTest (remote-exec):   HTTPS: true
null_resource.InstallOnVMToTest (remote-exec):   Insecure: false
null_resource.InstallOnVMToTest (remote-exec):   NTLM: false
null_resource.InstallOnVMToTest (remote-exec):   CACert: true
null_resource.InstallOnVMToTest (remote-exec): Connected!
#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;
null_resource.InstallOnVMToTest (remote-exec): C:\Users\azadmin&gt;powershell -File c:/temp/Test-Script.ps1
#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.InstallOnVMToTest: Creation complete after 3s [id=XxXxX]

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
XxXxXxXxX@devops-automation:/etc/terraform$ Communication Flow using SSHSSH, known as Secure Shell, is a protocol designed for secure remote system login. It is primarily used for accessing remote Linux servers. An SSH server establishes a secure and encrypted connection that facilitates remote management, file transfers, and tunneling. Its significance lies in its ability to supersede older, less secure protocols such as Telnet, support automation and DevOps practices, and provide secure remote access across various platforms including Linux, Windows, and network devices. Important Windows Server 2025 already includes an OpenSSH-based SSH server. SSH Secure Enablement ChecklistAs for WinRM, enabling SSH must be carefully planned, as it exposes potential attack vectors on the target machines. Caution Be sure to enable SSH in the most secure, least-privileged way possible. Look at an example checklist for enabling SSH on a Linux machine: SSH Server Hardening Checklist with Compliance Mapping Control Area Action / Best Practice Compliance Mapping (examples) Preparation Keep OpenSSH and OS patched; back up sshd_config; define access scope CIS 5.1, NIST SI‑2, ISO A.12.6.1 Authentication Disable root login (PermitRootLogin no); disable password auth; enforce key‑based auth; use strong keys (RSA 3072+, Ed25519); rotate keys; enable MFA CIS 5.2.8, NIST IA‑2, ISO A.9.4.2 Access Control Restrict users/groups (AllowUsers, AllowGroups); limit source IPs; use bastion hosts; disable empty passwords CIS 5.2.9, NIST AC‑3, ISO A.9.1.2 Cryptography Force SSH‑2 only; allow modern ciphers (chacha20, AES‑GCM); strong MACs (SHA‑2); secure KEX (curve25519) CIS 5.2.11, NIST SC‑12, ISO A.10.1.2 Session Management Idle timeout (ClientAliveInterval 300, ClientAliveCountMax 2); limit concurrent sessions; reduce MaxAuthTries; configure legal banner CIS 5.2.12, NIST AC‑11, ISO A.9.4.3 Logging &amp; Monitoring Set LogLevel VERBOSE; centralize logs; monitor failed logins (fail2ban/sshguard); audit authorized keys CIS 4.1, NIST AU‑6, ISO A.12.4.1 Network &amp; Firewall Restrict SSH to trusted networks; optionally change default port; enable TCP wrappers; apply rate limiting CIS 9.1, NIST SC‑7, ISO A.13.1.1 Advanced Hardening Use chroot/restricted shells for untrusted accounts; disable port/X11/agent forwarding unless required; consider SSH certificates for scalable key management CIS 5.2.14, NIST AC‑6, ISO A.9.2.3 Maintenance Patch OpenSSH regularly; rotate keys; review access quarterly; validate against CIS/NIST/ISO benchmarks CIS 3.4, NIST SI‑2, ISO A.12.6.1  Example Flow for Enabling SSH Communication on UbuntuThis is an example of enabling SSH communication on Ubuntu: Install SSH: sudo apt update
sudo apt install openssh-server Check SSH Service Status and start it, if necessary: sudo systemctl status ssh
sudo systemctl start ssh
sudo systemctl enable ssh Configure Firewall: sudo ufw allow ssh
sudo ufw enable Harden SSH Configuration by editing /etc/ssh/sshd_config: sudo nano /etc/ssh/sshd_config
Port 22                     # Or change to a non-standard port
Protocol 2                  # Enforce SSH-2 only
PermitRootLogin no          # Disable direct root login
PasswordAuthentication no   # Enforce key-based authentication
PermitEmptyPasswords no     # Disallow empty passwords
MaxAuthTries 3              # Limit login attempts
AllowUsers adminuser        # Restrict access to specific users
ClientAliveInterval 300     # Disconnect idle sessions after 5 minutes
ClientAliveCountMax 2
LogLevel VERBOSE            # Enable detailed logging Restart SSH: sudo systemctl restart ssh Set up Key-Based Authentication (if wanted): ssh-keygen -t ed25519 -C "admin@yourdomain"
ssh-copy-id adminuser@yourserver Ensure the storage location of the authorized keys is properly secured - ~/.ssh/authorized_keys  Example Flow for Enabling SSH Communication on Windows before Windows Server 2025This is an example of enabling SSH communication on Windows Server/Windows 11 Check if OpenSSH is available: PS&gt; Get-WindowsCapability -Online | Where-Object Name -like 'OpenSSH*'

Name  : OpenSSH.Client~~~~0.0.1.0
State : Installed

Name  : OpenSSH.Server~~~~0.0.1.0
State : NotPresent

PS&gt; Install the OpenSSH server components needed: PS&gt; Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0

Path          :
Online        : True
RestartNeeded : False

PS&gt; Configure OpenSSH Server: PS&gt; Set-Service -Name sshd -StartupType 'Automatic'
PS&gt; 
PS&gt; if (!(Get-NetFirewallRule -Name "OpenSSH-Server-In-TCP" -ErrorAction SilentlyContinue)) {
&gt;&gt;     Write-Output "Firewall Rule 'OpenSSH-Server-In-TCP' does not exist, creating it..."
&gt;&gt;     New-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' -DisplayName 'OpenSSH Server (sshd)' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22
&gt;&gt; } else {
&gt;&gt;     Write-Output "Firewall rule 'OpenSSH-Server-In-TCP' has been created and exists."
&gt;&gt; }
Firewall rule 'OpenSSH-Server-In-TCP' has been created and exists.
PS&gt;
PS&gt; Start-Service sshd
PS&gt; Example Flow for Enabling SSH Communication on Windows Server 2025Beginning with Windows Server 2025, OpenSSH is installed by default. You only need to follow Step 3 above to configure it.  Installing OpenSSH using PackerLater in this guide, we will show an example of how to use Packer to automatically install and configure SSH on a Windows-based master image.  Testing SSH Communication FlowAfter enabling SSH, test the communication flow. Therefore, use the Terraform snippet mentioned above and change it accordingly to use SSH.  Benefits of Infrastructure-as-CodeLet´s have a look at some important benefits when using Infrastructure-as-Code: Speed and Efficiency:What it really means: IaC turns infrastructure provisioning from a manual, ticket-driven process into deterministic code execution. This shortens lead time for changes and slashes toil across Day 0/1/2 operations (create, update, and maintain). Why it matters: Lead time: From request to ready environment drops from days/weeks to minutes/hours. MTTR (mean time to recovery): Improves because you can recreate, not repair. Self-service: Becomes viable (devs or platform consumers request pre-approved stacks via PR or portal). Example: A dev team spins up a production-like environment with Terraform modules + GitHub Actions in ~15 minutes, instead of opening IT tickets and waiting a week. Feature branches get on-demand preview environments, improving test fidelity and cycle time. KPIs to track: Lead time for infra changes Time to provision the new environment MTTR for infra incidents % change automation (vs. manual) Practices that enable it: Standard modules/blueprints (e.g., Terraform Registry modules, Azure Bicep modules) CI/CD for infra (GitHub Actions/Azure DevOps) with plan → review → apply Ephemeral/preview environments for feature branches Pre-approved patterns/guardrails (policy-as-code) to avoid human bottlenecks  Consistency and Standardization:What it really means: You codify “the right way” to build and configure infrastructure, so every environment is created from reusable modules with opinionated defaults. Why it matters: Fewer drift and misconfigurations (the top cause of outages/security gaps) Compliance by construction: encryption, logging, network boundaries baked into templates Predictability across dev/test/stage/prod increases confidence and reduces surprises Example: A “base Virtual Machine” module enforces: The same settings for all VMs are created based on the module All regulations and needs are met Centralized logging/metrics Tagging for cost and ownership Every service team consumes the same module versions via a module registry. KPIs to track: Drift rate (resources not matching code) Policy violations per deployment % of infra created via approved modules/templates Change failure rate Practices that enable it: Golden modules/blueprints with versioning and semantic versioning Policy-as-code (e.g., Azure Policy, Citrix Policy) in CI Guardrails in the pipeline (failing builds on violations) Module registries/internal catalogs for discoverability and re-use  Scalability:What it really means: IaC lets you scale breadth (many teams/environments) and depth (complex, multi-cloud, multi-account/tenant topologies) without linear increases in human effort. Why it matters: Add new environments/regions/accounts with minimal friction Safely manage complex estates (e.g., hub/spoke networks, shared services) Support rapid org growth and M&amp;A without fragile manual processes Example: A platform team uses composition: org &gt; account/subscription &gt; environment &gt; workload. Each layer references vetted modules Workspaces (or stacks) handle per-environment state and variables Global rollouts move from “snowflake” deployments to controlled, batched changes KPIs to track: Ratio of platform engineers to product teams supported Time to bring a new team/service online % automated vs. manual tasks Practices that enable it: Layered architecture (foundation → platform → product) Remote state with locking (e.g., Terraform backends) and clear state separation Workspaces/stacks and environment-specific variables Pipelines that fan out (matrix builds) for multi-environment changes  Version Control and Auditability:What it really means: Every infrastructure change is a code change—reviewed, versioned, traceable, and reversible. Why it matters: Auditable history: Who changed what, when, and why (PRs tie changes to tickets)  Safe rollbacks:  Revert git commits and re-apply  Separation of duties:  Reviewers/approvers defined in the repo, not ad hoc Example: A PR updating a database parameter includes a Terraform plan, cost estimate, and security policy checks. Approvers see the exact delta and impact before merging and applying. Six months later, auditors can reconstruct the decision trail. KPIs to track: % infra changes via PR (vs. console) Average reviewers per infra PR Time in review; % auto-approvals vs. manual Incidents tied to unreviewed changes Practices that enable it: Enforce “no-clickops” (block cloud console changes to production or detect &amp; revert drift) Mandatory code owners and review policies Signed commits, protected branches, and required checks (plan, policy, tests) Secrets management (no secrets in code; use KV/managed identities/OIDC)  Cost Management:What it really means: Cost control becomes proactive: you estimate before provisioning, enforce budgets and right-sizing through code, and automatically tear down idle resources. Why it matters: Prevents “surprise bills” from ad hoc provisioning Enables FinOps guardrails (budgets, quotas, instance families, tagging) Facilitates chargeback/showback with reliable tagging and ownership data Example: Pipelines run a cost-estimation step on the plan and fail the PR if it breaches a budget threshold.  Modules enforce mandatory tags (owner, cost center, environment).  Non-production resources default to auto-suspend or TTL policies. KPIs to track: % changes with pre-merge cost estimate Cost per environment/team vs. budget variance Idle/underutilized spend reclaimed Resource tagging coverage Practices that enable it: Tagging standards enforced in CI (and policy-as-code) Budget and quota policies at the org/subscription level Default sizes and autoscaling parameters in modules Scheduled shutdowns/TTL for non-prod and previews  Collaboration:What it really means: Infrastructure becomes a shared, reviewable artifact—just like application code—enabling platform, security, SRE, and app teams to work from a single source of truth. Why it matters: Requirements are codified rather than buried in tickets Reusable patterns reduce rework and cognitive load across teams Example: A platform repo hosts modules (networking, data stores, observability).  App teams reference these modules in their own repos, while central teams maintain the standards.  Security authors policies-as-code that run in every pipeline. Master image creators offer validated, proven master images for deployment. KPIs to track: Reuse ratio (modules used across teams) Time from request to approved pattern availability % of issues closed with community PRs Practices that enable it: Clear ownership: platform maintains modules; product teams consume Contribution guidelines and examples (scaffolding/templates) Documentation as code (README/diagrams in the repo) GitOps practices (PR-based changes, environments reconciled from git)  Cross-Cutting Enablers - all 6 benefits together:Testing for IaC:  Unit tests for modules (e.g., Terratest), static analysis (tflint, checkov), integration tests in ephemeral envs  Policy-as-Code:  OPA/Conftest, Azure Policy/Bicep rules, Security Hub/Config for AWS—fail fast in CI  Environment Strategy:  Strict separation of dev/test/stage/prod, promotion via artifacted module versions (not copy/paste)  State &amp; Secrets Hygiene:  Remote state with locking, least-privilege service principals, short-lived tokens via OIDC, no shared credentials  Drift Detection:  Scheduled plans or reconciler tools to detect and correct out-of-band changes  Change Management:  PR templates tying changes to tickets, risk classification, and approval routing  The Core Idea: Compliance by Construction + Continuous EnforcementIaC turns infrastructure requirements into executable standards. Instead of relying on docs and manual approvals, you codify controls so that: Compliant configurations are the only way to deploy (prevention), and Non‑compliant drift is detected and corrected continuously (detection + remediation). This directly supports control objectives in frameworks like ISO 27001 (e.g., A.12 change management), SOC 2 (Change Management &amp; Logical Access), NIST 800‑53 (CM, AU, SC), PCI DSS (network segmentation, logging, encryption), and HIPAA (access control, audit, transmission security). Controls by Construction (Standardized, Reusable Modules):What happens: You encapsulate required controls into golden modules/blueprints (e.g., Terraform modules, Bicep modules) with opinionated defaults. Teams consume these modules rather than crafting bespoke resources. Compliance impact: Encryption:  Default to encryption at rest/in transit; enforce KMS/Key Vault usage  Networking:  Private subnets, controlled egress, mandatory security groups/NSGs  Logging/Monitoring:  Centralized log sinks, metrics, and SIEM forwarding configured automatically  Tagging &amp; Ownership:  Mandatory tags (owner, data classification, cost center) for audit and chargeback  Backups &amp; DR: Standard backup policies baked in (RPO/RTO targets) Example: module "secure_storage" {
  source  = "org/secure-storage/azurerm"
  version = "1.4.2"

  # Enforced controls
  enable_encryption   = true
  encryption_key_id   = var.managed_key_id
  private_endpoint    = true
  logging_enabled     = true
  mandatory_tags = {
    owner        = var.owner
    environment  = var.env
    data_class   = var.data_classification   # e.g., PII, Confidential
  }
} Policy‑as‑Code Guardrails (Preventive &amp; Detective)What happens: You encode organizational policies into machine‑readable rules that run in CI/CD and/or are enforced in the cloud control plane. Compliance impact: Preventive controls:  Pipelines fail early when a change violates policy (e.g., public S3 bucket, open firewall)  Detective controls:  Runtime monitoring flags drift or misconfigurations post‑deploy  Consistent enforcement:  No subjective interpretation—policies are deterministic  Tools &amp; patterns: CI checks:  tflint, Regula against IaC code  Cloud enforcement:  Azure Policy, AWS Config + SCPs, GCP Organization Policy  Cost guardrails:  Infracost in PR to prevent budget violations  Auditability &amp; Evidence (Change History, Approvals, Plans)What happens: Every infrastructure change is a code change: it lives in Git, goes through PR review, and produces a plan that shows the exact delta before applying. Compliance impact: Full audit trail:  Who changed what, when, and why—linked to tickets/issues  Evidence artifacts:  Stored Terraform plans, policy check outputs, pipeline logs, and approvals  Rollback capability:  Revert to known‑good states; demonstrate controlled, reversible changes Operational practices: Protected branches, code owners, and required reviewers (segregation of duties) Signed commits; PR templates capturing risk and control references Retain pipeline logs and plan artifacts for the audit period  Continuous Compliance &amp; Drift ManagementWhat happens: IaC pipelines (or GitOps reconcilers) regularly compare the desired state to the actual runtime state; cloud‑native services scan for violations. Compliance impact: Drift detection:  Catch “clickops” or unapproved changes that break compliance  Automated remediation:  Reapply code or trigger playbooks to restore a compliant baseline  Real‑time posture:  Dashboards show compliance coverage and open findings  Common tooling: Scheduled terraform plan with alerts on unexpected changes AWS Config, Azure Policy, GCP Security Command Center for runtime checks SIEM hooks to surface violations into central incident workflows  Access Control &amp; Segregation of Duties (SoD)What happens: You implement role‑based access to repos, pipelines, and cloud identities, and limit blast radius with scoped service principals. Compliance impact: Least privilege:  Pipelines use narrowly scoped identities (e.g., workload identity/OIDC) per environment  SoD:  Different roles for authors, reviewers, and deployers; approvals enforced by policy  Controlled production:  No direct console changes; production is applied by approvals and automated checks  Data Residency, Classification &amp; LifecycleWhat happens: Data location rules and retention policies are enforced via code and guardrails Compliance impact: Residency:  Regions/subscriptions/projects constrained to approved geos  Classification:  Required tags drive handling rules (backup, encryption, access review cadence)  Lifecycle:  TTLs for non‑prod, automatic deletion of ephemeral resources to reduce surface area  Reporting &amp; Attestation (Proving Compliance)What happens: Pipelines produce machine‑readable reports, and posture tools consolidate findings for stakeholders. Compliance impact: Evidence on demand:  Exportable lists of compliant/non‑compliant resources, with timestamps and remediation notes  Control mapping:  Reports align checks to control IDs (e.g., ISO/NIST/PCI clauses) for audit narratives  Trend visibility:  Track improvement over time across teams and environments  Implementation Blueprint (Practical Steps)Define control objectives: Map regulatory requirements to technical checks (encryption, logging, network boundaries, SoD)  Codify standards into modules  Publish a curated catalog with opinionated defaults and semver  Integrate policy‑as‑code in CI/CD  Fail PRs on violations; include cost checks if relevant  Enforce runtime guardrails  Azure Policy/AWS Config/GCP Org Policy; block non‑compliant resources  Lock down production  Protected branches, mandatory reviews, no console changes (“no‑clickops”)  Establish drift detection  Scheduled plan + alerts; reconcile deviations automatically  Generate evidence Archive plans, check outputs, and approval logs; align with control catalogs  Measure &amp; iterate Publish KPIs to stakeholders; use trends to prioritize control improvements  Infrastructure-as-Code in Regulated IndustriesInfrastructure-as-Code (IaC) in regulated industries helps ensure compliance by making infrastructure management auditable, consistent, and automated. By using IaC, organizations can use version control, automated testing, and integrated governance to meet requirements in sectors like healthcare, finance, and government. This approach reduces human error and provides a verifiable history of every change, which is crucial for audits and risk mitigation. IaC supports these requirements by: Auditability and Traceability Every infrastructure change is stored in version control systems (e.g., Git), creating a complete audit trail Regulators demand evidence of who changed what, when, and why. IaC provides this automatically through commit histories and pull requests This aligns with frameworks such as GAMP® 5 and NIST guidance, which emphasize lifecycle management and the verification of IT systems  Consistency and Repeatability Manual provisioning introduces human error and configuration drift. IaC eliminates this by ensuring environments (dev, test, prod) are identical and reproducible This is crucial in industries like pharma, finance, and government, where even small deviations can invalidate compliance certifications Policy-as-Code for Compliance  Policy-as-Code for Compliance IaC integrates with policy-as-code frameworks (e.g., HashiCorp Sentinel) to enforce compliance rules automatically This ensures that security baselines, encryption standards, and access controls are applied consistently across multi-cloud and hybrid environments  Risk Reduction IaC enforces immutable infrastructure—instead of patching live systems, new compliant environments are provisioned from code This reduces the risk of untracked changes and strengthens cyber resilience, which regulators increasingly scrutinize  Faster Compliance Validation IaC templates can be pre-validated against regulatory frameworks (e.g., HIPAA, SOX, GDPR) Automated testing and compliance scans shorten audit cycles and reduce the cost of demonstrating conformity  Facilitating disaster recovery through reproducible infrastructure setups Using IaC allows a faster disaster recovery due to proven processes, well-tested scripts  Implementation and best practicesTreat IaC like application code:  IaC should be developed and managed using risk-based software development practices, including verification and validation before deployment  Integrate with CI/CD:  Integrate IaC changes into CI/CD pipelines to trigger automated deployments, tests, and validations  Conduct regular validation:  Implement a validation and verification schedule that includes automated testing, peer code reviews, and security assessments  Embed security and compliance:  Incorporate security checks and compliance guardrails at every step of the IaC pipeline  Choose a declarative approach:  When possible, use a declarative IaC approach, which defines the desired final state rather than a series of steps. The system then determines the best way to achieve that state, simplifying management and increasing consistency   What is Continuous Integration and Continuous Deployment (CI/CD)Continuous Integration and Continuous Deployment (CI/CD) are practices that complement Automation and IaC. CI/CD stands for Continuous Integration and Continuous Delivery/Deployment. It is a set of practices that enable development teams to deliver code changes more frequently and reliably. Continuous Integration (CI):  CI involves automatically integrating code changes from multiple contributors into a shared repository. For example, Jenkins triggers builds and tests on each commit, ensuring that new changes do not break existing functionality  Continuous Delivery (CD):  CD ensures that code is always deployable. For example, Jenkins automates application deployment to staging environments, allowing manual approval before release to production  Continuous Deployment:  Continuous Deployment extends CD by automatically deploying every change that passes tests to production without manual intervention  CI/CD for Infrastructure DeploymentInfrastructure as Code (IaC) allows teams to manage infrastructure using code. For example, Jenkins integrates with tools such as Terraform and Ansible to automate infrastructure provisioning and configuration. A typical CI/CD pipeline for infrastructure might look like this: Validate IaC templates Generate execution plans (e.g., terraform plan) Apply changes (e.g., terraform apply) Run compliance and security checks Destroy resources if needed (for ephemeral environments)  CI/CD for Master Image DeploymentMaster images are pre-configured virtual machine templates that include operating systems, patches, and software. Jenkins can automate the creation and deployment of these images using Packer, Terraform, and Ansible. A typical CI/CD pipeline for master image deployment: Trigger Packer to build the image Run Ansible during the build to install software Publish the image to a cloud image gallery (e.g., Azure Shared Image Gallery) Create test VMs from the image Run post-deployment validation using Ansible or other tools  CI/CD Using GitHub Actions, Azure Pipelines, and JenkinsThere are various ways to implement Continuous Integration and Continuous Deployment (CI/CD) using GitHub Actions, Azure Pipelines, and Jenkins. GitHub Actions:  GitHub Actions is GitHub’s built‑in automation and CI/CD platform. You define workflows as YAML files in your repo that run on specific events (like push, pull_request, schedules, or manual triggers) and execute on runners (GitHub‑hosted Linux/Windows/macOS VMs, or your own self‑hosted machines). It’s used to build, test, deploy, and automate repository tasks  Azure Pipelines:  Offers seamless integration with Azure services, supports YAML-based pipeline definitions, and provides robust security and compliance features  Jenkins:  An open-source automation server that supports a wide range of plugins for building, deploying, and automating software projects All tools enable automated testing, deployment, and rollback, ensuring high-quality releases and reducing downtime. Example we have implemented using Jenkins:   What is DevOpsDevOps encompasses a collection of practices that integrates software development (Dev) with IT operations (Ops). Its primary objective is to reduce the development lifecycle and facilitate continuous delivery while maintaining high standards of software quality. More than merely a methodology, DevOps represents a cultural transformation that promotes collaboration among teams that have traditionally operated in isolation. The emergence of the DevOps concept was a response to the difficulties organizations encountered in rapidly and reliably delivering software. Prior to the advent of DevOps, development and operations teams functioned independently, which often resulted in delays, poor communication, and failures during deployment. Organizations recognized the necessity for enhanced collaboration and automation within the software delivery process. DevOps has its roots in Agile methodologies and has integrated principles from Lean practices as well as Continuous Delivery strategies.  DevOps MethodologiesAssess the Citrix Infrastructure and ProcessesGoal:  Build a current‑state map of services, automation entry points, and operational constraints to inform IaC scope and sequencing. What to inventory: Citrix DaaS / CVAD control‑plane &amp; resource locations:  Note whether to use the Citrix Terraform provider (recommended), the Citrix DaaS Remote PowerShell SDK, or the DaaS REST APIs for orchestration  These are primary automation surfaces  NetScaler (Citrix ADC) &amp; ADM:  Confirm versions and whether configuration is done via NITRO API, Terraform provider, or Ansible collection; ADM may act as a proxy and automation hub  StoreFront, WEM, Provisioning (PVS), MCS:  Identify if you rely on PowerShell SDKs (CVAD/StoreFront/WEM) for scripted operations, or if you could use Terraform  Monitoring paths:  Capture telemetry sources (Citrix Monitor Service OData API) and NetScaler Prometheus/Observability pipelines for metrics/logs/traces; these will feed CI/CD quality gates and SRE dashboards Outcome:  A capability matrix—what can be automated via REST/SDK today, which gaps require upgrades or tooling, and which processes (provisioning, config, change, incident) are candidates for codification first. (Use Citrix Cloud API clients/service principals as the auth model for control‑plane automation.)  Identify Existing Usage of Automation‑ and Integration‑ToolsObjective:  Catalog all existing items to avoid duplication and guide standardization  Select Tools Aligned with Existing StandardsRecommended stack (aligned to Citrix &amp; cloud ecosystem): Provisioning IaC:  Terraform for cloud infrastructure and resource locations, and NetScaler/SDX configurations  Configuration Mgmt:  Ansible for Windows configuration and NetScaler run‑time objects (lbvserver, bindings, policies), use Terraform for Daas/CVAD entity configuration PowerShell SDKs can be used out of Terraform and Ansible, where no Terraform or Ansible modules exist  CI/CD:  GitHub Actions with OIDC federation to Azure or Vault for plan/test/apply workflows and policy validation in pipelines  Governance:  Azure Policy as Code with EPAC to manage and audit policy definitions/assignments as code (GitOps)  Observability:  NetScaler Prometheus integration and Citrix Monitor Service OData API for CVAD; wire into Grafana/Splunk as needed  Define Source Control and Versioning StrategyPatterns to adopt: Mono‑repo vs. multi‑repo:  Keep platform modules (Terraform providers/modules, Ansible roles) in a platform repo, and workload‑specific stacks in application repos. Use reusable workflows for consistency across repos  Branching &amp; environments:  Git‐based promotion (dev -&gt; test -&gt; prod) with environment‑scoped workflows and workload identity (OIDC) for per‑env credentials; store Terraform state, e.g., in Azure Blob with RBAC  Code Owners &amp; approvals:  Enforce PR reviews and protected branches; tie changes to tickets for auditability  Secrets model:  Prefer OIDC over long‑lived secrets; where secrets are unavoidable (e.g., on‑prem SQL, legacy scripts), isolate via environment secrets and ADM proxies  Automate Infrastructure ProvisioningTargets &amp; examples: NetScaler/SDX:  Declaratively provision VPX instances, HA pairs, GSLB, SSL offload via Terraform provider (NITRO under the hood); ADM proxying is supported in provider config  Citrix DaaS/CVAD: Automate Azure/Vsphere/AHV/EC2 components (VNets/VPCs, subnets, VMs), then call the respective Terraform/Ansible snippets to create and configure all needed entities like Machine Catalogs and Delivery Groups  Reference guides:  Citrix Tech Zone shows end‑to‑end IaC deployments (Terraform + Ansible + Packer) for DaaS resource locations on various Hypervisors and Hyperscalers, and DaaS/CVAD sites  Automate Configuration ManagementApproach by domain: NetScaler ADC:  Use the netscaler.adc Ansible collection. Works against ADC directly or via ADM proxy  CVAD / StoreFront / WEM:  Automate with Terraform and PowerShell SDKs called byTerraform/Ansible  Citrix DaaS: Automate completely using Terraform and Ansible  Implement / Define CI/CD Methodologies for Deployments and UpdatesTriggers:  Run plan on pull_request, apply on merge to main; use matrices for multi‑env fan‑out; store plan/apply artifacts  OIDC to Azure/AWS/GCP/etc.:  Authenticate pipelines without secrets; provision state backend (Blob) and federated identities per environment  Quality gates:  Static checks (terraform fmt/validate, tflint), policy validation (Azure Policy as Code), optional cost estimates  MonitoringTwo pillars for Citrix estates: NetScaler Observability:  Export metrics directly to Prometheus (pull/push), visualize in Grafana, and/or use the Observability Exporter to stream logs/traces to Splunk/Zipkin/Kafka  CVAD/DaaS Telemetry:  Consume service health, sessions, and machine stats via Monitor Service OData API for trend analysis, troubleshooting, and CI/CD post‑deploy verification  Governance (policy‑as‑code):Manage Azure Policy definitions/initiatives/assignments via EPAC; keep policies in Git, test in “non‑enforcing” mode, then promote; export non‑compliance reports. Security (pipeline &amp; runtime): Use OIDC federation for GitHub → Azure auth (short‑lived tokens; no long‑lived secrets in repos)  Scope identities per environment; enforce PR approvals and required checks in Actions before production applies  For NetScaler API access, follow NITRO authentication patterns (session/login and multi‑factor flows when applicable) Rollback &amp; drift: GitOps rollback:  Revert the offending commit/PR and re‑apply Terraform/Ansible; Terraform state + plans provide deterministic change sets  Drift detection:  Schedule terraform plan and compare against the desired state; use ADM configuration audit for ADC fleet to catch out‑of‑band changes  Integrate an existing Citrix Environment into your Automation StrategySince Terraform is the backend for our Automation Strategy across all our guides, we need to import existing infrastructure into an IaC-based representation. It would be impossible to recreate an existing infrastructure from scratch only to end up with an IaC-based representation. Citrix provides a script that lets you import your existing infrastructure into Terraform. You can find the script and explanations here:  https://github.com/citrix/terraform-provider-citrix/tree/main/scripts/onboarding-helper This automation script is designed to onboard an existing site to Terraform. It collects the list of resources from DDC, imports them into Terraform, and generates the Terraform skeletons for the site resources.  Onboarding ProcessThe onboarding process is easy – here are the main steps: Create a new folder for your Terraform project. Copy the terraform-onboarding.ps1 script and terraform.tf to the Terraform project directory created in step 1. Open a PowerShell session with Administrator privileges. Navigate to the directory where the terraform-onboarding.ps1 script is located. Set the Execution policy to Bypass in the PowerShell session. Run the script according to your needed structure. Please consult the documentation. Wait for the script to complete. The execution time will depend on the complexity of the onboarding process and the resources being imported. Once the script has finished running, check the .tf files for the output. The Terraform state file should also be updated with the site resources. Please note that the onboarding script masks all sensitive attributes in the generated Terraform files. Please update these placeholders with the appropriate values. At this point, if you run terraform plan, you should only see the sensitive attributes from step 9 being updated. Run terraform apply. This will synchronize the state file with the updated values of the sensitive attributes from step 9. If you run terraform plan again, you should see the following message: No changes. Your infrastructure matches the configuration..  This indicates that all the Citrix resources have been successfully onboarded.  The script creates a representation of each resource type, for example: citrix_admin_folder, citrix_admin_role, citrix_admin_scope, citrix_admin_user, citrix_application, citrix_application_group, citrix_application_icon, citrix_aws_hypervisor, citrix_azure_hypervisor, citrix_delivery_group, citrix_gcp_hypervisor, citrix_image_definition, citrix_machine_catalog, citrix_nutanix_hypervisor, citrix_openshift_hypervisor, citrix_policy_set, citrix_scvmm_hypervisor, citrix_service_account, citrix_storefront_server, citrix_tag, citrix_vsphere_hypervisor, citrix_xenserver_hypervisor, citrix_zone Look at the script in action:   Overview of the TriadIn all our guides and documentation, we use three distinct tools for all our core Automation tasks. Let´s call these the “Triad”. The Triad consists of these tools: Hashicorp Packer for Master Image Creation Hashicorp Terraform for the deployment of the Infrastructure components and as the backbone of the Automation task RedHat Ansible for System configurations and the deployment, installation, and configuration of Applications  HashiCorp PackerHashiCorp Packer is a foundational tool for organizations seeking to standardize, automate, and scale the creation of master images across multi-cloud and hybrid environments. As organizations increasingly adopt Infrastructure-as-Code (IaC) approaches, Packer provides a consistent, version-controlled, and repeatable way to build golden images aligned with modern DevOps and platform engineering practices. Standardization and Consistency Across Environments: Packer enables the creation of identically configured images for AWS, Azure, GCP, VMware, and other platforms from a single source template. This eliminates configuration drift and ensures that compute instances, virtual desktops, containers, or on-prem VMs are consistently built from the same baseline. For enterprises managing multiple environments, Packer’s multi-builder architecture ensures that every deployment starts from a deterministic, verified image.  Full Infrastructure-as-Code Alignment: Packer templates are declarative and integrate seamlessly into IaC workflows using tools such as Terraform, Ansible, and even cloud-native configuration tools. This yields several architectural advantages: Versioned images: Each new image build is immutable and tied to a version-controlled configuration. Reproducibility: Builds can be recreated deterministically at any time, supporting compliance and audit requirements. Automation-ready: Image creation fits naturally into CI/CD pipelines, enabling continuous delivery of operating system and middleware baselines.  Efficient, Scalable Deployments: Master images generated by Packer accelerate workload provisioning by drastically reducing runtime bootstrap configuration. This leads to: Faster autoscaling responsiveness Reduced risk of configuration failures during provisioning Decreased cloud compute costs due to shorter initialization times Better operational consistency across development, staging, and production These benefits scale exceptionally well for enterprise cloud deployments, VDI environments, and large-scale microservice platforms.  Multi-Cloud Flexibility and Future-Proofing: Packer’s provider-agnostic architecture helps organizations avoid lock-in and stay adaptable. A single template can create AMIs, Azure Managed Images, GCP images, Docker images, and VMware templates. This uniformity supports long-term cloud strategy, modernization efforts, and disaster recovery architectures that depend on identical images across regions and providers.  Integration with Modern Platform Engineering Practices: Packer integrates tightly with modern platform engineering toolchains: Terraform for downstream deployment Vault for secure secrets handling GitHub Actions, GitLab CI, Azure DevOps for automated pipelines This positions Packer as a first-class component in automated golden image pipelines, enabling platform teams to provide self-service, pre-validated, compliant images to developers and operations teams. Packer is a mature, efficient, and cloud-agnostic solution that brings the rigor of Infrastructure-as-Code to master image creation. It ensures repeatability, security, compliance, and operational speed while integrating seamlessly into modern CI/CD and platform engineering ecosystems. For organizations pursuing scalable, automated, and consistent image-based deployments, Packer is not only valid but strategically advantageous. Packer is an open-source tool that automates the creation of machine images for multiple platforms from a single source configuration. It enables developers and DevOps teams to define image-creation workflows in code, resulting in consistent, repeatable, and version-controlled image builds. Packer supports a wide range of platforms, including: AWS AMIs (Amazon Machine Images) Azure Managed Images Azure Compute Galleries Google Cloud Images Docker containers vSphere, XenServer, VirtualBox, and other Hypervisors  Core ConceptsImportant Packer does not replace configuration management like Chef, Puppet, or Ansible.   Packer can use tools to install software onto the image. In this handbook, we will focus on Packer using Ansible for configuration management and software installation, as well as Chocolatey for software installation. Immutable InfrastructurePacker promotes the concept of immutable infrastructure, where Images are not modified after deployment. Instead, new Images are built and deployed. This approach ensures zero Configuration drift as the Image contains all configurations. The reproducibility is also 100% - all Image builds are the same.  Common Use CasesGolden Image Creation: Create pre-configured, secure, and tested base images for cloud or on-prem environments  CI/CD Integration: Integrate with CI/CD pipelines to automatically build and test images during deployment  Multi-Cloud/Hybrid-Cloud Deployments: Build identical images for multiple cloud providers or even hybrid cloud deployments to ensure consistency across environments  Container Image Creation: Use Packer to build Docker images with consistent provisioning steps  Disaster Recovery: Maintain up-to-date images that can be quickly deployed in case of failure  Benefits of using PackerBenefit Description Consistency Ensures all environments use the same base image, reducing "it works on my machine" issues Automation Fully automates image creation, reducing manual errors and saving time Speed Speeds up instance provisioning by using pre-baked images Version Control Templates can be stored, for example, in Git, enabling change tracking and collaboration Multi-Platform Support Build images for multiple platforms from a single template Security Embed security patches and configurations into images before deployment This establishes Packer as a top-tier element within automated golden image workflows, allowing platform teams to deliver self-service, pre-validated, and compliant images for developers and operational teams. Packer is a well-established, effective, and cloud-agnostic tool that applies Infrastructure-as-Code principles to the process of creating master images. It guarantees consistency, security, compliance, and enhanced operational efficiency while fitting smoothly into contemporary CI/CD and platform engineering frameworks. For organizations aiming for scalable, automated, and uniform image-based deployments, Packer is not only relevant but also strategically beneficial. As an open-source solution, Packer streamlines the creation of machine images across various platforms using a single source configuration. This functionality allows developers and DevOps teams to articulate image creation procedures in code, ensuring that image builds are consistent, repeatable, and subject to version control.  Installing PackerThe installation of Packer is straightforward. There are many ways to install Packer – you can find more information about Installation on the Packer homepage. Note In our case, we use a dedicated Ubuntu-based VM that hosts all automation-related frameworks. You can find more information about creating and installing such a dedicated machine in this guide. Example of installing Packer on Ubuntu: XxXxXxXxX@tacg-cicd:~$curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add -
OK

XxXxXxXxX@tacg-cicd:~$echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(grep -oP '(?&lt;=UBUNTU_CODENAME=).*' /etc/os-release || lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
deb [arch=amd64 signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com noble main

XxXxXxXxX@tacg-cicd:~$ sudo apt-get update &amp;&amp; sudo apt-get install packer
Hit:1 http://at.archive.ubuntu.com/ubuntu noble InRelease
Get:2 http://at.archive.ubuntu.com/ubuntu noble-updates InRelease [126 kB]
Get:3 https://apt.releases.hashicorp.com noble InRelease [12.9 kB]
Ign:4 https://pkg.jenkins.io/debian binary/ InRelease
Get:5 http://at.archive.ubuntu.com/ubuntu noble-backports InRelease [126 kB]
Hit:6 https://pkg.jenkins.io/debian binary/ Release
Get:7 http://at.archive.ubuntu.com/ubuntu noble-updates/main amd64 Packages [1,110 kB]
Hit:9 https://ppa.launchpadcontent.net/ansible/ansible/ubuntu noble InRelease
Get:10 http://security.ubuntu.com/ubuntu noble-security InRelease [126 kB]
Get:11 http://at.archive.ubuntu.com/ubuntu noble-updates/main amd64 Components [161 kB]
Get:12 http://at.archive.ubuntu.com/ubuntu noble-updates/restricted amd64 Components [212 B]
Get:13 http://at.archive.ubuntu.com/ubuntu noble-updates/universe amd64 Packages [1,068 kB]
Get:14 http://at.archive.ubuntu.com/ubuntu noble-updates/universe amd64 Components [376 kB]
Get:15 http://security.ubuntu.com/ubuntu noble-security/main amd64 Components [21.6 kB]
Get:16 http://at.archive.ubuntu.com/ubuntu noble-updates/multiverse amd64 Components [940 B]
Get:17 http://at.archive.ubuntu.com/ubuntu noble-backports/main amd64 Components [7,072 B]
Get:18 http://at.archive.ubuntu.com/ubuntu noble-backports/restricted amd64 Components [216 B]
Get:19 http://at.archive.ubuntu.com/ubuntu noble-backports/universe amd64 Components [16.4 kB]
Get:20 http://at.archive.ubuntu.com/ubuntu noble-backports/multiverse amd64 Components [212 B]
Get:21 http://security.ubuntu.com/ubuntu noble-security/restricted amd64 Components [212 B]
Get:22 http://security.ubuntu.com/ubuntu noble-security/universe amd64 Components [52.2 kB]
Get:23 http://security.ubuntu.com/ubuntu noble-security/multiverse amd64 Components [212 B]
Fetched 3,206 kB in 1s (3,247 kB/s)
Reading package lists... Done
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following NEW packages will be installed:
  packer
0 upgraded, 1 newly installed, 0 to remove and 20 not upgraded.
Need to get 14.4 MB of archives.
After this operation, 42.0 MB of additional disk space will be used.
Get:1 https://apt.releases.hashicorp.com noble/main amd64 packer amd64 1.13.0-1 [14.4 MB]
Fetched 14.4 MB in 0s (32.6 MB/s)
Selecting previously unselected package packer.
(Reading database ... 223616 files and directories currently installed.)
Preparing to unpack .../packer_1.13.0-1_amd64.deb ...
Unpacking packer (1.13.0-1) ...
Setting up packer (1.13.0-1) ...
XxXxXxXxX@tacg-cicd:~$
XxXxXxXxX@tacg-cicd:~$ packer --version
Packer v1.13.0
XxXxXxXxX@tacg-cicd:~$
Packer CommandsPacker is controlled using its command-line interface (CLI). Calling packer without any subcommand shows all currently implemented commands: XxXxXxXxX@tacg-cicd:~$ packer
Usage: packer [--version] [--help] &lt;command&gt; [&lt;args&gt;]

Available commands are:
    build           build image(s) from template
    console         creates a console for testing variable interpolation
    fix             fixes templates from old versions of packer
    fmt             Rewrites HCL2 config files to canonical format
    hcl2_upgrade    transform a JSON template into an HCL2 configuration
    init            Install missing plugins or upgrade plugins
    inspect         see components of a template
    plugins         Interact with Packer plugins and catalog
    validate        check that a template is valid
    version         Prints the Packer version

XxXxXxXxX@tacg-cicd:~$ Note You can find a detailed description of the commands on the Packer homepage. For this guide, we will use only 3 commands – these make up the core workflow: packer init -upgrade path_to_template.hcl This command downloads and installs all needed plugins according to the template file. XxXxXxXxX@tacg-cicd:~/packer-templates/winsrv2025-ssh$ packer init -upgrade win2025-std-config.pkr.hcl
Installed plugin github.com/hashicorp/vsphere v1.4.2 in "/home/XxXxXxXxX/.config/packer/plugins/github.com/hashicorp/vsphere/packer-plugin-vsphere_v1.4.2_x5.0_linux_amd64"
Installed plugin github.com/rgl/windows-update v0.16.10 in "/home/XxXxXxXxX/.config/packer/plugins/github.com/rgl/windows-update/packer-plugin-windows-update_v0.16.10_x5.0_linux_amd64"
XxXxXxXxX@tacg-cicd:~/packer-templates/winsrv2025-ssh$packer validate -var-file=path_to_varfile.hcl path_to_template.hcl This command validates the syntax and configuration of a template file.  It returns a specific exit status depending on success or failure. An error message will be displayed if a template cannot be successfully validated. XxXxXxXxX@tacg-cicd:~/packer-templates/winsrv2025-ssh$ packer validate -var-file win2025-std.auto.pkrvars.hcl win2025-std-
config.pkr.hcl
The configuration is valid.
XxXxXxXxX@tacg-cicd:~/packer-templates/winsrv2025-ssh$packer build -var-file=path_to_varfile.hcl -force -on-error=abort path_to_template.hcl This command builds an image from the template file and the variables defined for it. The -force parameter instructs Packer to overwrite existing artifacts. If a previous build has created an artifact (such as an AMI or Vagrant box) with the same name or ID, Packer will delete or overwrite it without prompting. It also instructs Packer to skip confirmation prompts, thereby bypassing any interactive confirmation that might otherwise halt the build. In case of an error during the building process, the -on-error=abort parameter instructs Packer to exit without removing any created stuff, which is helpful during debugging. Example of the output of a successful image build: XxXxXxXxX@tacg-cicd:~/packer-templates/winsrv2025-ssh$ packer build -var-file win2025-std.auto.pkrvars.hcl -force -on-error=abort win2025-std-config.pkr.hcl
The configuration is valid.
vsphere-iso.winsrv2025: output will be in this color.

==&gt; vsphere-iso.winsrv2025: Creating virtual machine...
==&gt; vsphere-iso.winsrv2025: Customizing hardware...
==&gt; vsphere-iso.winsrv2025: Adding virtual machine flags...
==&gt; vsphere-iso.winsrv2025: Mounting ISO images...
==&gt; vsphere-iso.winsrv2025: Adding configuration parameters...
==&gt; vsphere-iso.winsrv2025: Creating floppy disk...
    vsphere-iso.winsrv2025: Copying files flatly from floppy_files
...
vsphere-iso.winsrv2025: Successfully installed
    vsphere-iso.winsrv2025:
==&gt; vsphere-iso.winsrv2025: Provisioning with Powershell...
==&gt; vsphere-iso.winsrv2025: Provisioning with powershell script: ./setup/post-setup.ps1
    vsphere-iso.winsrv2025: Disable SSH
==&gt; vsphere-iso.winsrv2025: Provisioning with powershell script: ./setup/disable-ssh.ps1
==&gt; vsphere-iso.winsrv2025: Running shutdown command...
==&gt; vsphere-iso.winsrv2025: Deleting floppy drives...
==&gt; vsphere-iso.winsrv2025: Deleting floppy image...
==&gt; vsphere-iso.winsrv2025: Ejecting CD-ROM media...
==&gt; vsphere-iso.winsrv2025: Removing CD-ROM devices...
    vsphere-iso.winsrv2025: Closing sessions ....
Build 'vsphere-iso.winsrv2025' finished after 57 minutes 36 seconds.

==&gt; Wait completed after 57 minutes 36 seconds

==&gt; Builds finished. The artifacts of successful builds are:
--&gt; vsphere-iso.winsrv2025: winsrv2025-01
XxXxXxXxX@tacg-cicd:~/packer-templates/winsrv2025-ssh$Packer TerminologyA short overview of terms related to Packer: Artifacts Each Builder creates a single Artifact during the building process. For example, the vSphere Builder creates a directory containing all the files of the created VM. Builds A Build is a task that produces an Artifact according to the instructions in the Template. Builder A Builder is specific to each platform and is the process that creates the Virtual Machine. Each Builder has different capabilities and needs depending on the underlying platform.  For example, the azure-arm builder allows you to build VHDs and Managed Images in ARM. The vsphere-ISO uses an OS ISO file and builds a VM Image on vSphere.  Provisioner A Provisioner allows Packer to install Software before the Build finishes. Provisioners are the main component that adds all the desired elements to the underlying OS. Provisioners can also call other IaC Frameworks, such as Ansible, or run scripts like Shell Scripts or PowerShell snippets, or upload files. It is imperative that communication between the Packer Machine and the Windows Machine via WinRM and/or SSH can be established without error. Post-Processor A Post-Processor takes a successful build and can be used, e.g., to upload an Artifact or package the files. For example, the vsphere Post-Processor uploads a successfully created Artifact to vSphere. Template A JSON- or HCL-based human-readable file that defines the build process. It holds the configuration and tells the Builder what to do. Data Source A Data Source fetches Data from the outer World and makes it accessible to Packer.  Important It is imperative that communication between the Packer Machine and the Windows Machine using WinRM and/or SSH can be established without error.  Note You can ensure that WinRM is configured correctly by adding the configuration script to the unattend.xml or autounattend.xml file, which Packer will use.  Packer StructurePacker uses HCL templates as the recipe for creating an Image. You must follow a simple structure in the Templates (see example of a Windows Server 2025 Template below): # Windows Server 2025 deployment using Hashicorp Packer

locals {
  SSHUser          = "XxXxXxXxXxXxX"
  SSHPass          = "XxXxXxXxXxXxX"
  WinRMUser        = "administrator"
  WinRMPass        = "XxXxXxXxXxXxX"
  vcenter_admin    = "admin@vspXxX.tmmXxXxX"
  vcenter_password = "XxXxXxXxXxXxX"
  timestamp        = regex_replace(timestamp(), "[- TZ:01]", "")
  buildtime        = formatdate("DD-MM-YYYY hh:mm ZZZ", timestamp())
  manifest_date    = formatdate("DD-MM-YYYY hh:mm:ss", timestamp())
}

packer {
  required_version = "&gt;= 1.12.0"
  required_plugins {
    vsphere = {
      version = "&gt;= v1.4.2"
      source  = "github.com/hashicorp/vsphere"
    }
  }
  required_plugins {
    windows-update = {
      version = "&gt;= 0.16.8"
      source  = "github.com/rgl/windows-update"
    }
  }
}

variable "host" {
  type        = string
  description = "Name of the Host to use"
}

variable "name" {
  type        = string
  description = "Name of the Image"
}
...
variable "vvtd_enabled" {
  type = string
}

source "vsphere-iso" "winsrv2025" {
  CPUs     = var.vm_cpus
  notes    = "TMM - Built by HashiCorp Packer on ${local.buildtime}."
  RAM      = var.vm_memory
  firmware = var.vm_firmware
  ...
  network_adapters {
    network      = var.vcenter_network
    network_card = var.vm_network_card
  }
  ...
  storage {
    disk_size             = var.vm_disk_size
    disk_thin_provisioned = var.vm_disk_thin
  }
  ...
  boot_order       = var.vm_boot_order
  boot_wait        = var.vm_boot_wait
  boot_command     = var.vm_boot_command
  shutdown_command = var.vm_shutdown_command
}

build {
  sources = ["source.vsphere-iso.winsrv2025"]

  provisioner "windows-update" {
    pause_before    = "30s"
    search_criteria = "IsInstalled=0"
    filters = [
      "exclude:$_.Title -like '*Preview*'",
      "exclude:$_.InstallationBehavior.CanRequestUserInput",
      "include:$true"
    ]
    restart_timeout = "120m"
  }

  provisioner "windows-restart" {
    restart_check_command = "powershell -command \"&amp; {Write-Output 'restarted.'}\""
    restart_timeout       = "20m"
  }

  provisioner "powershell" {
    elevated_user     = local.SSHUser
    elevated_password = local.SSHPass
    scripts = [
      "./setup/disable-autolog.ps1",
      "./setup/disable-ssh.ps1"
    ]
  }
}The locals {} block defines variables used locally in this particular Template. The packer {} block is the root block of this Template and contains all settings. It specifies a required version – in this case, at least Packer version 1.12 or higher. The required_plugins {} block defines all plugins needed for a successful build. The variable {} blocks define all the variable names, the types, and, if needed, default values. Packer looks for the values in the variable file mentioned in the CLI command you used to start the build (remember packer build -var-file=path_to_varfile.hcl -force -on-error=abort path_to_template.hcl). The source {} block configures a plugin for building an Image on a specific platform and defines all needed settings. Important This block differs based on the underlying platform and its needs. It can be reused whenever you need to build an Image on the same platform. The build {} block defines all the steps Packer should execute sequentially after the complete source {} block. Now, as all needed blocks are defined, you can build your image. If you are not sure whether your template is valid, you can run packer validate -var-file=path_to_varfile.hcl path_to_template.hcl to check for errors.  HashiCorp TerraformTerraform is one of the most widely adopted Infrastructure-as-Code (IaC) platforms for provisioning and managing cloud and on-prem infrastructure at scale. Its declarative configuration model, strong ecosystem support, and provider-agnostic architecture make it a foundational tool for organizations seeking consistency, compliance, and operational maturity in their infrastructure lifecycle. Terraform relies on declarative code to define infrastructure in a predictable, reproducible manner. By representing compute, networking, storage, IAM, and platform services as version-controlled code artifacts, Terraform: Eliminates configuration drift Ensures consistent provisioning across development, staging, and production Produces deployments that can be rebuilt deterministically This consistency is particularly valuable in regulated environments where documentation, repeatability, and environmental fidelity are required for audits and certification processes. For highly regulated verticals—such as finance, healthcare, government, and critical infrastructure—Terraform provides the governance, repeatability, and auditability required to meet stringent compliance obligations. These regulated verticals often operate under strict frameworks, such as: Finance:  PCI-DSS, FFIEC, SOX  Healthcare:  HIPAA, HITECH  Government/Public Sector:  FedRAMP, NIST 800-53, DoD SRG  Energy/Critical Infrastructure:  NERC CIP  Global Privacy Requirements:  GDPR, regional data sovereignty mandates Terraform helps organizations meet these obligations by providing: Complete Traceability and Auditability: All infrastructure changes can be captured in version control systems. This enables traceable change histories, approvals, and rollbacks—supporting SOX, NIST, and ISO 27001 requirements around change management and configuration governance.  Standardized, Repeatable Environments: Terraform modules enable organizations to define compliant “building blocks” that development teams cannot inadvertently modify, ensuring every environment meets baseline controls for security, networking, identity, and data protection.  Operational Efficiency and Reduced Risk: Terraform’s execution plan and state management capabilities allow organizations to preview changes before applying them, reducing misconfigurations—a key risk driver in regulated environments. Additional operational benefits include: Automated provisioning at scale Predictable change outcomes through planning and applying workflows Reduced human error, a significant source of compliance violations Consistent rollback and recovery patterns  Integration With Secure DevOps and Platform Engineering Practices: Terraform fits seamlessly into CI/CD, GitOps, and automated compliance frameworks, enabling regulated organizations to adopt modern DevOps practices without compromising governance. It perfectly integrates with frameworks such as: Terraform Cloud/Enterprise for controlled workflows Vault for secure secrets injection Packer for master image pipelines GitHub Actions, GitLab, Azure DevOps for automated deployment  Core FeaturesMulti-Cloud Provisioning: Terraform supports all major cloud providers (AWS, Azure, GCP, OCI) and many others via providers  Declarative Configuration: You define what infrastructure you want (not how to create it), and Terraform figures out the steps  Execution Plan (Preview Changes): Terraform generates an execution plan before making changes, showing you exactly what it will do  State Management: Terraform keeps track of resources it manages in a state file, enabling drift detection and dependency resolution  Modularization &amp; Reuse: You can build modules to encapsulate complex infrastructure patterns and reuse them  Easily and widely extendable: Terraform can be extended with custom Providers, Provisioners, and Data Sources  Core ConceptsImmutable InfrastructureTerraform promotes the concept of immutable infrastructure, where Resources are not modified after deployment. Instead, the Resources are recreated and replaced. This approach ensures zero Configuration drift as the Resources always have the same configurations. Reproducibility is also 100% - all Resources deployed across different Infrastructures use the same configuration.  Security and Compliance as CodeSecurity policies and compliance requirements can be codified into Terraform to ensure consistent enforcement.  Environment Management (Dev, Staging, Prod)Terraform allows managing separate Infrastructure environments through Workspaces or Folder-based patterns.  State Management &amp; Drift DetectionTerraform maintains a state file that represents the Infrastructure’s current state. This state can be used to detect configuration drift or unauthorized changes.  Automated Scaling and Auto-Healing InfrastructureTerraform works in tandem with cloud-native tools for dynamic scaling and fault recovery. That leads to improved reliability and reduced manual maintenance.  Common Use CasesCreation of Infrastructures based on Infrastructure-as-Code Create your Infrastructure in the Cloud or on-premises  CI/CD Integration Integrate with CI/CD pipelines to automatically build and test your Infrastructure during deployment  Multi-Cloud/Hybrid-Cloud Deployments Build identical Infrastructure for multiple Cloud providers or even hybrid Cloud deployments to ensure consistency across environments  Detect Configuration Drifts Use Terraform to ensure your Infrastructure is in the desired state. You can even automatically remediate any changes  Disaster Recovery Using Terraform ensures that your Infrastructure or Components can be quickly deployed in case of failure  Benefits of using TerraformBenefit Description Consistency Ensures all environments use the same IaC Codebase, reducing Configuration sprawl Automation Fully automates Infrastructure creation, reducing manual errors and saving time Speed Speeds up Infrastructure provisioning by using IaC Version Control Templates can be stored, for example, in Git, enabling change tracking and collaboration Multi-Platform Support Build deployments for multiple platforms from the same codebase with minor modifications if needed Security Ensure all entities match all security needs and regulations, as these settings are part of the modules  Installing TerraformThe installation of Terraform is straightforward. There are many ways to install Terraform – you can find more information about Installation on the Terraform homepage. Note In our case, we use a dedicated Ubuntu-based VM that hosts all automation-related frameworks. You can find more information about creating and installing such a dedicated machine in this guide. Example of installing Terraform on Ubuntu: XxXxXxXxX@tacg-cicd:~$ sudo apt install gnupg software-properties-common -y
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
gnupg is already the newest version (2.4.4-2ubuntu17).
gnupg set to manually installed.
software-properties-common is already the newest version (0.99.49.1).
software-properties-common set to manually installed.
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
XxXxXxXxX@tacg-cicd:~$
XxXxXxXxX@tacg-cicd:~$ wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
--2024-12-13 15:21:43--  https://apt.releases.hashicorp.com/gpg
Resolving apt.releases.hashicorp.com (apt.releases.hashicorp.com)... 13.225.47.104, 13.225.47.21, 13.225.47.116, ...
Connecting to apt.releases.hashicorp.com (apt.releases.hashicorp.com)|13.225.47.104|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 3980 (3.9K) [binary/octet-stream]
Saving to: ‘STDOUT’

-                             100%[=================================================&gt;]   3.89K  --.-KB/s    in 0s

2024-12-13 15:21:43 (488 MB/s) - written to stdout [3980/3980]

XxXxXxXxX@tacg-cicd:~$
XxXxXxXxX@tacg-cicd:~$ echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com noble main
XxXxXxXxX@tacg-cicd:~$
XxXxXxXxX@tacg-cicd:~$ sudo apt update
Hit:1 https://packages.microsoft.com/repos/code stable InRelease
Hit:2 https://esm.ubuntu.com/apps/ubuntu noble-apps-security InRelease
Hit:3 https://esm.ubuntu.com/apps/ubuntu noble-apps-updates InRelease
Hit:4 https://esm.ubuntu.com/infra/ubuntu noble-infra-security InRelease
Hit:5 https://esm.ubuntu.com/infra/ubuntu noble-infra-updates InRelease
Hit:6 http://azure.archive.ubuntu.com/ubuntu noble InRelease
Get:7 https://apt.releases.hashicorp.com noble InRelease [12.9 kB]
Hit:8 http://azure.archive.ubuntu.com/ubuntu noble-updates InRelease
Hit:9 http://azure.archive.ubuntu.com/ubuntu noble-backports InRelease
Get:10 https://apt.releases.hashicorp.com noble/main amd64 Packages [161 kB]
Hit:11 http://azure.archive.ubuntu.com/ubuntu noble-security InRelease
Fetched 174 kB in 1s (146 kB/s)
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
All packages are up to date.
XxXxXxXxX@tacg-cicd:~$ sudo apt install terraform -y
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following NEW packages will be installed:
  terraform
0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
Need to get 27.4 MB of archives.
After this operation, 90.2 MB of additional disk space will be used.
Get:1 https://apt.releases.hashicorp.com noble/main amd64 terraform amd64 1.10.2-1 [27.4 MB]
Fetched 27.4 MB in 2s (13.1 MB/s)
Selecting previously unselected package terraform.
(Reading database ... 179937 files and directories currently installed.)
Preparing to unpack .../terraform_1.10.2-1_amd64.deb ...
Unpacking terraform (1.10.2-1) ...
Setting up terraform (1.10.2-1) ...
Scanning processes...
Scanning linux images...

Running kernel seems to be up-to-date.

No services need to be restarted.

No containers need to be restarted.

No user sessions are running outdated binaries.

No VM guests are running outdated hypervisor (qemu) binaries on this host.
XxXxXxXxX@tacg-cicd:~$
XxXxXxXxX@tacg-cicd:~$ terraform --version
Terraform v1.10.2
on linux_arm64
XxXxXxXxX@tacg-cicd:~$Terraform CommandsTerraform is controlled using its command-line interface (CLI). Calling terraform without any subcommand shows all currently implemented commands: XxXxXxXxX@tacg-cicd:~$ terraform
Usage: terraform [global options] &lt;subcommand&gt; [args]

The available commands for execution are listed below.
The primary workflow commands are given first, followed by
less common or more advanced commands.

Main commands:
  init          Prepare your working directory for other commands
  validate      Check whether the configuration is valid
  plan          Show changes required by the current configuration
  apply         Create or update infrastructure
  destroy       Destroy previously-created infrastructure

All other commands:
  console       Try Terraform expressions at an interactive command prompt
  fmt           Reformat your configuration in the standard style
  force-unlock  Release a stuck lock on the current workspace
  get           Install or upgrade remote Terraform modules
  graph         Generate a Graphviz graph of the steps in an operation
  import        Associate existing infrastructure with a Terraform resource
  login         Obtain and save credentials for a remote host
  logout        Remove locally-stored credentials for a remote host
  metadata      Metadata related commands
  modules       Show all declared modules in a working directory
  output        Show output values from your root module
  providers     Show the providers required for this configuration
  refresh       Update the state to match remote systems
  show          Show the current state or a saved plan
  stacks        Manage HCP Terraform stack operations
  state         Advanced state management
  taint         Mark a resource instance as not fully functional
  test          Execute integration tests for Terraform modules
  untaint       Remove the 'tainted' state from a resource instance
  version       Show the current Terraform version
  workspace     Workspace management

Global options (use these before the subcommand, if any):
  -chdir=DIR    Switch to a different working directory before executing the
                given subcommand.
  -help         Show this help output or the help for a specified subcommand.
  -version      An alias for the "version" subcommand.
XxXxXxXxX@tacg-cicd:~$ Note You can find a detailed description of the commands on the Terraform homepage. Normally, we will use these commands – these make up the core workflow: terraform fmt This command formats all .tf files in the current directory according to the standard, making them much easier to read. XxXxXxXxX@tacg-cicd:/etc/terraform-tmm/CVADXS/TestWinRM$ terraform fmt
WinRM-provider-variables.tf
WinRM-provider.tf
WinRM-test-Secure.tf
WinRM-variables.tf
XxXxXxXxX@tacg-cicd:/etc/terraform-tmm/CVADXS/TestWinRM$ terraform init This command initializes a working directory containing Terraform configuration files.  This is the first command you should run after writing a new Terraform configuration or cloning an existing configuration from version control. It is safe to run this command multiple times. XxXxXxXxX@tacg-cicd:/etc/terraform-tmm/CVADXS/TestWinRM$ terraform init
Initializing the backend...
Initializing provider plugins...
- Finding latest version of hashicorp/null...
- Installing hashicorp/null v3.2.4...
- Installed hashicorp/null v3.2.4 (signed by HashiCorp)
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.
XxXxXxXxX@tacg-cicd:/etc/terraform-tmm/CVADXS/TestWinRM$ terraform validate This command validates the configuration files in a directory. It does not validate remote services, such as remote state or provider APIs. XxXxXxXxX@tacg-cicd:/etc/terraform-tmm/CVADXS/TestWinRM$ terraform validate
Success! The configuration is valid.

XxXxXxXxX@tacg-cicd:/etc/terraform-tmm/CVADXS/TestWinRM$ terraform plan This command generates an execution plan that lets you preview the changes Terraform plans to make to your infrastructure. The plan command alone does not actually carry out the proposed changes - use this command to check whether the proposed changes match what you expected before you apply the changes or share your changes with your team for broader review. XxXxXxXxX@tacg-cicd:/etc/terraform-tmm/CVADXS/TestWinRM$ terraform plan

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:

  # null_resource.InstallonIP1 will be created
  + resource "null_resource" "InstallonIP1" {
      + id = (known after apply)
    }

  # null_resource.InstallonIP2 will be created
  + resource "null_resource" "InstallonIP2" {
      + id = (known after apply)
    }

  # null_resource.UploadTestFileToIP1 will be created
  + resource "null_resource" "UploadTestFileToIP1" {
      + id = (known after apply)
    }

  # null_resource.UploadTestFileToIP2 will be created
  + resource "null_resource" "UploadTestFileToIP2" {
      + id = (known after apply)
    }

Plan: 4 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.
XxXxXxXxX@tacg-cicd:/etc/terraform-tmm/CVADXS/TestWinRM$In this example, Terraform lists all entities that would be created/changed/destroyed: Plan: 4 to add, 0 to change, 0 to destroy.  Important If Terraform detects that no changes are needed to the underlying infrastructure, it will report that no actions are required.  This functionality can be used for regular checks to ensure the infrastructure continues to meet your requirements. You will see a detailed description of this use case later.  terraform apply This command executes the operations proposed in a Terraform plan. When you run terraform apply without passing a saved plan file, Terraform automatically creates a new execution plan as if you had run terraform plan, prompts you to approve that plan, and performs the indicated operations. You can also use auto-approve to start the execution without asking for confirmation.  Extreme Caution If you use -auto-approve, make sure that no one changed your infrastructure or Terraform snippets without consent to minimize the risk of unpredictable changes.  XxXxXxXxX@tacg-cicd:/etc/terraform-tmm/CVADXS/TestWinRM$ terraform apply

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:

  # null_resource.InstallonIP1 will be created
  + resource "null_resource" "InstallonIP1" {
      + id = (known after apply)
    }

  # null_resource.InstallonIP2 will be created
  + resource "null_resource" "InstallonIP2" {
      + id = (known after apply)
    }

  # null_resource.UploadTestFileToIP1 will be created
  + resource "null_resource" "UploadTestFileToIP1" {
      + id = (known after apply)
    }

  # null_resource.UploadTestFileToIP2 will be created
  + resource "null_resource" "UploadTestFileToIP2" {
      + id = (known after apply)
    }

Plan: 4 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.UploadTestFileToIP1: Creating...
null_resource.UploadTestFileToIP1: Provisioning with 'file'...
null_resource.UploadTestFileToIP2: Creating...
null_resource.UploadTestFileToIP2: Provisioning with 'file'...
#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadTestFileToIP1: Creation complete after 1s [id=38299301982712762563]
#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadTestFileToIP2: Creation complete after 1s [id=2420916592744961178]
null_resource.InstallonIP2: Creating...
null_resource.InstallonIP2: Provisioning with 'remote-exec'...
null_resource.InstallonIP2 (remote-exec): Connecting to remote host via WinRM...
null_resource.InstallonIP2 (remote-exec):   Host: 10.10.119.32
null_resource.InstallonIP2 (remote-exec):   Port: 5985
null_resource.InstallonIP2 (remote-exec):   User: XxXxXxXxX
null_resource.InstallonIP2 (remote-exec):   Password: true
null_resource.InstallonIP2 (remote-exec):   HTTPS: false
null_resource.InstallonIP2 (remote-exec):   Insecure: false
null_resource.InstallonIP2 (remote-exec):   NTLM: false
null_resource.InstallonIP2 (remote-exec):   CACert: false
null_resource.InstallonIP2 (remote-exec): Connected!
#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;
null_resource.InstallonIP2 (remote-exec): C:\Users\Administrator&gt;powershell -File c:/temp/Test-Script.ps1
#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.InstallonIP2: Creation complete after 3s [id=98225693556377100230]
null_resource.InstallonIP2: Creating...
null_resource.InstallonIP2: Provisioning with 'remote-exec'...
null_resource.InstallonIP2 (remote-exec): Connecting to remote host via WinRM...
null_resource.InstallonIP2 (remote-exec):   Host: 10.10.119.31
null_resource.InstallonIP2 (remote-exec):   Port: 5985
null_resource.InstallonIP2 (remote-exec):   User: XxXxXxXxX
null_resource.InstallonIP2 (remote-exec):   Password: true
null_resource.InstallonIP2 (remote-exec):   HTTPS: false
null_resource.InstallonIP2 (remote-exec):   Insecure: false
null_resource.InstallonIP2 (remote-exec):   NTLM: false
null_resource.InstallonIP2 (remote-exec):   CACert: false
null_resource.InstallonIP2 (remote-exec): Connected!
#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;
null_resource.InstallonIP2 (remote-exec): C:\Users\Administrator&gt;powershell -File c:/temp/Test-Script.ps1
#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.InstallonIP1: Creation complete after 3s [id=63698556510210360780]

Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
azadmin@tacg-cicd:/etc/terraform-tmm/CVADXS/TestWinRM$ terraform destroy This command deprovisions all objects managed by a Terraform configuration. You can use terraform destroy to conveniently clean up all objects or a specific object created by Terraform.  azadmin@tacg-cicd:/etc/terraform-tmm/CVADXS/TestWinRM$ terraform destroy
null_resource.UploadTestFileToIP1: Refreshing state... [id=38299301982712762563]
null_resource.InstallonIP1: Refreshing state... [id=63698556510210360780]
null_resource.UploadTestFileToIP2: Refreshing state... [id=2420916592744961178]
null_resource.InstallonIP2: Refreshing state... [id=98225693556377100230]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
  - destroy

Terraform will perform the following actions:

  # null_resource.InstallonIP1 will be destroyed
  - resource "null_resource" "InstallonIP1" {
      - id = "63698556510210360780" -&gt; null
    }

  # null_resource.UploadTestFileToIP1 will be destroyed
  - resource "null_resource" "UploadTestFileToIP1" {
      - id = "38299301982712762563" -&gt; null
    }
  
  # null_resource.InstallonIP2 will be destroyed
  - resource "null_resource" "InstallonIP2" {
      - id = "98225693556377100230" -&gt; null
    }

  # null_resource.UploadTestFileToIP2 will be destroyed
  - resource "null_resource" "UploadTestFileToIP2" {
      - id = "2420916592744961178" -&gt; null
    }

Plan: 0 to add, 0 to change, 2 to destroy.

Do you really want to destroy all resources?
  Terraform will destroy all your managed infrastructure, as shown above.
  There is no undo. Only 'yes' will be accepted to confirm.

  Enter a value: yes

null_resource.InstallonIP1: Destroying... [id=63698556510210360780]
null_resource.InstallonIP1: Destruction complete after 0s
null_resource.UploadTestFileToIP1: Destroying... [id=38299301982712762563]
null_resource.UploadTestFileToIP1: Destruction complete after 0s
null_resource.InstallonIP2: Destroying... [id=98225693556377100230]
null_resource.InstallonIP2: Destruction complete after 0s
null_resource.UploadTestFileToIP2: Destroying... [id=2420916592744961178]
null_resource.UploadTestFileToIP2: Destruction complete after 0s

Destroy complete! Resources: 4 destroyed.
azadmin@tacg-cicd:/etc/terraform-tmm/CVADXS/TestWinRM$ Terraform TerminologyA short overview of terms related to Packer: Configuration The set of .tf files (written in HCL) that declare desired infrastructure. These files are the primary interface for all workflows. Provider A plugin that maps Terraform’s model to a real service’s API (AWS, Azure, vSphere, SaaS) Resource A managed object that Terraform creates, updates, or destroys (e.g., citrix_machine_catalog) Data Source Read‑only lookup that fetches external data (from the provider or other backends) to use in your configuration. Nothing is altered. Module A reusable collection of Terraform code/snippets Input Variables (variable) Parameters that make modules configurable Local Variables (locals) Named expressions for reuse inside a module Outputs (output) Prints out a value used by a module State A record of the objects Terraform manages, used to plan changes safely. Holds the representation of the underlying infrastructure. Provisioners Imperative post‑apply steps (run scripts, upload files)  Important It is imperative that communication between the Terraform machine and the Windows Machine using WinRM and/or SSH can be established without error.  Note You can ensure that WinRM is configured correctly by adding the configuration via local or domain-based group policies. It is best practice to configure WinRM when creating the virtual machine (e.g., with Packer).  Terraform StructureTerraform uses HCL as its "programming language". Example with explanations - environment and provider definition: terraform {
  ## Defining Terraform version to be at least 1.9.7
  required_version = "&gt;= 1.9.7"

  ## Defining all needed providers and the respective minimal versions
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "&gt;=4.6.0"
    }

    citrix = {
      source  = "citrix/citrix"
      version = "&gt;=1.0.30"
    }

    azuread = {
      source  = "hashicorp/azuread"
      version = "&gt;=3.0.2"
    }
  }

  ## Backend Configuration for Terraform Remote State on Azure
  ### In the Backend configuration block, no variables can be used, the settings must be hardcoded
  backend "azurerm" {
    resource_group_name  = XxXxXxXxX
    storage_account_name = XxXxXxXxX
    container_name       = XxXxXxXxX
    key                  = XxXxXxXxX
    use_oidc             = true
    client_id            = XxXxXxXxX
    client_secret        = XxXxXxXxX
    subscription_id      = XxXxXxXxX
    tenant_id            = XxXxXxXxX
    snapshot             = true
  }
}

## Configure all Providers
provider "azurerm" {
  features {}
  client_id       = var.azurerm-clientid
  client_secret   = var.azurerm-clientsecret
  tenant_id       = var.azurerm-tenantid
  subscription_id = var.azurerm-subscriptionid
}

provider "citrix" {
  cvad_config = {
    customer_id   = var.cc-customerid
    client_id     = var.cc-apikey-clientId
    client_secret = var.cc-apikey-clientSecret
  }
}

provider "azuread" {
  tenant_id = var.azurerm-tenantid
} Example with explanations - creation of some DaaS entities: ### Set local variables and scripts - in this module no local variables are needed
locals {}

### Get existing entity datan
#### Retrieving the ZoneID
data "citrix_zone" "GetTFAzureZoneID" {
  name = var.CC-Azure-ZoneID
}

#### Retrieving the Image Definition ID
data "citrix_image_definition" "GetImageDefinition" {
  name = var.CC-ImageDefinition-Name
  #name = "Example Image Definition"
} 

#### Retrieving the Image Version ID
data "citrix_image_version" "GetImageDefinitionVersion" {
  version_number   = var.CC-ImageDefinition-Version
  image_definition = data.citrix_image_definition.GetImageDefinition.id
}

##### Write out some information for debugging/validation
output "name" {
  value = data.citrix_image_definition.GetImageDefinition.id
}

output "version" {
  value = data.citrix_image_version.GetImageDefinitionVersion.id
} 

#### Retrieving the Shared Image Gallery
 data "azurerm_shared_image_gallery" "GetAzureSIG" {
  name                = var.CC-Azure-SIG-Name
  resource_group_name = var.TACG-TMM-ResourceGroup-Name
} 

#### Retrieving the Shared Image Definition
data "azurerm_shared_image" "GetAzureSIGDefinition" {
  name                = var.CC-Azure-SIGDefinition-Name
  gallery_name        = data.azurerm_shared_image_gallery.GetAzureSIG.name
  resource_group_name = var.TACG-TMM-ResourceGroup-Name
} 

#### Retrieving the Shared Image Definition Version
data "azurerm_shared_image_version" "GetAzureSIGDefinitionVersion" {
  name                = var.CC-Azure-SIGDefinitionVersion
  image_name          = data.azurerm_shared_image.GetAzureSIGDefinition.name
  gallery_name        = data.azurerm_shared_image_gallery.GetAzureSIG.name
  resource_group_name = var.TACG-TMM-ResourceGroup-Name
} 

#### Creating the Hypervisor Connection
resource "citrix_azure_hypervisor" "CreateAzureHostingConnection" {
  depends_on          = [data.citrix_zone.GetTFAzureZoneID]
  name                = var.CC-Azure-HypConn-Name
  zone                = data.citrix_zone.GetTFAzureZoneID.id
  active_directory_id = var.azurerm-tenantid
  subscription_id     = var.azurerm-subscriptionid
  application_secret  = var.azurerm-clientsecret
  application_id      = var.azurerm-clientid
}

#### Sleep 30s to let Background processes settle
resource "time_sleep" "wait_30_seconds" {
  depends_on      = [citrix_azure_hypervisor.CreateAzureHostingConnection]
  create_duration = "30s"
}

#### Creating the Hypervisor Resource Pool
resource "citrix_azure_hypervisor_resource_pool" "CreateAzureHostingConnectionPool" {
  depends_on                     = [time_sleep.wait_30_seconds]
  name                           = var.CC-Azure-HypConn-Name
  hypervisor                     = citrix_azure_hypervisor.CreateAzureHostingConnection.id
  region                         = var.TACG-TMM-ResourceGroup-Location
  virtual_network_resource_group = var.TACG-TMM-ResourceGroup-Name
  virtual_network                = var.CC-Azure-VNet-Name
  subnets                        = var.CC-Azure-Subnets
}

##### Write out some information for debugging/validation
output "hypname" {
  value = citrix_azure_hypervisor.CreateAzureHostingConnection.id
}

output "hypconnname" {
  value = citrix_azure_hypervisor_resource_pool.CreateAzureHostingConnectionPool.id
}Terraform normally knows the order in which entities will be created. To ensure the order manually, you can use the [depends_on] statement. It forces Terraform to create the respective resource only if the depending resource is created. Example with explanations - definition of variables: ## Definition of Azure Terraform Provider variables - not all available variables are defined, only these we use
### Reference https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs
#### The sensitive keyword means that Terraform dies not print out the value
variable "azurerm-clientid" {
  type        = string
  sensitive   = true
  description = "REQUIRED: Client-ID of the Azure SPN"
}

variable "azurerm-clientsecret" {
  type        = string
  sensitive   = true
  description = "REQUIRED: Client-Secret of the Azure SPN"
}

variable "azurerm-tenantid" {
  type        = string
  sensitive   = true
  description = "REQUIRED: Tenant-ID of the Azure SPN"
}

variable "azurerm-subscriptionid" {
  type        = string
  sensitive   = true
  description = "REQUIRED: Subscription-ID of the Azure subscription used"
} Example with explanations - assignment of values to variables in JSON format: {
...
    "cc-customerid": "XxXxXxX",
    "cc-apikey-clientId": "XxXxXxX",
    "cc-apikey-clientSecret": "XxXxXxX",
    "azurerm-subscriptionid":"XxXxXxX",
    "azurerm-tenantid":"XxXxXxX",
    "azurerm-clientid":"XxXxXxX",
    "azurerm-clientsecret":"XxXxXxX",
    "TAG-Environment":"XxXxXxX",
    "TAG-Environment-Entity":"XxXxXxX",
    "TAG-Environment-Usage":"XxXxXxX",
...
}All in all, Terraform's syntax is relatively straightforward.   Red Hat Ansible Automation PlatformMany organizations that have matured their Infrastructure-as-Code (IaC) practices increasingly require tooling that not only provisions infrastructure but also configures systems, applications, and operating environments in a controlled, repeatable manner. Ansible is a leading configuration management and automation platform that aligns naturally with IaC principles and complements provisioning tools such as Terraform, or cloud-native IaC engines. Its agentless architecture, declarative playbook model, and extensive ecosystem make it a strategic choice for enterprises seeking predictable, scalable, and compliant post-provision configuration. Ansible provides a declarative, idempotent configuration model that ensures systems converge to a desired state regardless of their current configuration. This makes it ideally suited for environments provisioned using IaC tools, allowing organizations to: Configure compute resources, operating systems, middleware, and applications immediately after provisioning Apply consistent baseline templates across development, staging, and production Avoid configuration drift and reduce manual intervention Enforce standardization across diverse platforms and environments Because Ansible uses standard protocols (SSH, WinRM, API calls) and does not require agents, it can manage virtually any resource created by IaC tooling with minimal setup. Ansible excels by enabling: OS configuration (packages, services, permissions, patching) Deployment and maintenance of middleware or applications Secure configuration of agents (monitoring, logging, EDR, secrets managers) Post-provision checks and compliance enforcement Continuous updates and lifecycle operations long after initial provisioning Ansible delivers operational benefits such as: Idempotent reruns that safely correct drift Automated configuration updates Consistent rollout of application versions or patches Reduced human error and faster recovery Centralized configuration logic rather than ad-hoc scripts These capabilities support long-term environment stability, which is often a weak point in IaC deployments if configuration is not managed separately.   Core ConceptsInventoryThe list of managed hosts (static INI/YAML or dynamic inventory from clouds like AWS, Azure, GCP). You can group hosts (e.g., ddc, cc, prod, test). PlaybooksYAML files describing desired states. A play targets host groups and runs a sequence of tasks using modules. ModulesThe building blocks like “package”, “service”, “user”, “copy”, “template”, “win_feature”, cloud/network modules, etc. RolesReusable content bundles (tasks, handlers, templates, defaults, vars, files) with a predictable structure for maintainability. CollectionsNamespaced distributions of roles, modules, plugins. (e.g., “amazon.aws”, “ansible.windows”, “kubernetes.core”). Variables &amp; FactsInput data and discovered system facts (“gather_facts”). Variable precedence is essential for predictability. TemplatesJinja2 templates to render config files dynamically. HandlersTriggered by tasks to perform actions like service restarts, only when changes occur  Key FeaturesAgentless, Human‑Readable Automation: Ansible connects over SSH/WinRM/HTTPS—no agents on managed nodes—reducing operational overhead and security footprint. Automation is expressed in YAML playbooks that describe the desired state and support idempotency, so re‑runs are safe and predictable  Enterprise Control with Automation Controller: A centralized UI/API provides RBAC, credential management, job templates, scheduling, workflows, and auditing—standardizing how automation is deployed, delegated, and measured across teams and environments  Scale-Out Execution with Automation Mesh: An overlay network distributes automation to execution/hop nodes across WANs and multiple sites, supports control–execution plane separation, and offers resilient, bi‑directional, multi‑hop routing (FIPS‑compliant) to meet large‑scale and edge needs  Repeatable Runtimes via Execution Environments: Containerized execution environments (EEs) package the Ansible engine, collections, and dependencies—ensuring that what you test is exactly what runs in production. This improves reliability and portability, and shortens development cycles  Curated Content with Automation Hub: Automation Hub provides certified content collections (modules, roles, plugins, docs) from Red Hat and partners; Private Automation Hub lets you curate, sign, and govern internal content and mirror external sources for disconnected sites  Event‑Driven Ansible (EDA): EDA introduces rulebooks (if/then logic) to subscribe to event sources (e.g., monitoring tools, webhooks, Kafka) and trigger playbooks or actions—enabling near real‑time remediation, compliance enforcement, and closed‑loop operations  Analytics, Insights, and Governance: Automation analytics quantify business impact (e.g., ROI calculators, job success/failure patterns), while Red Hat Insights monitors the platform estate, identifies drift/vulnerabilities, and can feed EDA for proactive remediation. Data collection deliberately excludes secrets and detailed task output  Security &amp; Hardening Guidance: Red Hat provides a hardening guide for AAP on RHEL, including firewall, topology, and DISA STIG considerations; community and official roles exist to automate CIS/STIG baselines on hosts (e.g., RHEL)  Example Use CasesHybrid Cloud Provisioning &amp; Lifecycle: Automate Cloud resource creation, configuration, tagging, security controls, and teardown across AWS, Azure, GCP, and VMware, Nutanix, XenServer, Azure Local using certified/maintained collections.  Use workflows to orchestrate approvals, compliance checks, and change records.   Application Deployment &amp; Day‑2 Operations: Express application delivery as code—playbooks and workflows—to ensure deterministic rollouts across dev/test/prod with idempotent tasks (config files, services, DB migrations), then schedule patching and post‑deployment validation.   Security &amp; Compliance at Scale: Codify baseline hardening and continuous compliance (e.g., DISA STIG/CIS) and use Insights to detect drift or vulnerabilities. Trigger EDA rulebooks to remediate misconfigurations on detection.   Network Automation (Multi‑Vendor): Standardize configuration and state validation across routers, switches, firewalls, and load balancers with Ansible’s network collections and platform‑specific modules; orchestrate backups, firmware, and policy enforcement, and leverage EDA for signal‑based responses.   Installing AnsibleThe installation of Ansible is straightforward. There are many ways to install Ansible– you can find more information about Installation on the Ansible homepage. Note In our case, we use a dedicated Ubuntu-based VM that hosts all automation-related frameworks. You can find more information about creating and installing such a dedicated machine in this guide. Example of installing Ansible on Ubuntu: XxXxXxXxX@tacg-cicd:~$ echo "sysops ALL=(ALL) NOPASSWD:ALL" | sudo tee /etc/sudoers.d/sysops
sysops ALL=(ALL) NOPASSWD:ALL
 
XxXxXxXxX@tacg-cicd:~$ sudo apt update
Hit:1 https://packages.microsoft.com/repos/code stable InRelease
Hit:2 https://esm.ubuntu.com/apps/ubuntu noble-apps-security InRelease
Hit:3 https://esm.ubuntu.com/apps/ubuntu noble-apps-updates InRelease
Hit:4 https://esm.ubuntu.com/infra/ubuntu noble-infra-security InRelease
Hit:5 https://esm.ubuntu.com/infra/ubuntu noble-infra-updates InRelease
Hit:6 http://azure.archive.ubuntu.com/ubuntu noble InRelease
Hit:7 http://azure.archive.ubuntu.com/ubuntu noble-updates InRelease
Hit:8 https://apt.releases.hashicorp.com noble InRelease
Hit:9 http://azure.archive.ubuntu.com/ubuntu noble-backports InRelease
Hit:10 http://azure.archive.ubuntu.com/ubuntu noble-security InRelease
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
All packages are up to date.
XxXxXxXxX@tacg-cicd:~$ sudo apt upgrade -y


Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
Calculating upgrade... Done
#
# Patches available for the local privilege escalation issue in needrestart
# tracked by CVE-2024-48990, CVE-2024-48991, CVE-2024-48992, and CVE-2024-10224
# For more see: https://ubuntu.com/blog/needrestart-local-privilege-escalation
#
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
XxXxXxXxX@tacg-cicd:~$ sudo apt install -y software-properties-common
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
software-properties-common is already the newest version (0.99.49.1).
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
XxXxXxXxX@tacg-cicd:~$ sudo add-apt-repository --yes --update ppa:ansible/ansible
Repository: 'Types: deb
URIs: https://ppa.launchpadcontent.net/ansible/ansible/ubuntu/
Suites: noble
Components: main
'
Description:
Ansible is a radically simple IT automation platform that makes your applications and systems easier to deploy. Avoid writing scripts or custom code to deploy and update your applications— automate in a language that approaches plain English, using SSH, with no agents to install on remote systems.

http://ansible.com/

If you face any issues while installing Ansible PPA, file an issue here:
https://github.com/ansible-community/ppa/issues
More info: https://launchpad.net/~ansible/+archive/ubuntu/ansible
Adding repository.
Hit:1 https://packages.microsoft.com/repos/code stable InRelease
Hit:2 https://esm.ubuntu.com/apps/ubuntu noble-apps-security InRelease
Hit:3 https://esm.ubuntu.com/apps/ubuntu noble-apps-updates InRelease
Get:4 https://ppa.launchpadcontent.net/ansible/ansible/ubuntu noble InRelease [17.8 kB]
Hit:5 https://esm.ubuntu.com/infra/ubuntu noble-infra-security InRelease
Hit:6 https://esm.ubuntu.com/infra/ubuntu noble-infra-updates InRelease
Hit:7 http://azure.archive.ubuntu.com/ubuntu noble InRelease
Get:8 https://ppa.launchpadcontent.net/ansible/ansible/ubuntu noble/main amd64 Packages [772 B]
Get:9 https://ppa.launchpadcontent.net/ansible/ansible/ubuntu noble/main Translation-en [472 B]
Hit:10 http://azure.archive.ubuntu.com/ubuntu noble-updates InRelease
Hit:11 https://apt.releases.hashicorp.com noble InRelease
Hit:12 http://azure.archive.ubuntu.com/ubuntu noble-backports InRelease
Hit:13 http://azure.archive.ubuntu.com/ubuntu noble-security InRelease
Fetched 19.0 kB in 1s (18.7 kB/s)
Reading package lists... Done
XxXxXxXxX@tacg-cicd:~$ sudo apt update
Hit:1 https://esm.ubuntu.com/apps/ubuntu noble-apps-security InRelease
Hit:2 https://esm.ubuntu.com/apps/ubuntu noble-apps-updates InRelease
Hit:3 https://esm.ubuntu.com/infra/ubuntu noble-infra-security InRelease
Hit:4 https://esm.ubuntu.com/infra/ubuntu noble-infra-updates InRelease
Hit:5 https://packages.microsoft.com/repos/code stable InRelease
Hit:6 http://azure.archive.ubuntu.com/ubuntu noble InRelease
Hit:7 http://azure.archive.ubuntu.com/ubuntu noble-updates InRelease
Hit:8 https://apt.releases.hashicorp.com noble InRelease
Hit:9 http://azure.archive.ubuntu.com/ubuntu noble-backports InRelease
Hit:10 http://azure.archive.ubuntu.com/ubuntu noble-security InRelease
Hit:11 https://ppa.launchpadcontent.net/ansible/ansible/ubuntu noble InRelease
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
All packages are up to date.
XxXxXxXxX@tacg-cicd:~$ sudo apt install -y ansible
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following additional packages will be installed:
  ansible-core python3-kerberos python3-ntlm-auth python3-requests-ntlm python3-resolvelib python3-winrm
  python3-xmltodict sshpass
The following NEW packages will be installed:
  ansible ansible-core python3-kerberos python3-ntlm-auth python3-requests-ntlm python3-resolvelib python3-winrm
  python3-xmltodict sshpass
0 upgraded, 9 newly installed, 0 to remove and 0 not upgraded.
Need to get 19.0 MB of archives.
After this operation, 212 MB of additional disk space will be used.
Get:1 https://ppa.launchpadcontent.net/ansible/ansible/ubuntu noble/main amd64 ansible-core all 2.17.7-1ppa~noble [1015 kB]
Get:2 http://azure.archive.ubuntu.com/ubuntu noble/universe amd64 python3-resolvelib all 1.0.1-1 [25.7 kB]
Get:3 https://ppa.launchpadcontent.net/ansible/ansible/ubuntu noble/main amd64 ansible all 10.7.0-1ppa~noble [17.8 MB]
Get:4 http://azure.archive.ubuntu.com/ubuntu noble/universe amd64 python3-kerberos amd64 1.1.14-3.1build9 [21.2 kB]
Get:5 http://azure.archive.ubuntu.com/ubuntu noble/universe amd64 python3-ntlm-auth all 1.5.0-1 [21.3 kB]
Get:6 http://azure.archive.ubuntu.com/ubuntu noble/universe amd64 python3-requests-ntlm all 1.1.0-3 [6308 B]
Get:7 http://azure.archive.ubuntu.com/ubuntu noble/main amd64 python3-xmltodict all 0.13.0-1 [13.4 kB]
Get:8 http://azure.archive.ubuntu.com/ubuntu noble/universe amd64 python3-winrm all 0.4.3-2 [31.9 kB]
Get:9 http://azure.archive.ubuntu.com/ubuntu noble/universe amd64 sshpass amd64 1.09-1 [11.7 kB]
Fetched 19.0 MB in 3s (6995 kB/s)
Selecting previously unselected package python3-resolvelib.
(Reading database ... 179940 files and directories currently installed.)
Preparing to unpack .../0-python3-resolvelib_1.0.1-1_all.deb ...
Unpacking python3-resolvelib (1.0.1-1) ...
Selecting previously unselected package ansible-core.
Preparing to unpack .../1-ansible-core_2.17.7-1ppa~noble_all.deb ...
Unpacking ansible-core (2.17.7-1ppa~noble) ...
Selecting previously unselected package ansible.
Preparing to unpack .../2-ansible_10.7.0-1ppa~noble_all.deb ...
Unpacking ansible (10.7.0-1ppa~noble) ...
Selecting previously unselected package python3-kerberos.
Preparing to unpack .../3-python3-kerberos_1.1.14-3.1build9_amd64.deb ...
Unpacking python3-kerberos (1.1.14-3.1build9) ...
Selecting previously unselected package python3-ntlm-auth.
Preparing to unpack .../4-python3-ntlm-auth_1.5.0-1_all.deb ...
Unpacking python3-ntlm-auth (1.5.0-1) ...
Selecting previously unselected package python3-requests-ntlm.
Preparing to unpack .../5-python3-requests-ntlm_1.1.0-3_all.deb ...
Unpacking python3-requests-ntlm (1.1.0-3) ...
Selecting previously unselected package python3-xmltodict.
Preparing to unpack .../6-python3-xmltodict_0.13.0-1_all.deb ...
Unpacking python3-xmltodict (0.13.0-1) ...
Selecting previously unselected package python3-winrm.
Preparing to unpack .../7-python3-winrm_0.4.3-2_all.deb ...
Unpacking python3-winrm (0.4.3-2) ...
Selecting previously unselected package sshpass.
Preparing to unpack .../8-sshpass_1.09-1_amd64.deb ...
Unpacking sshpass (1.09-1) ...
Setting up python3-ntlm-auth (1.5.0-1) ...
Setting up python3-resolvelib (1.0.1-1) ...
Setting up python3-kerberos (1.1.14-3.1build9) ...
Setting up ansible-core (2.17.7-1ppa~noble) ...
Setting up sshpass (1.09-1) ...
Setting up python3-xmltodict (0.13.0-1) ...
Setting up ansible (10.7.0-1ppa~noble) ...
Setting up python3-requests-ntlm (1.1.0-3) ...
Setting up python3-winrm (0.4.3-2) ...
Processing triggers for man-db (2.12.0-4build2) ...
Scanning processes...
Scanning linux images...

Running kernel seems to be up-to-date.

No services need to be restarted.

No containers need to be restarted.

No user sessions are running outdated binaries.

No VM guests are running outdated hypervisor (qemu) binaries on this host.
XxXxXxXxX@tacg-cicd:~$ ansible --version
ansible [core 2.17.7]
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/home/tmm-XxXxXxXxX/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3/dist-packages/ansible
  ansible collection location = /home/tmm-XxXxXxXxX/.ansible/collections:/usr/share/ansible/collections
  executable location = /usr/bin/ansible
  python version = 3.12.3 (main, Nov  6 2024, 18:32:19) [GCC 13.2.0] (/usr/bin/python3)
  jinja version = 3.1.2
  libyaml = True
XxXxXxXxX@tacg-cicd:~$Ansible was successfully installed, and the correct version is reported.  We recommend installing important Ansible libraries based on your needs – for example ####Run on all hosts:
XxXxXxXxX@tacg-cicd:~$ ansible-galaxy collection install azure.azcollection --force

Starting galaxy collection install process
Process install dependency map
Starting collection install process
Downloading https://galaxy.ansible.com/api/v3/plugin/ansible/content/published/collections/artifacts/azure-azcollection-3.3.1.tar.gz to /home/XxXxXxXxX/.ansible/tmp/ansible-local-4576vflvb_gi/tmpu0orj7n3/azure-azcollection-3.3.1-wid_5_qj
Installing 'azure.azcollection:3.3.1' to '/home/XxXxXxXxX/.ansible/collections/ansible_collections/azure/azcollection'
azure.azcollection:3.3.1 was installed successfully
XxXxXxXxX@tacg-cicd:~$  Ansible CommandsAnsible is controlled using its command-line interface (CLI). Calling Ansible without any subcommand shows all currently implemented commands: XxXxXxXxX@tacg-cicd:~$ ansible 
usage: ansible [-h] [--version] [-v] [-b] [--become-method BECOME_METHOD] [--become-user BECOME_USER]
               [-K | --become-password-file BECOME_PASSWORD_FILE] [-i INVENTORY] [--list-hosts] [-l SUBSET]
               [-P POLL_INTERVAL] [-B SECONDS] [-o] [-t TREE] [--private-key PRIVATE_KEY_FILE] [-u REMOTE_USER]
               [-c CONNECTION] [-T TIMEOUT] [--ssh-common-args SSH_COMMON_ARGS] [--sftp-extra-args SFTP_EXTRA_ARGS]
               [--scp-extra-args SCP_EXTRA_ARGS] [--ssh-extra-args SSH_EXTRA_ARGS]
               [-k | --connection-password-file CONNECTION_PASSWORD_FILE] [-C] [-D] [-e EXTRA_VARS]
               [--vault-id VAULT_IDS] [-J | --vault-password-file VAULT_PASSWORD_FILES] [-f FORKS] [-M MODULE_PATH]
               [--playbook-dir BASEDIR] [--task-timeout TASK_TIMEOUT] [-a MODULE_ARGS] [-m MODULE_NAME]
               pattern

Define and run a single task 'playbook' against a set of hosts

positional arguments:
  pattern               host pattern

options:
  --become-password-file BECOME_PASSWORD_FILE, --become-pass-file BECOME_PASSWORD_FILE
                        Become password file
  --connection-password-file CONNECTION_PASSWORD_FILE, --conn-pass-file CONNECTION_PASSWORD_FILE
                        Connection password file
  --list-hosts          outputs a list of matching hosts; does not execute anything else
  --playbook-dir BASEDIR
                        Since this tool does not use playbooks, use this as a substitute playbook directory. This sets
                        the relative path for many features including roles/ group_vars/ etc.
  --task-timeout TASK_TIMEOUT
                        set task timeout limit in seconds, must be positive integer.
  --vault-id VAULT_IDS  the vault identity to use. This argument may be specified multiple times.
  --vault-password-file VAULT_PASSWORD_FILES, --vault-pass-file VAULT_PASSWORD_FILES
                        vault password file
  --version             show program's version number, config file location, configured module search path, module
                        location, executable location and exit
  -B SECONDS, --background SECONDS
                        run asynchronously, failing after X seconds (default=N/A)
  -C, --check           don't make any changes; instead, try to predict some of the changes that may occur
  -D, --diff            when changing (small) files and templates, show the differences in those files; works great
                        with --check
  -J, --ask-vault-password, --ask-vault-pass
                        ask for vault password
  -K, --ask-become-pass
                        ask for privilege escalation password
  -M MODULE_PATH, --module-path MODULE_PATH
                        prepend colon-separated path(s) to module library (default={{ ANSIBLE_HOME ~
                        "/plugins/modules:/usr/share/ansible/plugins/modules" }}). This argument may be specified
                        multiple times.
  -P POLL_INTERVAL, --poll POLL_INTERVAL
                        set the poll interval if using -B (default=15)
  -a MODULE_ARGS, --args MODULE_ARGS
                        The action's options in space separated k=v format: -a 'opt1=val1 opt2=val2' or a json string:
                        -a '{"opt1": "val1", "opt2": "val2"}'
  -e EXTRA_VARS, --extra-vars EXTRA_VARS
                        set additional variables as key=value or YAML/JSON, if filename prepend with @. This argument
                        may be specified multiple times.
  -f FORKS, --forks FORKS
                        specify number of parallel processes to use (default=5)
  -h, --help            show this help message and exit
  -i INVENTORY, --inventory INVENTORY, --inventory-file INVENTORY
                        specify inventory host path or comma separated host list. --inventory-file is deprecated. This
                        argument may be specified multiple times.
  -k, --ask-pass        ask for connection password
  -l SUBSET, --limit SUBSET
                        further limit selected hosts to an additional pattern
  -m MODULE_NAME, --module-name MODULE_NAME
                        Name of the action to execute (default=command)
  -o, --one-line        condense output
  -t TREE, --tree TREE  log output to this directory
  -v, --verbose         Causes Ansible to print more debug messages. Adding multiple -v will increase the verbosity,
                        the builtin plugins currently evaluate up to -vvvvvv. A reasonable level to start is -vvv,
                        connection debugging might require -vvvv. This argument may be specified multiple times.
A detailed description of all available commands can be found on Ansible´s homepage.  Better togetherTerraform, Packer, and Ansible serve complementary roles in modern Infrastructure-as-Code (IaC) architectures. Using Terraform, Ansible, and Packer together creates a powerful, cohesive, and maintainable workflow for provisioning, configuring, and managing modern infrastructure. Each tool has a specific purpose, and together they form a clean pipeline from image creation to infrastructure deployment to configuration management. Terraform excels at provisioning infrastructure resources—compute, networks, storage, identity, and cloud services—while Ansible specializes in configuring and managing the software, applications, and operating system state on those resources once they are created. When combined, they form a robust, scalable, and fully automated delivery pipeline that allows organizations to provision secure, consistent environments and configure them in a modular, policy-driven manner. Terraform is primarily designed for infrastructure lifecycle management, while Ansible is engineered for post-provisioning configuration. This clean separation ensures: Terraform builds and destroys infrastructure predictably Ansible configures servers, middleware, application stacks, and OS settings Changes to infrastructure and configuration are maintained in distinct codebases, improving clarity and reducing risk IT can version, audit, and test both layers independently This division of responsibilities aligns with architectural patterns used in platform engineering and regulated environments, where infrastructure and system configuration often have separate compliance requirements. Example schematic workflow:  Terraform builds your infrastructure, Packer builds your images, and Ansible configures your systems—together they give you a clean, scalable, and reliable IaC pipeline that reduces drift, speeds deployments, and ensures consistent environments.  Overview of CI/CD MethodologiesInstallation and Usage of JenkinsJenkins is one of the most popular free open-source automation servers used to automate parts of the software development process, especially building, testing, and deploying code. It’s widely used for Continuous Integration (CI) and Continuous Delivery/Deployment (CD). Key FeaturesExtensible:  Over 1,800 plugins for integration with tools like Git, Docker, Kubernetes, etc.  Distributed Builds:  Supports master-agent architecture for scaling builds across multiple machines  Pipeline as Code:  Jenkins Pipelines (written in Groovy) allow build workflows to be defined in code  Web Interface:  Easy-to-use UI for managing jobs, builds, and configurations  Community Support: Large and active community with frequent updates Importance of Jenkins in CI/CDJenkins plays a critical role in CI/CD pipelines by automating the entire software delivery lifecycle. It enables teams to: Automatically build Master Images Deploy environments to staging or production environments Run automated tests to ensure quality Monitor pipeline status and receive notifications Integrate infrastructure provisioning and configuration Core ConceptsAutomation of Repetitive Tasks:  Automates testing, packaging, and deployment.  Reduces manual errors and speeds up deployment cycles  Scalability:  Can be distributed across multiple nodes to handle large-scale builds  Flexibility:  Supports multiple languages and platforms. Easily integrates with version control systems (Git), and deployment platforms  Pipeline as Code:  Enables version-controlled, reproducible build and deployment processes  Installing JenkinsThe installation of Ansible is straightforward. There are many ways to install Jenkins– you can find more information about Installation on the Jenkins homepage. Note In our case, we use a dedicated Ubuntu-based VM that hosts all automation-related frameworks. You can find more information about creating and installing such a dedicated machine in this guide. Example of installing Jenkins on Ubuntu: XxXxXxXxX@tacg-cicd:~$ sudo apt update
Get:1 https://apt.releases.hashicorp.com noble InRelease [12.9 kB]
...
Get:25 http://at.archive.ubuntu.com/ubuntu noble-backports/multiverse amd64 Components [212 B]
Fetched 3,961 kB in 1s (2,937 kB/s)
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
158 packages can be upgraded. Run 'apt list --upgradable' to see them.

XxXxXxXxX@tacg-cicd:~$ sudo apt upgrade
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
Calculating upgrade... Done
...
Fetched 164 MB in 26s (6,412 kB/s)
Extracting templates from packages: 100%
Preconfiguring packages ...
...
XxXxXxXxX@tacg-cicd:~$ sudo apt install openjdk-17-jdk
[sudo] password for XxXxXxXxX:
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following package was automatically installed and is no longer required:
  net-tools
Use 'sudo apt autoremove' to remove it.
The following additional packages will be installed:
  libice-dev libpthread-stubs0-dev libsm-dev libx11-dev libxau-dev libxcb1-dev libxdmcp-dev libxt-dev
  openjdk-17-jdk-headless openjdk-17-jre openjdk-17-jre-headless x11proto-dev xorg-sgml-doctools xtrans-dev
Suggested packages:
  libice-doc libsm-doc libx11-doc libxcb-doc libxt-doc openjdk-17-demo openjdk-17-source visualvm fonts-ipafont-gothic
  fonts-ipafont-mincho fonts-wqy-microhei | fonts-wqy-zenhei fonts-indic
The following NEW packages will be installed:
  libice-dev libpthread-stubs0-dev libsm-dev libx11-dev libxau-dev libxcb1-dev libxdmcp-dev libxt-dev openjdk-17-jdk
  openjdk-17-jdk-headless openjdk-17-jre openjdk-17-jre-headless x11proto-dev xorg-sgml-doctools xtrans-dev
0 upgraded, 15 newly installed, 0 to remove and 8 not upgraded.
Need to get 123 MB of archives.
After this operation, 281 MB of additional disk space will be used.
Do you want to continue? [Y/n] Y
...
Setting up openjdk-17-jdk:amd64 (17.0.16+8~us1-0ubuntu1~24.04.1) ...
update-alternatives: using /usr/lib/jvm/java-17-openjdk-amd64/bin/jconsole to provide /usr/bin/jconsole (jconsole) in auto mode
XxXxXxXxX@tacg-cicd:~$
XxXxXxXxX@tacg-cicd:~$ java --version
openjdk 21.0.8 2025-07-15
OpenJDK Runtime Environment (build 21.0.8+9-Ubuntu-0ubuntu124.04.1)
OpenJDK 64-Bit Server VM (build 21.0.8+9-Ubuntu-0ubuntu124.04.1, mixed mode, sharing)
XxXxXxXxX@tacg-cicd:~$
XxXxXxXxX@tacg-cicd:~$ sudo wget -O /usr/share/keyrings/jenkins-keyring.asc https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key
--2025-09-04 13:17:23--  https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key
Resolving pkg.jenkins.io (pkg.jenkins.io)... 199.232.18.133, 2a04:4e42:41::645
Connecting to pkg.jenkins.io (pkg.jenkins.io)|199.232.18.133|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 3175 (3.1K) [application/pgp-keys]
Saving to: ‘/usr/share/keyrings/jenkins-keyring.asc’

/usr/share/keyrings/jenkins-k 100%[=================================================&gt;]   3.10K  --.-KB/s    in 0s

2025-09-04 13:17:24 (56.7 MB/s) - ‘/usr/share/keyrings/jenkins-keyring.asc’ saved [3175/3175]

XxXxXxXxX@tacg-cicd:~$
XxXxXxXxX@tacg-cicd:~$ sudo sh -c 'echo deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc] https://pkg.jenkins.io/debian binary/ &gt; /etc/apt/sources.list.d/jenkins.list'
XxXxXxXxX@tacg-cicd:~$
XxXxXxXxX@tacg-cicd:~$ sudo apt update
Hit:1 http://at.archive.ubuntu.com/ubuntu noble InRelease
Hit:2 http://at.archive.ubuntu.com/ubuntu noble-updates InRelease
Hit:3 http://at.archive.ubuntu.com/ubuntu noble-backports InRelease
Hit:4 http://security.ubuntu.com/ubuntu noble-security InRelease
Ign:5 https://pkg.jenkins.io/debian binary/ InRelease
Hit:6 https://pkg.jenkins.io/debian binary/ Release
Hit:7 https://apt.releases.hashicorp.com noble InRelease
Hit:9 https://ppa.launchpadcontent.net/ansible/ansible/ubuntu noble InRelease
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
8 packages can be upgraded. Run 'apt list --upgradable' to see them.
XxXxXxXxX@tacg-cicd:~$
XxXxXxXxX@tacg-cicd:~$ sudo apt install jenkins
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following NEW packages will be installed:
  jenkins
0 upgraded, 1 newly installed, 0 to remove and 8 not upgraded.
Need to get 95.0 MB of archives.
After this operation, 95.5 MB of additional disk space will be used.
Get:1 https://pkg.jenkins.io/debian binary/ jenkins 2.526 [95.0 MB]
Fetched 95.0 MB in 26s (3,584 kB/s)
Selecting previously unselected package jenkins.
(Reading database ... 225680 files and directories currently installed.)
Preparing to unpack .../archives/jenkins_2.526_all.deb ...
Unpacking jenkins (2.526) ...
Setting up jenkins (2.526) ...
Created symlink /etc/systemd/system/multi-user.target.wants/jenkins.service → /usr/lib/systemd/system/jenkins.service.
XxXxXxXxX@tacg-cicd:~$
XxXxXxXxX@tacg-cicd:~$ sudo systemctl start jenkins
XxXxXxXxX@tacg-cicd:~$ sudo systemctl enable jenkins
Synchronizing state of jenkins.service with SysV service script with /usr/lib/systemd/systemd-sysv-install.
Executing: /usr/lib/systemd/systemd-sysv-install enable jenkins
XxXxXxXxX@tacg-cicd:~$ sudo systemctl status jenkins
● jenkins.service - Jenkins Continuous Integration Server
     Loaded: loaded (/usr/lib/systemd/system/jenkins.service; enabled; preset: enabled)
     Active: active (running) since Thu 2025-09-04 13:19:19 CEST; 45s ago
   Main PID: 1189395 (java)
      Tasks: 48 (limit: 9403)
     Memory: 706.7M (peak: 721.0M)
        CPU: 11.417s
     CGroup: /system.slice/jenkins.service
             └─1189395 /usr/bin/java -Djava.awt.headless=true -jar /usr/share/java/jenkins.war --webroot=/var/cache/jen&gt;

Sep 04 13:19:16 tacg-cicd jenkins[1189395]: f8a1bf5689a142ef90fbe1fc2ba17856
Sep 04 13:19:16 tacg-cicd jenkins[1189395]: This may also be found at: /var/lib/jenkins/secrets/initialAdminPassword
Sep 04 13:19:16 tacg-cicd jenkins[1189395]: *************************************************************
Sep 04 13:19:16 tacg-cicd jenkins[1189395]: *************************************************************
Sep 04 13:19:16 tacg-cicd jenkins[1189395]: *************************************************************
Sep 04 13:19:19 tacg-cicd jenkins[1189395]: 2025-09-04 11:19:19.375+0000 [id=40]        INFO        jenkins.InitReactor&gt;
Sep 04 13:19:19 tacg-cicd jenkins[1189395]: 2025-09-04 11:19:19.382+0000 [id=30]        INFO        hudson.lifecycle.Li&gt;
Sep 04 13:19:19 tacg-cicd systemd[1]: Started jenkins.service - Jenkins Continuous Integration Server.
Sep 04 13:19:20 tacg-cicd jenkins[1189395]: 2025-09-04 11:19:20.200+0000 [id=56]        INFO        h.m.DownloadService&gt;
Sep 04 13:19:20 tacg-cicd jenkins[1189395]: 2025-09-04 11:19:20.200+0000 [id=56]        INFO        hudson.util.Retrier&gt;
lines 1-20/20 (END)
XxXxXxXxX@tacg-cicd:~$  Usage of JenkinsIn our environments, we use Jenkins not for software building, testing, and deployment. Jenkins can orchestrate a full infrastructure and application deployment pipeline using Packer, Terraform, and Ansible. This setup is ideal for teams practicing Infrastructure as Code (IaC) and DevOps automation. The pipeline consists of three major stages, each triggered by Jenkins: Image Creation with Packer Infrastructure Provisioning with Terraform Configuration Management with Ansible Each stage is defined in a Jenkins Pipeline (Groovy syntax), allowing full automation and visibility.   Stage 1: Build Golden Images with PackerPurpose: Create immutable machine images (e.g., AWS AMIs, Azure VM images) with pre-installed base software. Jenkins Integration: Jenkins triggers a Packer build using a shell step or plugin. Packer reads a JSON or HCL template to build the image. Output: Image ID (e.g., AMI ID) stored for use in Terraform. Example Jenkinsfile Snippet: stage('Build Image with Packer') {
    steps {
        sh 'packer build -var "build_version=${BUILD_NUMBER}" packer/template.json'
    }
} Stage 2: Provision Infrastructure with TerraformPurpose: Deploy infrastructure (VMs, networks, load balancers, etc.) using the image created by Packer. Jenkins Integration: Jenkins uses the Terraform CLI or plugin. Terraform reads .tf files and uses the image ID from Packer. Jenkins stores Terraform state securely (e.g., in remote backend). Example Jenkinsfile Snippet: stage('Deploy Infrastructure with Terraform') {
    steps {
        sh '''
            cd terraform/
            terraform init
            terraform apply -auto-approve -var "image_id=${packer_image_id}"
        '''
    }
} Stage 3: Configure Software with AnsiblePurpose: Install and configure applications, services, and settings on provisioned infrastructure. Jenkins Integration: Jenkins runs Ansible playbooks via shell or plugin. Uses dynamic inventory (e.g., Terraform output). Ensures idempotent configuration. Example Jenkinsfile Snippet: stage('Configure with Ansible') {
    steps {
        sh 'ansible-playbook -i inventory/hosts.ini ansible/site.yml'
    }
} Full Pipeline FlowCommit -&gt; triggers Jenkins Jenkins runs Packer -&gt;builds an image Jenkins runs Terraform -&gt; provisions infra using builtimage Jenkins runs Ansible -&gt; configures infra Notifications -&gt; Slack/Teams/email on success/failure Benefits of This Approach Immutable Infrastructure:  Ensures consistency across environments  Full Automation:  Reduces manual effort and risk  Scalability:  Easily extendable to multi-cloud or hybrid setups  Auditability:  Every step is logged and version-controlled Complete Jenkinsfile Snippet: pipeline {
    agent any
    environment {
        PACKER_TEMPLATE = 'packer/template.json'
        TERRAFORM_DIR = 'terraform'
        ANSIBLE_PLAYBOOK = 'ansible/site.yml'
        IMAGE_ID_FILE = 'packer_image_id.txt'
        NOTIFY_EMAIL = 'devops-team@example.com'
    }
    stages {
        stage('Build Image with Packer') {
            steps {
                script {
                    echo "Starting Packer build..."
                    sh """
                        packer build -var 'build_version=${BUILD_NUMBER}' ${PACKER_TEMPLATE} | tee output.log
                        grep 'AMI:' output.log | awk '{print \$2}' &gt; ${IMAGE_ID_FILE}
                    """
                }
            }
        }
        stage('Deploy Infrastructure with Terraform') {
            steps {
                script {
                    def imageId = readFile(IMAGE_ID_FILE).trim()
                    echo "Using Image ID: ${imageId}"
                    dir("${TERRAFORM_DIR}") {
                        sh """
                            terraform init
                            terraform apply -auto-approve -var 'image_id=${imageId}'
                        """
                    }
                }
            }
        }
        stage('Configure with Ansible') {
            steps {
                script {
                    echo "Running Ansible playbook..."
                    sh "ansible-playbook -i inventory/hosts.ini ${ANSIBLE_PLAYBOOK}"
                }
            }
        }
    }
    post {
        success {
            echo 'Pipeline completed successfully.'
            mail to: "${NOTIFY_EMAIL}",
                 subject: "Jenkins Pipeline Success - Build #${BUILD_NUMBER}",
                 body: "The CI/CD pipeline completed successfully."
        }
        failure {
            echo 'Pipeline failed.'
            mail to: "${NOTIFY_EMAIL}",
                 subject: "Jenkins Pipeline Failure - Build #${BUILD_NUMBER}",
                 body: "The CI/CD pipeline failed. Please check the Jenkins logs."
        }
    }
} Installation and Usage of GitHub ActionsGitHub Actions is a CI/CD (Continuous Integration and Continuous Deployment) platform built into GitHub. It allows you to automate workflows for building, testing, and deploying code directly from your GitHub repository.  The Actions are defined in YAML files, allowing them to be triggered by GitHub events such as Pull Requests.   Key FeaturesWorkflow Automation:  A workflow is a set of automated steps defined in a YAML file inside .github/workflows/. It enables you to automate repetitive tasks, such as running tests, building artifacts, or deploying applications, whenever code changes occur  Event-Driven Execution:   Based on the Trigger types, you can tailor workflows to run only when needed, saving time and resources push_request , pull_request  → e.g., detect which stacks changed, then run the plan only for those stacks schedule → Cron-like scheduled runs, for e.g., nightly drift detection workflow_dispatch → e.g, manual trigger apply/destroy flows  Core ConceptsWorkflow:  A YAML file defining automation steps  Job:  A set of steps that run on the same runner  Step:  A single task, e.g., running a script, uses an action  Runner:  A server that executes the workflow jobs  Action:  A reusable unit of code (e.g., actions/checkout)  Trigger:  An event that starts the workflow (e.g., push, schedule)  Installing GitHub ActionsGitHub Actions is natively integrated into GitHub, so there’s no installation required.   Example: Create a workflow file in your repository: Location: .github/workflows/ Format: YAML (.yml or .yaml) name: Build master image (Packer/Azure) and provision with Terraform

on:
  workflow_dispatch:
    inputs:
      location:
        description: 'Azure location (e.g., westeurope)'
        required: true
        default: 'westeurope'
      apply:
        description: 'Apply Terraform? (false = plan only)'
        type: boolean
        required: true
        default: false
  push:
    branches: [ "main" ]
    paths:
      - 'packer/**'
      - 'terraform/**'
      - '.github/workflows/packer-terraform-azure.yml'

concurrency:
  group: packer-terraform-azure-${{ github.ref }}
  cancel-in-progress: true

permissions:
  id-token: write   # required for OIDC to Azure
  contents: read

env:
  AZURE_LOCATION: ${{ github.event.inputs.location || 'westeurope' }}
  TF_IN_AUTOMATION: true
  TF_INPUT: false

jobs:
  packer:
    name: Build master image with Packer (Azure)
    runs-on: ubuntu-latest
    outputs:
      image_id: ${{ steps.capture.outputs.image_id }}

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      # --- AZURE AUTH VIA OIDC ---
      - name: Azure login (OIDC)
        uses: azure/login@v2
        with:
          client-id: ${{ secrets.AZURE_CLIENT_ID }}
          tenant-id: ${{ secrets.AZURE_TENANT_ID }}
          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

      - name: Export Azure creds for Hashi tools (OIDC)
        run: |
          echo "ARM_USE_OIDC=true" &gt;&gt; $GITHUB_ENV
          echo "ARM_CLIENT_ID=${{ secrets.AZURE_CLIENT_ID }}" &gt;&gt; $GITHUB_ENV
          echo "ARM_TENANT_ID=${{ secrets.AZURE_TENANT_ID }}" &gt;&gt; $GITHUB_ENV
          echo "ARM_SUBSCRIPTION_ID=${{ secrets.AZURE_SUBSCRIPTION_ID }}" &gt;&gt; $GITHUB_ENV
          echo "AZURE_LOCATION=${{ env.AZURE_LOCATION }}" &gt;&gt; $GITHUB_ENV

      - name: Set up Packer
        uses: hashicorp/setup-packer@v3

      - name: Packer init
        run: packer init ./packer

      - name: Packer validate
        run: packer validate ./packer

      - name: Packer build (machine-readable)
        run: |
          mkdir -p artifacts
          # Adjust the path if your primary template is e.g., packer/azure.pkr.hcl
          packer build -color=false -machine-readable ./packer | tee artifacts/packer-build.log

      - name: Capture image ID from build output (Azure)
        id: capture
        shell: bash
        run: |
          set -euo pipefail
          LOG="artifacts/packer-build.log"
          IMAGE_ID=""

          # Prefer Azure Compute Gallery (SIG) version ID
          if grep -qE '/subscriptions/.*/resourceGroups/.*/providers/Microsoft\.Compute/galleries/.*/images/.*/versions/[0-9.]+' "$LOG"; then
            IMAGE_ID=$(grep -oE '/subscriptions/.*/resourceGroups/.*/providers/Microsoft\.Compute/galleries/.*/images/.*/versions/[0-9.]+' "$LOG" | head -n1)
          elif grep -qE '/subscriptions/.*/resourceGroups/.*/providers/Microsoft\.Compute/images/[^ ]+' "$LOG"; then
            # Managed Image ID
            IMAGE_ID=$(grep -oE '/subscriptions/.*/resourceGroups/.*/providers/Microsoft\.Compute/images/[^ ]+' "$LOG" | head -n1)
          fi

          # Fallback to manifest.json if you configured the Packer manifest post-processor
          if [ -z "${IMAGE_ID}" ] &amp;&amp; [ -f "packer/manifest.json" ]; then
            sudo apt-get update -y &amp;&amp; sudo apt-get install -y jq
            # .builds[-1].artifact_id may look like "azure-arm:/subscriptions/.../images/..." or SIG version id
            IMAGE_ID=$(jq -r '.builds[-1].artifact_id // empty' packer/manifest.json | sed -E 's#.*:##')
          fi

          if [ -z "${IMAGE_ID}" ]; then
            echo "Could not detect image ID in Packer output." &gt;&amp;2
            exit 1
          fi

          echo "Detected image: ${IMAGE_ID}"
          echo "image_id=${IMAGE_ID}" &gt;&gt; "$GITHUB_OUTPUT"

      - name: Upload Packer artifacts
        uses: actions/upload-artifact@v4
        with:
          name: packer-artifacts
          path: |
            artifacts/packer-build.log
            packer/manifest.json
          if-no-files-found: ignore

  terraform:
    name: Terraform plan/apply (Azure)
    needs: packer
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      # --- AZURE AUTH VIA OIDC ---
      - name: Azure login (OIDC)
        uses: azure/login@v2
        with:
          client-id: ${{ secrets.AZURE_CLIENT_ID }}
          tenant-id: ${{ secrets.AZURE_TENANT_ID }}
          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

      - name: Export Azure creds for Terraform (OIDC)
        run: |
          echo "ARM_USE_OIDC=true" &gt;&gt; $GITHUB_ENV
          echo "ARM_CLIENT_ID=${{ secrets.AZURE_CLIENT_ID }}" &gt;&gt; $GITHUB_ENV
          echo "ARM_TENANT_ID=${{ secrets.AZURE_TENANT_ID }}" &gt;&gt; $GITHUB_ENV
          echo "ARM_SUBSCRIPTION_ID=${{ secrets.AZURE_SUBSCRIPTION_ID }}" &gt;&gt; $GITHUB_ENV

      - name: Set up Terraform
        uses: hashicorp/setup-terraform@v3
        with:
          terraform_wrapper: false

      - name: Terraform fmt
        working-directory: ./terraform
        run: terraform fmt -check -recursive

      - name: Terraform init
        working-directory: ./terraform
        run: terraform init -input=false

      - name: Terraform validate
        working-directory: ./terraform
        run: terraform validate

      - name: Terraform plan
        id: plan
        working-directory: ./terraform
        env:
          TF_VAR_image_id: ${{ needs.packer.outputs.image_id }}
          TF_VAR_location: ${{ env.AZURE_LOCATION }}
        run: terraform plan -input=false -out=tfplan

      - name: Upload Terraform plan
        if: ${{ always() }}
        uses: actions/upload-artifact@v4
        with:
          name: terraform-plan
          path: ./terraform/tfplan

      - name: Terraform apply
        if: ${{ github.event_name == 'workflow_dispatch' &amp;&amp; github.event.inputs.apply == 'true' }}
        working-directory: ./terraform
        env:
          TF_VAR_image_id: ${{ needs.packer.outputs.image_id }}
          TF_VAR_location: ${{ env.AZURE_LOCATION }}
        run: terraform apply -auto-approve tfplanUsage of GitHub ActionsAs with Jenkins, you can use GitHub Actions for a variety of different scenarios, like: Triggering CI/CD pipelines Automation of GitHub tasks Setting up or running scheduled tasks like checks for configuration drifts or security checks Deploying and/or Altering your Infrastructure by using Infrastructure as Code Example for running a Terraform workflow: name: Terraform CI/CD

on:
  pull_request:
    paths:
      - "infra/**"
      - ".github/workflows/terraform.yml"
  push:
    branches: [ "main" ]
    paths:
      - "infra/**"
      - ".github/workflows/terraform.yml"
  workflow_dispatch: {}

permissions:
  contents: read

# Map GitHub Repository Variables/Secrets -&gt; TF_VAR_* so Terraform sees them as input vars
env:
  TF_INPUT: "false"
  TF_VAR_project: ${{ vars.PROJECT }}
  TF_VAR_environment: ${{ vars.ENVIRONMENT }}
  TF_VAR_greeting: ${{ vars.GREETING }}
  TF_VAR_db_password: ${{ secrets.DB_PASSWORD }}

jobs:
  plan:
    name: Terraform Init/Validate/Plan
    runs-on: ubuntu-latest
    defaults:
      run:
        shell: bash
        working-directory: infra

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      # Install Terraform (update version as desired)
      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v2
        with:
          terraform_version: 1.9.5

      - name: Terraform Init
        run: terraform init -input=false

      - name: Terraform Format (check)
        run: terraform fmt -check -diff

      - name: Terraform Validate
        run: terraform validate

      - name: Terraform Plan
        run: terraform plan -no-color -out=tfplan.bin

      - name: Upload Plan Artifact
        uses: actions/upload-artifact@v4
        with:
          name: tfplan
          path: infra/tfplan.bin
          if-no-files-found: error

  apply:
    name: Terraform Apply
    needs: plan
    runs-on: ubuntu-latest
  
    environment:
      name: production
    if: github.event_name == 'push' &amp;&amp; github.ref == 'refs/heads/main'
    defaults:
      run:
        shell: bash
        working-directory: infra

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v2
        with:
          terraform_version: 1.9.5

      - name: Terraform Init
        run: terraform init -input=false

      - name: Download Plan Artifact
        uses: actions/download-artifact@v4
        with:
          name: tfplan
          path: infra

      - name: Terraform Apply
        run: terraform apply -input=false -auto-approve tfplan.bin Installation and Usage of Azure PipelinesAzure Pipelines is a cloud-based offering from Microsoft Azure designed for continuous integration (CI) and continuous delivery (CD). It accommodates various programming languages and project types, and it seamlessly integrates with GitHub, Azure Repos, and other version control platforms. This service is essential for infrastructure automation as it facilitates Infrastructure as Code (IaC) methodologies. Similar to Jenkins and GitHub Actions, Azure Pipelines empowers teams to automate the provisioning and management of infrastructure through tools like Terraform, Ansible, and Packer.  Key FeaturesMulti-platform Support:  Build and deploy on Windows, Linux, and macOS  Language Support:  Works with .NET, Java, Node.js, Python, PHP, Ruby, and more  Integration:  Seamlessly integrates with GitHub, Bitbucket, Azure Repos, and Docker  Parallel Jobs:  Run multiple jobs in parallel to speed up the CI/CD process  Custom Agents:  Use Microsoft-hosted or self-hosted agents for builds  YAML Pipelines:  Define pipelines as code using YAML syntax  Environment Management:  Manage approvals and checks for deployments  Core ConceptsTrigger:  A manual, scheduled, or automated trigger causes a pipeline to start  Pipeline:  The workflow that contains one or more stages and is deployed to one or more environments  Stages:  Used to organize pipelines by logically grouping jobs; each stage contains one or more  Jobs:  A collection of steps that run on the same agent. Each job contains one or more steps  Step:  An individual task (e.g., running a script, installing a package)  Task:  A prepackaged script that performs an action, such as invoking a Packer or Terraform script A pipeline run produces artifacts such as files or packages. Installing Azure PipelinesSign in to Azure DevOps Create a new project in Azure DevOps Navigate to Pipelines -&gt; New Pipeline Connect your repository (GitHub, Azure Repos, etc.) Choose a template or start with an empty YAML file Define your pipeline steps and save the YAML file in your repo Run the pipeline to start the CI/CD process  Usage of Azure PipelinesAs with Jenkins and GitHub Actions, you can use Azure Pipelines for a variety of different scenarios, like: Triggering CI/CD pipelines Setting up or running scheduled tasks like checks for configuration drifts or security checks Deploying and/or Altering your Infrastructure by using Infrastructure as Code Example for running an Azure Pipelines workflow: azure-pipelines.yml
trigger:
  branches:
    include:
      - main

pr:
  branches:
    include:
      - '*'

variables:
  vmImage: 'ubuntu-latest'
  terraformVersion: '1.8.7'         # set the TF version you want
  workingDirectory: 'infra'         # folder with your Terraform code
  azureServiceConnection: 'sc-az-devops'
  prodEnvironmentName: 'production' # Azure DevOps Environment (set approvals/checks in UI)

stages:
# --------------------------------------
# Stage: Validate &amp; Plan
# --------------------------------------
- stage: Validate_and_Plan
  displayName: 'Terraform Validate &amp; Plan'
  jobs:

  # PRs -&gt; Dev plan
  - job: plan_dev
    displayName: 'Dev plan (PRs)'
    condition: ne(variables['Build.SourceBranch'], 'refs/heads/main')
    pool:
      vmImage: $(vmImage)
    steps:
    - checkout: self
      clean: true

    - script: |
        set -euo pipefail
        curl -sLo terraform.zip https://releases.hashicorp.com/terraform/$(terraformVersion)/terraform_$(terraformVersion)_linux_amd64.zip
        sudo unzip -o terraform.zip -d /usr/local/bin
        terraform -version
      displayName: 'Install Terraform $(terraformVersion)'

    - task: Cache@2
      displayName: 'Cache Terraform plugins'
      inputs:
        key: 'terraform | $(Agent.OS) | $(Build.SourcesDirectory)/$(workingDirectory)/.terraform.lock.hcl'
        path: '$(Build.SourcesDirectory)/$(workingDirectory)/.terraform'
        restoreKeys: |
          terraform | $(Agent.OS)

    - task: AzureCLI@2
      displayName: 'terraform fmt/validate/plan (dev)'
      inputs:
        azureSubscription: $(azureServiceConnection)
        scriptType: bash
        scriptLocation: inlineScript
        workingDirectory: $(workingDirectory)
        inlineScript: |
          set -euo pipefail
          terraform fmt -check
          terraform init -upgrade -input=false
          terraform validate
          terraform workspace select dev || terraform workspace new dev
          terraform plan -input=false -var-file=env/dev.tfvars

  # main -&gt; Prod plan (artifact published for apply)
  - job: plan_prod
    displayName: 'Prod plan (main)'
    condition: eq(variables['Build.SourceBranch'], 'refs/heads/main')
    pool:
      vmImage: $(vmImage)
    steps:
    - checkout: self
      clean: true

    - script: |
        set -euo pipefail
        curl -sLo terraform.zip https://releases.hashicorp.com/terraform/$(terraformVersion)/terraform_$(terraformVersion)_linux_amd64.zip
        sudo unzip -o terraform.zip -d /usr/local/bin
        terraform -version
      displayName: 'Install Terraform $(terraformVersion)'

    - task: Cache@2
      displayName: 'Cache Terraform plugins'
      inputs:
        key: 'terraform | $(Agent.OS) | $(Build.SourcesDirectory)/$(workingDirectory)/.terraform.lock.hcl'
        path: '$(Build.SourcesDirectory)/$(workingDirectory)/.terraform'
        restoreKeys: |
          terraform | $(Agent.OS)

    - task: AzureCLI@2
      displayName: 'terraform init/validate/plan (prod)'
      inputs:
        azureSubscription: $(azureServiceConnection)
        scriptType: bash
        scriptLocation: inlineScript
        workingDirectory: $(workingDirectory)
        inlineScript: |
          set -euo pipefail
          terraform fmt -check
          terraform init -upgrade -input=false
          terraform validate
          terraform workspace select prod || terraform workspace new prod
          # Write the plan into the repo working dir so paths remain valid on apply
          terraform plan -input=false -var-file=env/prod.tfvars -out=tfplan

    - publish: $(workingDirectory)/tfplan
      artifact: tfplan
      displayName: 'Publish Terraform plan artifact (prod)'

# --------------------------------------
# Stage: Apply (main only; protected Environment)
# --------------------------------------
- stage: Apply
  displayName: 'Terraform Apply (main)'
  dependsOn: Validate_and_Plan
  condition: eq(variables['Build.SourceBranch'], 'refs/heads/main')
  jobs:
  - deployment: apply_to_production
    displayName: 'Apply to production'
    environment: $(prodEnvironmentName)   # configure approvals/checks in Azure DevOps UI
    strategy:
      runOnce:
        deploy:
          steps:
          - checkout: self
            clean: true

          - download: current
            artifact: tfplan
            displayName: 'Download plan artifact'

          - script: |
              set -euo pipefail
              curl -sLo terraform.zip https://releases.hashicorp.com/terraform/$(terraformVersion)/terraform_$(terraformVersion)_linux_amd64.zip
              sudo unzip -o terraform.zip -d /usr/local/bin
              terraform -version
            displayName: 'Install Terraform $(terraformVersion)'

          - task: Cache@2
            displayName: 'Cache Terraform plugins'
            inputs:
              key: 'terraform | $(Agent.OS) | $(Build.SourcesDirectory)/$(workingDirectory)/.terraform.lock.hcl'
              path: '$(Build.SourcesDirectory)/$(workingDirectory)/.terraform'
              restoreKeys: |
                terraform | $(Agent.OS)

          # Copy the downloaded plan back into the same working directory used for planning
          - script: |
              set -euo pipefail
              cp "$(Pipeline.Workspace)/tfplan/tfplan" "$(Build.SourcesDirectory)/$(workingDirectory)/tfplan"
            displayName: 'Place plan file in working directory'

          - task: AzureCLI@2
            displayName: 'terraform init/apply (prod)'
            inputs:
              azureSubscription: $(azureServiceConnection)
              scriptType: bash
              scriptLocation: inlineScript
              workingDirectory: $(workingDirectory)
              inlineScript: |
                set -euo pipefail
                terraform init -input=false
                terraform workspace select prod || terraform workspace new prod
                terraform apply -input=false -auto-approve tfplan Comparison of Azure Pipelines, GitHub Actions, and JenkinsAll three mentioned CI/CD Frameworks have their strengths and weaknesses – use the one that best suits your needs. OverviewFeature Azure Pipelines GitHub Actions Jenkins Type Cloud &amp; self-hosted CI/CD service (part of Azure DevOps) Cloud-native CI/CD integrated with GitHub Open-source automation server Best For Enterprise environments, Azure-heavy ecosystems GitHub-native teams, open source, small to mid-size teams Highly customized workflows, legacy systems Hosting Microsoft-hosted or self-hosted agents GitHub-hosted or self-hosted runners Self-hosted (on-prem or cloud) Config Style YAML pipelines YAML workflows in .github/workflows Jenkinsfile (Groovy) or UI jobs Pricing Paid (with free tier), included in Azure DevOps Free for public repos, paid for private Free (open source), but infra costs apply  Ease of Setup/Learning CurveAzure Pipelines:  Quick for Azure users, but requires service connections and RBAC setup. Steeper learning curve for newcomers  GitHub Actions:  Easiest to get started if the code is on GitHub.  Zero installation, YAML-based, huge marketplace of pre-built actions  Jenkins:  Most complex to set up. Requires server installation, plugin management, and pipeline scripting.  That completes Part 1 of the Citrix Automation Handbook. In the first part of the Citrix Automation Handbook, we discussed the Citrix Automation story and the basics of Infrastructure-as-Code, of Continuous Integration/Continuous Deployment (CI/CD) strategies, and our main IaC tools: Packer, Terraform, and Ansible. In the second part of the Citrix Automation Handbook, we focus on: Applying automation and infrastructure-as-code in the Citrix universe. We discuss all relevant components and provide code examples from real environments Best practises for designing an environment for automation and IaC use, and discuss deployment tips In the third part of the Citrix Automation Handbook, we focus on common and special use cases, as well as complete deployment examples from the field. In the fourth part of the Citrix Automation Handbook, we cover creating a Citrix Virtual Apps and Desktops 2507 LTSR deployment on XenServer 8.4. In the fifth part of the Citrix Automation Handbook, we cover creating a Citrix DaaS deployment on Azure. In the sixth part of the Citrix Automation Handbook, we cover some final topics.  DisclaimerMost important All code snippets mentioned in this handbook were thoroughly tested and run in a sandbox environment. All shown Packer snippets, Terraform snippets, Ansible Playbooks, and PowerShell scripts were tailored exclusively for the specific environment used for this handbook. You need to adjust all settings, values, snippets, etc., in the most suitable way tailored to your requirements, regulations, and existing environments. All settings, scripts, and code examples listed in this document are intended only to illustrate the possibilities and serve as a basis for your own deployment. We tried to show different ways, even if they were not always in line with best-practice guidelines, such as unencrypted WinRM communication for demonstration purposes. Using all the provided code snippets is at your own risk – please read the disclaimer below for further information.   EXCEPT WHERE EXPRESSLY PROVIDED OTHERWISE BY CLOUD SOFTWARE GROUP, THE CONTENT IN THIS DOCUMENT IS PROVIDED “AS IS” AND CLOUD SOFTWARE GROUP HEREBY DISCLAIMS ALL EXPRESS OR IMPLIED REPRESENTATIONS, WARRANTIES, GUARANTIES, AND CONDITIONS, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES OR CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD TO BE LEGALLY INVALID. CLOUD SOFTWARE GROUP MAKES NO REPRESENTATIONS, WARRANTIES, GUARANTIES, OR CONDITIONS AS TO THE QUALITY, SUITABILITY, TRUTH, ACCURACY, OR COMPLETENESS OF ANY OF THE CONTENT CONTAINED ON THE WEBSITE. The above disclaimer applies to any damages, liability, or injuries caused by any failure of performance, error, omission, interruption, deletion, defect, delay in operation or transmission, computer virus, communication line failure, theft or destruction of or unauthorized access to, alteration of, or use, whether for breach of contract, tort, negligence or any other cause of action.]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2026_01/autom-overview.png.9dd0991f6b46932818ce7cfd8fda068e.png" length="274622" type="image/png"/><pubDate>Wed, 21 Jan 2026 09:11:00 +0000</pubDate></item><item><title>Licensing Guide</title><link>https://community.stage.citrix.com/tech-zone/learn/tech-briefs/unicon-licensing-guide/</link><description><![CDATA[Citrix Unicon Licensing Guide  IntroductionThe Citrix Unicon OS (eLux® Operating System) is a secure, lightweight, hardened operating system designed for Citrix DaaS environments. Combined with Citrix Unicon OS Management (Scout Management Suite), it provides a centralized solution for easily managing all endpoints in your enterprise. Currently, Scout can still be installed and operated using multiple licensing models. This guide explains the specifics and technical dependencies of those licensing models. Furthermore, it provides step-by-step instructions for using the available licensing models in Scout. This information allows customers to choose the listening model that best meets their individual requirements, ensuring an efficient setup. This guide is structured as follows: Brief technical background Overview of licensing models for Scout Compatibility of licensing models for Scout with Scout versions Process for obtaining Citrix Unicon entitlements through Citrix How to use the available licensing models for Scout Summary  Brief technical background The Scout Management Suite must be licensed to ensure full functionality. The licensing model (installation type/operating mode) can be selected during initial installation. Furthermore, it can be switched after the initial installation, depending on the current Scout version (see the following paragraphs for more details). All available licensing models require Unicon entitlements obtained from Citrix or Citrix partners.   Overview of licensing models for ScoutLicensing Model Validity Limitation Citrix License Server No limitation  License Activation Service (LAS) and Citrix legacy licensing only, not available for file-based licensing Enterprise Subscriptions End-of-Life (EOL) on 2026-04-15   Perpetual (keys for licenses and Software Maintenance) End-of-Life (EOL), no further extensions    Compatibility of licensing models for Scout with Scout versionsLicensing model Compatibility Scout 2507 and earlier Scout 2508 and later Citrix License Server Upon installation No Yes  After updating from an earlier version No Yes  Switch to another licensing model No No Enterprise Subscriptions Upon installation Yes Yes  After updating from an earlier version Yes Yes  Switch to another licensing model No To Citrix License Server  Perpetual (keys for licenses and Software Maintenance) Upon installation Yes No  After updating from an earlier version Yes Still functioning, but no further extensions are possible  Switch to another licensing model Scout 2412 or Scout 2409 LTSR CU2 and later: to Enterprise Subscriptions To Citrix License Server (Scout 2508 and later) and Enterprise Subscriptions (Scout 2508.1 and later) If the Scout was installed using the “Standard” installation type in Scout 2507 or earlier, it is using the perpetual licensing model. In this case, the license information dialog in Scout Board shows “Software Maintenance for eLux &amp; Scout”:  It is recommended to switch from the perpetual licensing model to another licensing model.  Process for obtaining Citrix Unicon entitlements through CitrixThe process for obtaining Citrix Unicon entitlements depends on the customer type and the current Citrix licenses. Similarly, there are different processes to obtain trials for Unicon solutions. Generally, all Scout licensing models are compatible with all Citrix licenses that include Unicon entitlements. However, the limitations listed in the overview of licensing models for Scout apply. For customers with CPL licenses, Unicon entitlements are already included in the Citrix license. For customers with UHMC licenses and some other licenses, Unicon entitlements are partially already included in the Citrix license. To verify this, check whether “Citrix Unicon” is listed as a sub-component of the license (https://www.citrix.com/account/) or reach out to your Citrix representative (e.g. Citrix ATS / Citrix Partner). One case in which Unicon entitlements are included in the Citrix license is a UHMC license renewed or initially purchased after 2025-06-08. If Unicon entitlements are included in the Citrix license, customers do not require trials and can use their actual Unicon entitlements instead. Otherwise, customers can reach out to their Citrix representative (e.g., Citrix ATS / Citrix Partner) to obtain Unicon entitlements or to request trials.  How to use the available licensing models for ScoutHow to use the licensing model Citrix License Server in ScoutSince this licensing model remains valid after 2026-04-15, when the Enterprise Subscription reaches End-of-Life (EOL), it is the recommended option. Moreover, it is the most user-friendly licensing model because once a License Server is set up and License Activation Service (LAS) is activated, existing Unicon entitlements are automatically available. Furthermore, LAS provides an offline activation option for airgapped environments. Contact your Citrix representative for this option. Technical requirements and detailed instructions on how to set up a Scout with this licensing model can be found in the eLux and Scout POC Guide and in the product documentation. To switch from another licensing model to this licensing model, use the corresponding button (Switch to Citrix License Server) in the license information dialog in Scout Board.  How to use the licensing model Enterprise Subscriptions in ScoutSince this licensing model reaches its end-of-life on 2026-04-15, it is recommended to use the Citrix License Server licensing model instead. To use Enterprise Subscriptions, the Scout must connect to the Citrix backend at least once over the internet. Afterwards, offline reporting is possible. Contact your Citrix representative for this option. This licensing model is therefore not suitable for permanently airgapped environments. Furthermore, a Technical Scout User is required. Follow the subsequent steps to create or activate a Technical Scout User and to use Enterprise Subscriptions for Scout. If a Technical Scout User was already activated before the last renewal, follow step 1 again to re-activate the Technical Scout User once the renewal is processed. This will update the Technical Scout user with the most recent expiration date and seat limit. Activate a Technical Scout User: Locate and click the Unicon button in Citrix MyAccount. Manage licenses &gt; Activate Unicon Customer  If the button is not visible, consider that it may take some time for orders to be processed, and the button will appear. Submit the user's information: Click “Activate Unicon Customer”. Enter the “First Name”, “Last Name” and “Email” address of the Technical Scout user which will be used for the Enterprise Subscriptions. One Technical Scout User can be used to install multiple Scouts Click “Submit”.  A success notification is shown, and the activated user is listed on the right under “Active Users”.  In the backend, a new myelux user is created as a Technical Scout User or an existing myelux user with the submitted email address updated accordingly with the number of seats and expiration date. A confirmation email is sent to the submitted email address, describing the next steps.  Complete the User setup in myelux: Follow the instructions in the confirmation email: If the myelux user did not exist before, set the initial password. If the password cannot be set, your user was most likely not correctly created/updated. If you have a myelux user and are able to log in but don’t see the page Licensing &gt; Scout MSP License Report, your myelux user was not correctly updated.  Scout installation or licensing model switch: Scout installation: Technical requirements and detailed instructions for setting up a Scout with this licensing model can be found in the eLux and Scout POC Guide and the product documentation. During installation, do not change the default MSP reporting portal / URL unless instructed to do so. Licensing model switch to Enterprise Subscriptions: To switch from another licensing model to this licensing model, use the corresponding button (Switch to Citrix License Server) in the license information dialog in Scout Board. During the switch, do not change the default MSP reporting portal / URL unless you are instructed to do so. In case you are encountering a “Mode switch error” (see screenshot below) during the switch of the Scout operating mode, do the following:  If you are using a proxy: Leave the proxy field of the SBD empty. Set the proxy in Edge for the user account instead. If this does not resolve the error, contact the technical support. You will most likely need to submit your Scout's diagnostic data, which you can already retrieve. For technical issues related to the licensing model, contact your Citrix ATS, Citrix Partner, or Citrix Support. How CSPs can use licensing models for ScoutNormally, a CSP would install a Scout as described in the previous sections, using its own Citrix License Server or its own Technical Scout User for Enterprise Subscriptions. After installation, the CSP can grant admin rights to the Scout for customers.  SummaryThis guide provides a detailed explanation of licensing models currently available for Unicon Scout. It described the specifics and technical dependencies of those licensing models. Furthermore, it provides step-by-step instructions for using the available licensing models in Scout. Following this guide enabled customers to choose the listening model that best meets their individual requirements. This leads to time savings and guarantees an efficient setup of Unicon Scout. For further guidance, reach out to your Citrix representative, check out the eLux and Scout POC Guide, or consult the official documentation at https://docs.citrix.com/en-us/unicon-elux-scout.]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2026_01/image.png.70c932e3015b16903881967f4e59e692.png" length="66894" type="image/png"/><pubDate>Thu, 15 Jan 2026 13:10:00 +0000</pubDate></item><item><title>Avoid Common Misconfigurations that Can Negatively Impact DaaS Resiliency</title><link>https://community.stage.citrix.com/tech-zone/learn/tech-briefs/avoid-daas-resiliency-misconfiguration/</link><description><![CDATA[Overview Local Host Cache and Service Continuity should be at the forefront of conversations when building a resilient Citrix environment.  Local Host Cache and Service Continuity are Citrix technologies that can maintain end-user access to business-critical workloads during potential service disruptions.  While they function differently, both features serve the same purpose: to keep users launching their apps and desktops regardless of service health.  Let's start by differentiating the 2 features:  Local Host Cache (LHC): LHC leverages a locally cached copy of the Site database hosted on Cloud Connectors. The local copy of the Site database is used to broker sessions if connectivity between the Cloud Connectors and Citrix Cloud is lost. LHC is enabled by default for DaaS environments, but some configurations must be considered to ensure LHC works properly during a service disruption.   Service Continuity: Service Continuity is a DaaS-only feature that uses Connection Lease files downloaded to a user’s endpoint when they log in to Citrix Workspace from either the Workspace app or a browser (the Citrix Workspace web extension is required for Service Continuity to work in a browser). Service Continuity uses the Connection Lease files when a normal end-user launch path cannot be established. It’s important to note that Service Continuity can leverage the LHC database on the Cloud Connectors, so many of the LHC misconfigurations below can also impact customers using Service Continuity for resiliency. Service Continuity is only supported for DaaS connections through the Workspace service. Service Continuity cannot be used with an on-premises StoreFront server.    Note The deprecated Citrix feature called “connection leasing” resembles Workspace connection leases in that it improves connection resiliency during outages. Otherwise, that deprecated feature is unrelated to service continuity.  Understanding the resiliency features is a critical first step in configuring your environment correctly in the case of a service disruption. This article assumes a working knowledge of LHC and Service Continuity.  There are a few crucial configurations to check for to ensure that users can continue to access their resources when LHC or Service Continuity activates. The table below lists common misconfigurations that may impact the availability of DaaS resources in the event of a service disruption. Review this list and update any potential misconfigurations in your environment before it’s too late!   Impacts All Access Methods In the table below, you’ll see that some misconfigurations impact StoreFront only, some impact Workspace only, and some can impact both. That’s because, with Service Continuity, the Cloud Connectors still attempt to retrieve the VDA addresses from the Citrix Cloud-hosted session broker (using the information supplied from the cached connection leases on the endpoint). If the session broker is unreachable, the VDA addresses are determined using the LHC database. You can see the entire process here in more detail.   Misconfiguration Description  Impact  Detection  Mitigation  MachinesUnusableDuringHA   Pooled single-session OS VDAs that are power managed are unavailable during LHC  Because power management services reside on Citrix Cloud infrastructure rather than Cloud Connectors, power management becomes unavailable during an LHC event. This results in an inability to reboot power-managed pooled single-session OS VDAs and reset their differencing/write cache disks while LHC is active. For security reasons, these VDAs are unavailable by default during LHC to avoid changes and data from previous user sessions being available on subsequent sessions.  During an LHC event, the machines in this delivery group will not be available for new launches.  Review the Delivery Groups node in Studio. Any pooled single-session delivery group that is power-managed and is not configured for access during LHC will show a warning icon.  Edit the Local Host Cache settings within the delivery group.    Note: Changing the default can result in changes and data from previous user sessions being present in subsequent sessions.  ExceedsRecommendedVdas   Too many VDAs in a Resource Location  The LHC broker on Cloud Connectors caps VDA registrations at 10,000 per resource location if the resource location goes into LHC mode.  A subset of VDAs will be unavailable during LHC.  Review the Zones node in Studio.  1. Reconfigure Resource Locations to contain no more than 10,000 VDAs.  3 Consecutive failed config syncs  Cloud Connectors periodically sync configurations from Citrix Cloud to the local database to ensure up-to-date configurations if LHC activates.  Failed configuration syncs can result in stale or corrupted configurations used in case of an LHC event. Monitor Cloud Connectors for 505 events from the Config Synchronizer Service. Email alerts for failed config syncs are on the roadmap for Citrix Monitor!  Review firewall configurations to ensure your firewall accepts XML and SOAP traffic. Review CTX238909. Open a support ticket to determine why config sync failures occur in your environment.   MultipleElectedLhcBrokers   Multiple elected brokers  One Cloud Connector per resource location should be elected for LHC events. Cloud Connectors must be able to communicate with each other to determine the elected broker and understand the health of other peer Cloud Connectors to make a go/no-go decision about entering LHC mode.  “Split brain” scenario where multiple Connectors in the same Resource Location remain active during an LHC event. VDAs may register with any elected connectors in the Resource Location, and launches may fail intermittently while LHC is active.  Review the Zones node in Studio.   Otherwise, monitor Cloud Connectors for 3504 events from the High Availability Service to see if more than one Cloud Connector per Resource Location is being elected. Ensure Cloud Connectors can communicate with each other at http://&lt;FQDN_OF_PEER_CONNECTOR&gt;:80/Citrix/CdsController/ISecondaryBrokerElection.  If using a proxy, bypassing the proxy for traffic between Cloud Connectors is recommended.    Lack of Regular Testing  It’s debatable whether a lack of testing can be considered a misconfiguration, but what’s not up for debate is the impact testing can have on ensuring this tech works in your environment! Testing can ensure infrastructure is scaled properly and works as expected before a disruption occurs. Testing should be done at regular intervals. For testing LHC, see Force an Outage. Forcing LHC is relevant to both on-prem StoreFront customers and customers leveraging Service Continuity.  DaaS resources are potentially inaccessible during a service disruption.  If you don’t have a testing plan for LHC or Service Continuity in your environment, consider this misconfiguration detected. Verify that Local Host Cache is working.  Create a testing plan to test Service Continuity and Local Host Cache regularly.  Low vCPU cores per socket configuration  LHC operates using a Microsoft SQL Server Express LocalDB on the Cloud Connector. Microsoft has a limitation on SQL Express in which the Connector is limited to the lesser of 1 socket or 4 vCPU cores when using the LHC DB. If we configure Cloud Connectors to use one core per socket (e.g., 4 sockets, 4 cores), we limit LHC operating on a single core during an LHC event. Because all VDA registration and brokering operations go through a single connector during LHC, this can negatively impact performance and cause issues with VDA registration during the service disruption. More info regarding LHC core and socket configurations can be found in the Recommended compute configuration for Local Host Cache.  Negative impact on the stability of VDA re-registration during an LHC event and the performance of LHC brokering operations.  Check Task Manager to view core and socket configurations. Divide the number of virtual processors by the number of sockets to get your core-per-socket ratio.  Reconfigure your VM to use at least 4 cores per socket. A new instance type may have to be used for public cloud workloads. Rebooting your connector may be required to reconfigure the core and socket configuration.   Undersized Cloud Connectors  During an event in which the LHC is activated, a single Cloud Connector per resource location begins to broker sessions. The elected LHC broker handles all VDA registrations and session launch requests (see Resource locations with multiple Cloud Connectors for more information on the election process). Sizing connectors to handle this added load during an LHC event is important for ensuring consistent performance.   Negative impact on stability and performance during LHC VDA registration and LHC steady state. Check out the Zones node within Citrix DaaS Full Configuration! If your environment has undersized connectors, see our documentation for troubleshooting steps. The sizing of connectors can also be checked on each connector machine at the hypervisor or VM level.  Reconfigure connectors to have at least 4 vCPU and 6 GB of RAM. Review the Recommended compute configuration for local host cache for recommended sizing guidelines based on the number of VDAs in the Resource Location.    MultipleDomainsInResourceLocation   Multiple Active Directory (AD) domains in a single resource location  As per Citrix DaaS limits e-docs, only one Active Directory domain is supported per resource location.  Multiple AD domains in a single Resource Location can cause issues with VDA registration during LHC events. VDAs may have to try multiple Connectors before finding one they can register with. This can impact VDA registration times and add additional load on Connectors when VDAs must register, especially in VDA registration storms like Local Host Cache re-registration or Autoscale events.  Review the Zones node in Studio.  If your environment has multiple AD domains in a resource location, a warning icon will show on the resource location.   Reconfigure resource locations to only contain Cloud Connectors in a single AD domain per resource location.     VdaRegisteringInMultipleZones   Auto-update of DDC list disabled  By default, auto-update of ListOfDDCs is enabled in the policy. If it has been deliberately disabled, it is important that VDAs are provided with a ListOfDDCs from the same resource location that the VDAs are configured for.  During an LHC event, VDAs that are registered in the wrong resource location will not be available for launch.  Check if the policy is disabled in Studio.  1. Enable the policy for auto update of DDC list.   Impacts Workspace The following misconfigurations only apply when Workspace is used as the access tier.    Misconfiguration Description Impact  Detection  Mitigation  Service Continuity not enabled  Things can’t work if they’re not turned on! Service Continuity is a core resiliency feature for customers leveraging the Citrix Workspace service as their access tier. You can manage Service Continuity on the Citrix Cloud Workspace configuration page.  Without Service Continuity enabled, Connection Lease files won’t be downloaded, and users won’t be able to access their apps and desktops during a service disruption.   View the Service Continuity tab in the Citrix Cloud Workspace configuration page to see if the feature is enabled.  Enable Service Continuity. See Configure Service Continuity for more information.  Access clients are unsupported for Service Continuity To download Service Continuity Connection Lease files, users must access their Workspace from a client that supports Service Continuity. See User device requirements to learn which client versions and access scenarios are supported.  Users accessing from clients that do not support Service Continuity will be unable to launch DaaS resources during a service disruption.  Review session launches in Monitor for Workspace app versions.  Update Citrix Workspace app clients to versions that support Service Continuity. Encourage users using the web browser to install the Citrix Workspace web extension.   Impacts StoreFrontThe following misconfigurations only apply when StoreFront is used as the access tier.    Misconfiguration Description Impact  Detection  Mitigation  SfAdvanceHealthCheck   StoreFront ‘Advanced Health Check’ setting not configured  StoreFront’s Advanced Health Check feature gives StoreFront additional information about the Resource Location where a published app or desktop can be launched.  Without an Advanced Health Check, StoreFront may send launch requests to a resource location that does not deliver that particular resource, resulting in launch failures during an LHC event.  On StoreFront, run “Get-STFStoreFarmConfiguration” via PowerShell.  Enable the StoreFront Advanced Health Check feature. For StoreFront 2308 forward, StoreFront Advanced Health Check is enabled by default.  If you upgrade your StoreFront, Advanced Health Check will be enabled automatically.  Out of Support StoreFront (1912)  StoreFront 1912 LTSR reached end of life in December 2024. No support, bugfixes, security patches, etc. Newer versions of StoreFront also have several additional resiliency enhancements.  Check the version on all StoreFront servers.  Upgrade StoreFront servers.  Incorrect load balancing monitor  Some customers opt to use a load-balancing vServer to balance XML traffic between StoreFront and Connectors for optimized manageability and traffic management. When connectors in a resource location go into LHC, only the primary broker can service launch requests. The remaining Connectors send health checks to try to reconnect. If an incorrect monitor is used on the load balancing server, StoreFront may continue to send launch requests to all connectors in the resource location rather than just the elected broker.  Potential intermittent launch failures during an LHC event.   Check your load balancer to ensure the monitor bound to the load balancer is monitoring for brokering capabilities, not just TCP responses. NetScaler has this functionality out of the box with the CITRIX-XD-DDC monitor. Note: The CITRIX-XML-SERVICE monitor is for previous versions of Citrix Virtual Apps and Desktops and does not perform the same checks as the CITRIX-XD-DDC monitor.   Configure your load balancing vServer to monitor Connectors based on brokering capabilities (e.g., use the CITRIX-XD-DDC monitor for connector load balancing).  Connectors not listed as a single set of resource feed in StoreFront  With the addition of StoreFront’s Advanced Health Check feature, Citrix recommends that all Cloud Connectors within a single Cloud tenant be included as a single set of Delivery Controllers in StoreFront. Check out this Citrix TIPs blog for more information regarding recommended configurations.  Duplicate icons for end users or more complex multi-site aggregation configurations are required in StoreFront.  View your resource feed configuration in the “Manage Delivery Controllers” tab in StoreFront.  Configure all Connectors (or all Connector load balancing vServers) are listed as a single Site within StoreFront. Review Add resource feeds for Desktops as a Service for more information.  Tags used to restrict launches to a subset of Resource Locations in a Delivery Group  With Advanced Health Check, StoreFront knows what resource locations a published app or desktop can launch from. StoreFront does this using Delivery Group to Machine Catalog mappings. However, StoreFront is not aware of tags. Consider a scenario in which a Delivery Group contains Machine Catalogs from Resource Location “A” and Resource Location “B”. If we use tags to restrict app/desktop launches to only Resource Location “A”, StoreFront will continue to send launch requests to both Resource Location “A” and “B” during an LHC event because it does not have tag information.  Potential intermittent launch failures during LHC events.  Review tags used in your environment. Automated detection of Resource Location-based tag restrictions is on our roadmap for Web Studio!  Configure tags so that at least one (preferably multiple!) VDA in each Resource Location delivered from a Delivery Group contains each tag.  MissingCtxStaTraffic   Not all connectors are receiving Secure Ticket Authority (STA) requests   A subset of Cloud Connectors in a resource location is not receiving STA traffic. This can be because either they are not listed in StoreFront or NetScaler Gateway, or there is another problem with communication, such as an expired certificate on the Cloud Connector.    Note: The list of cloud connectors used in StoreFront for NFuse and STA is separate. Check the mitigation column for the correct configuration.  During a Local Host Cache event, a single Cloud Connector acts as the STA server for the resource location. If that Cloud Connector is not receiving STA traffic, all launches through a NetScaler could fail during an LHC event.    If however, Standalone STA feature is enabled, then all Cloud Connectors can act as the STA servers for the resource location irrespective of LHC event. We need all the Cloud Connectors listed in Storefront should also match those in NetScaler gateway. Review the Zones node in Studio.    Otherwise, review resource feeds in StoreFront to ensure that all Cloud Connectors are listed. If so, test that StoreFront can communicate with all listed Cloud Connectors over the port configured in the resource feed.  View NetScaler Gateway configurations in StoreFront and ensure all Cloud Connectors are listed as STA servers.  Review NetScaler appliances and ensure all STAs listed in StoreFront are in the same format in the NetScaler Gateway vServers.  STA service health can also be monitored in the Gateway vServer.    MissingCtxStaTrafficWhenVdaExists   Resource location has VDA registrations, but does not have STA traffic  The Cloud Connectors in a resource location are not receiving STA traffic, but have VDA registrations. This can be because either they are not listed in NetScaler Gateway or Storefront, or there is another problem with communication, such as an expired certificate on the Cloud Connector.  During a Local Host Cache event, a single Cloud Connector acts as the STA server for the resource location. If the elected broker is not receiving STA traffic, all launches through a NetScaler could fail during an LHC event.  If, however, Standalone STA feature is enabled, then all Cloud Connectors can act as the STA servers for the resource location irrespective of an LHC event.  Check that all Cloud Connectors are listed as STAs in StoreFront and NetScaler Gateway.  View NetScaler Gateway configurations in StoreFront and ensure all Cloud Connectors are listed as STA servers.  Review NetScaler appliances and ensure all STAs listed in StoreFront are in the same format in the NetScaler Gateway vServers.  STA service health can also be monitored in the Gateway vServer.  MissingNFuseTraffic   StoreFront not communicating with all Cloud Connectors  StoreFront can only contact a subset of Cloud Connectors in a resource location. This can be because either they are not listed in StoreFront or there is another problem with communication, such as an expired certificate on the Cloud Connector.    Note: The list of cloud connectors used in Storefront for Nfuse and Sta is separate. Check the mitigation column for the correct configuration.   StoreFront not communicating with a subset of Cloud Connectors can negatively impact the scalability and performance of an environment during both steady-state and LHC operations. If the elected LHC broker is not receiving StoreFront traffic, all LHC launch attempts may fail. Review the Zones node in Studio.    Otherwise, review resource feeds in StoreFront to ensure that all Cloud Connectors are listed. If so, test that StoreFront can communicate with all listed Cloud Connectors over the port configured in the resource feed.  Add all Cloud Connectors to the resource feed in StoreFront and fix any communication issues between StoreFront and the Cloud Connectors.  One of the most common communication issues between StoreFront and Cloud Connectors is an expired certificate on the Cloud Connector when XML traffic is over port 443.    Note: For customers with many Cloud Connectors, it may be beneficial to configure load-balancing vServers for each resource location to reduce the management overhead and simplify troubleshooting. Review Citrix TIPs: Integrating Citrix Virtual Apps and Desktops service and StoreFront for more information.  Review Citrix TIPs: Integrating Citrix Virtual Apps and Desktops service and StoreFront for more information.  MissingNFuseTrafficWhenVdaExists   Resource location has VDA registrations, but does not have NFuse traffic  The Cloud Connectors in a resource location are not receiving NFuse requests from StoreFront, but have VDA registrations in the resource location. This can be because either they are not listed in StoreFront or there is a problem with communication, such as an expired certificate on the Cloud Connector.  StoreFront not communicating with Cloud Connectors can negatively impact the scalability and performance of an environment during both steady-state and LHC operations. If the elected broker is not receiving StoreFront traffic, LHC launch attempts may fail.    Review the resource feeds in StoreFront to ensure that all Cloud Connectors are listed. If so, test that StoreFront can communicate with all listed Cloud Connectors over the port configured in the resource feed.  Add all Cloud Connectors to the resource feed in StoreFront and fix any communication issues between StoreFront and the Cloud Connectors.  One of the most common communication issues between StoreFront and Cloud Connectors is an expired certificate on the Cloud Connectors when XML traffic is over port 443.   Note: For customers with many Cloud Connectors, it may be beneficial to configure load-balancing vServers for each resource location to reduce the management overhead and simplify troubleshooting. Review Citrix TIPs: Integrating Citrix Virtual Apps and Desktops service and StoreFront for more information.   Summary Correctly configuring your Citrix environment significantly impacts its availability and performance. Review your environments for these potential misconfigurations to keep your business running, no matter what!]]></description><pubDate>Fri, 09 Jan 2026 14:35:00 +0000</pubDate></item><item><title>Deployment Guide: The Modern Way of creating Master Images using IaC with Packer, Ansible, Chocolatey, and GitHub Actions</title><link>https://community.stage.citrix.com/tech-zone/automation/masterimage-iac-packer-ansible-chocolatey-github/</link><description><![CDATA[Deployment Guide: The Modern Way of creating Master Images using IaC with Packer, Ansible, Chocolatey, and GitHub ActionsIntroductionImagine an enterprise IT team tasked with rolling out a new software stack across hundreds of machines. Without Infrastructure as Code (IaC) tools like Packer, Terraform, and Ansible, and without package managers like Chocolatey, this process quickly becomes a fragile, manual exercise.  Each master image for DaaS and/or CVAD is built by hand, each configuration is applied individually, and every update requires human intervention. Over time, inconsistencies creep in—one server has a slightly different patch level, another is missing a dependency. These discrepancies aren’t just inconvenient; they introduce real risk.  Security vulnerabilities hide in outdated components, compliance audits become nightmares, and troubleshooting turns into detective work because no two environments are truly identical. This process often leads to: Inconsistency across environments due to human error High operational overhead for updates and patching Limited scalability, making it difficult to replicate or roll back changes quickly Now imagine the same scenario with IaC and automated package management in place. Packer creates golden images that are version-controlled and reproducible, ensuring every deployment starts from a trusted baseline. Terraform provisions infrastructure declaratively, so environments can be spun up or torn down with confidence, knowing they’ll match the desired state exactly. Ansible handles configuration at scale, applying changes consistently across thousands of nodes without manual touchpoints. Chocolatey and Ansible take care of software installation and updates, eliminating the chaos of ad-hoc downloads and ensuring every machine runs the correct version of every application. The benefits compound quickly: Risk drops because environments are predictable and auditable Compliance becomes easier because configurations are codified and traceable Security improves because updates can be rolled out systematically, closing vulnerabilities before they become exploits And perhaps most importantly, the burden on IT staff shrinks dramatically. Instead of spending hours—or days—building images and troubleshooting inconsistencies, they manage code, not servers These tools automate repetitive tasks, freeing time for strategic work that drives business value. What was once a painstaking, error-prone process becomes a streamlined, reliable pipeline, turning infrastructure from a liability into an asset This approach not only streamlines image management but also aligns with modern DevOps practices, enabling agility and reliability at scale. OverviewCitrix has always focused on relieving administrators of administrative burden – the automation of Citrix DaaS and CVAD deployments is a big part of this strategy. Using Packer with a modern package manager like Chocolatey makes it easier to create master images that include all necessary software components. With Packer, you define the basic settings of the master image based on your requirements for the underlying operating system. Next, with Chocolatey, you can then install all the necessary software components after configuring the operating system. Chocolatey´s repository currently lists over 10,000 software packages. Finally, Ansible enables installing software components that are not available in Chocolatey or that are not supported for deployment via Chocolatey. Since we are referring to DaaS and CVAD, Packer can also perform the appropriate configuration and installation of the Virtual Delivery Agent (VDA) and other Citrix components, resulting in a fully configured master image ready for further use.      Let´s look at an example flow:  PrerequisitesWe assume you have a dedicated machine or a cluster running all Automation components.  If all IaC frameworks reside on the same machine/cluster, communication between them is less error-prone. We recommend an Ubuntu-based machine/cluster for various reasons: An Ubuntu VM is one of the most reliable, flexible, and well-supported environments for running IaC tools like Terraform, Ansible, Jenkins, and Packer, because it offers stability, compatibility, and strong community support. Most IaC tools (Terraform, Ansible, Packer) are developed and tested primarily on Linux.  Note You can find a guide to create such an all-in-one deployment machine on Citrix TechZone. All communication between Terraform, Packer, and Ansible on the IaC-VM and the target Master Image VM uses SSH and/or WinRM. WinRM (Windows Remote Management) is a Microsoft protocol that enables remote management of Windows systems by allowing access to remote computers to perform management tasks. It is Microsoft's implementation of the WS-Management (Web Services Management Protocol) standard.  It often uses SOAP (Simple Object Access Protocol) for communication over HTTP or HTTPS, using ports 5985 (HTTP) and 5986 (HTTPS) by default.  WinRM provides the basis for remote management with PowerShell and is used for tasks such as running remote scripts, automating, configuring, and performing system inventory. Therefore, WinRM and SSH must be configured correctly. Important We emphasize ensuring the communication flow works as intended from the outset of the pre-domain-joined stage; otherwise, Terraform and Ansible will fail to configure the VMs. During the pre-domain-join stage, you can configure these settings using the local Group Policy Editor on the VM, in the autounattend.xml file, or using Packer settings. We will use Packer settings in this guide. After a successful domain join, you can set all relevant settings using GPOs. To provide maximum flexibility and a fallback mechanism, you can use both listeners—HTTP and HTTPS. You can check all available listeners using PowerShell: PS &gt; winrm e winrm/config/listener
Listener [Source="GPO"]
    Address = *
    Transport = HTTP
    Port = 5985
    Hostname
    Enabled = true
    URLPrefix = wsman
    CertificateThumbprint
    ListeningOn = 10.10.137.10, 127.0.0.1, ::1
Listener
    Address = *
    Transport = HTTPS
    Port = 5986
    Hostname
    Enabled = true
    URLPrefix = wsman
    CertificateThumbprint = 24ed3…3ef21
    ListeningOn = 10.10.137.10, 127.0.0.1, ::1To check the communication, you can use a simple Terraform snippet: Unsecure Communication - connecting to the HTTP listener: resource "null_resource" "UploadTestFileToIP1" {
 connection {
    type            = var.Provisioner_Type
    user            = var.Provisioner_Admin-Username
    password        = var.Provisioner_Admin-Password
    host            = var.Provisioner_IP1
    timeout         = var.Provisioner_Timeout

  }

###### Upload Test script to IP1
  provisioner "file" {
     source      = "${path.module}/DATA/Test-Script.ps1"
    destination = "c:/temp/Test-Script.ps1"
    
  } 
}

resource "null_resource" "InstallonIP1" {
depends_on = [ null_resource.UploadTestFileToIP1 ]
connection {
    type            = var.Provisioner_Type
    user            = var.Provisioner_Admin-Username
    password        = var.Provisioner_Admin-Password
    host            = var.Provisioner_IP1
    timeout         = var.Provisioner_Timeout

  }
 provisioner "remote-exec" {
    inline = [
      "powershell -File c:/temp/Test-Script.ps1"
    ]
  }
}Output: ...
null_resource.InstallonIP1: Creating...
null_resource.InstallonIP1: Provisioning with 'remote-exec'...
null_resource.InstallonIP1 (remote-exec): Connecting to remote host via WinRM...
null_resource.InstallonIP1 (remote-exec):   Host: tf-w11-mi
null_resource.InstallonIP1 (remote-exec):   Port: 5985
null_resource.InstallonIP1 (remote-exec):   User: administrator
null_resource.InstallonIP1 (remote-exec):   Password: true
null_resource.InstallonIP1 (remote-exec):   HTTPS: false
null_resource.InstallonIP1 (remote-exec):   Insecure: true
null_resource.InstallonIP1 (remote-exec):   NTLM: false
null_resource.InstallonIP1 (remote-exec):   CACert: false
null_resource.InstallonIP1 (remote-exec): Connected!
...
Secure Communication - connecting to the HTTPS listener: resource "null_resource" "UploadTestFileToIP1" {
 connection {
    type            = var.Provisioner_Type
    user            = var.Provisioner_SecureAdmin-Username
    password        = var.Provisioner_SecureAdmin-Password
    host            = var.Provisioner_IP1
    port            = var.Provisioner_Port
    https           = var.Provisioner_HTTPS
    timeout         = var.Provisioner_Timeout

  }

###### Upload Test script to IP1
  provisioner "file" {
     source      = "${path.module}/DATA/Test-Script.ps1"
    destination = "c:/temp/Test-Script.ps1"
    
  } 
}

resource "null_resource" "InstallonIP1" {
depends_on = [ null_resource.UploadTestFileToIP1 ]
connection {
    type            = var.Provisioner_Type
    user            = var.Provisioner_SecureAdmin-Username
    password        = var.Provisioner_SecureAdmin-Password
    host            = var.Provisioner_IP1
    port            = var.Provisioner_Port
    https           = var.Provisioner_HTTPS
    timeout         = var.Provisioner_Timeout

  }
 provisioner "remote-exec" {
    inline = [
      "powershell -File c:/temp/Test-Script.ps1"
    ]
  }
}
Output: ...
null_resource.InstallonIP1: Creating...
null_resource.InstallonIP1: Provisioning with 'remote-exec'...
null_resource.InstallonIP1 (remote-exec): Connecting to remote host via WinRM...
null_resource.InstallonIP1 (remote-exec):   Host: tf-w11-mi
null_resource.InstallonIP1 (remote-exec):   Port: 5986
null_resource.InstallonIP1 (remote-exec):   User: svc_ansible
null_resource.InstallonIP1 (remote-exec):   Password: true
null_resource.InstallonIP1 (remote-exec):   HTTPS: true
null_resource.InstallonIP1 (remote-exec):   Insecure: false
null_resource.InstallonIP1 (remote-exec):   NTLM: false
null_resource.InstallonIP1 (remote-exec):   CACert: false
null_resource.InstallonIP1 (remote-exec): Connected!
...
 Important Configuring secure communication is a complex task that involves certificates and user mappings – you can find many comprehensive configuration guides online.  Note In this guide, we will primarily focus on using Azure as the Hyperscaler for deploying our master images. The available images offered in Azure have a basic WinRM configuration in place. Therefore, we do not need to run any WinRM configuration scripts in Packer.  Deploying Master Images with Software ComponentsAs soon as you are sure you have prepared all necessary scripts to configure WinRM consistently, you can start the deployment. Step 1: Determining available base images on AzureThe first step is to determine which base image you want to use – Azure offers many options.  As we create master images for MCS, we focus on Windows images. Packer needs to know which base image it should use: # Source Image
  os_type         = "Windows"
  image_publisher = var.azure_imgpublisher
  image_offer     = var.azure_imgoffer
  image_sku       = var.azure_imgsku
  image_version   = var.azure_imgversion
  Let´s find out which images we can use. The easiest way is to use PowerShell: Get the Image Publisher: PS C:\TMM&gt; $LocName="austriaeast"
PS C:\TMM&gt; $Publisher=Get-AzVMImagePublisher -Location $LocName | Where-Object { $_.PublisherName -eq "MicrosoftWindowsDesktop" }
PS C:\TMM&gt; $Publisher

PublisherName           Location   Id
-------------           --------   --
MicrosoftWindowsDesktop westeurope /Subscriptions/&lt;sensitive&gt;/Providers/Microsoft.Compute/Locations/austriaeast/Publishers/MicrosoftWindowsDesktop

PS C:\TMM&gt;Get the Image Offer: PS C:\TMM&gt; $Offer = Get-AzVMImageOffer -Location $LocName -PublisherName $Publisher.PublisherName | Where-Object { $_.Offer -eq "windows-11" }
PS C:\TMM&gt; $Offer

Offer      PublisherName           Location   Id
-----      -------------           --------   --
windows-11 MicrosoftWindowsDesktop austriaeast /Subscriptions/&lt;sensitive&gt;/Providers/Microsoft.Compute/Locations/austriaeast/Publishers/MicrosoftWindowsDesktop/ArtifactTypes/VMImage…

PS C:\TMM&gt;Get the Image SKU – here all SKUs are shown for informational purposes: PS C:\TMM&gt; $Skus = Get-AZVMImageSku -Location $LocName -PublisherName $Publisher.PublisherName -Offer $Offer.Offer | Select Skus
PS C:\TMM&gt; $SKus

Skus
----
win11-21h2-avd
win11-21h2-ent
win11-21h2-pro-zh-cn
win11-22h2-avd
win11-22h2-ent
win11-22h2-entn
win11-23h2-avd
win11-23h2-ent
win11-23h2-entn
win11-23h2-pro
win11-23h2-pro-zh-cn
win11-23h2-pron
win11-24h2-avd
win11-24h2-ent
win11-24h2-entn
win11-24h2-pro
win11-24h2-pro-zh-cn
win11-24h2-pron
win11-25h2-avd
win11-25h2-ent
win11-25h2-entn
win11-25h2-pro
win11-25h2-pro-zh-cn
win11-25h2-pron

PS C:\TMM&gt;Get the Image: PS C:\TMM&gt; Get-AzVMImage -Location $LocName -PublisherName $Publisher.PublisherName -Offer $Offer.Offer -Sku "win11-25h2-pro" | Select Version

Version
-------
26200.6584.250915
26200.6899.251011
26200.7171.251109
26200.7462.251207

PS C:\TMM&gt;We now have all the parameters we need to tell Packer which base image should be used: ...
azure_imgpublisher   = "MicrosoftWindowsDesktop"
azure_imgoffer       = "windows-11"
azure_imgsku         = "win11-25h2-pro"
azure_imgversion     = "26200.7462.251207"
...
 Step 2: Determining the software packages to be installed using ChocolateyThe next step is to determine which software packages you want to use – Chocolatey currently offers more than 10,000 packages. Note You can find an actual list at https://community.chocolatey.org/packages.  Important Please check whether a package is supported in your environment before deploying it. Each package has a lot of information tied to – the most important is if tests have failed. You can then look for a valid, error-free package in the package history. Example package data – Google Chrome:    In our environment, we, of course, only use problem-free packages. You can most easily check the last available problem-free versions using a PowerShell script to determine these packages: PS C:\TMM&gt; choco search --exact googlechrome -a --approved-only --not-broken --limit-output
GoogleChrome|143.0.7499.41
GoogleChrome|142.0.7444.176
GoogleChrome|142.0.7444.163
GoogleChrome|142.0.7444.135
GoogleChrome|142.0.7444.60
GoogleChrome|142.0.7444.52
GoogleChrome|141.0.7390.77
GoogleChrome|141.0.7390.66
GoogleChrome|141.0.7390.55
GoogleChrome|141.0.7390.37
...For easier use, we created a short Windows application that is called immediately before determining which software packages should be deployed: We declare all packages we offer for deployment in a JSON file to allow easy maintenance of the list: [
 {
  "Name": "Adobe Acrobat Reader DC",
  "Install": "adobereader"
 },
 {
  "Name": "Google Chrome",
  "Install": "googlechrome"
 },
 {
  "Name": "Mozilla Firefox",
  "Install": "firefox"
 },
 ...
 ,
 {
  "Name": "WinRAR",
  "Install": "winrar"
 },
 {
  "Name": "Dropbox",
  "Install": "dropbox"
 },
 {
  "Name": "KeePass",
  "Install": "keepass"
 }
]The application reads this list and retrieves the last three versions of each package and stores them also in a JSON file: [
  {
    "Install": "adobereader",
    "Version": "2025.1.20577"
  },
  {
    "Install": "adobereader",
    "Version": "2024.2.20857"
  },
  {
    "Install": "adobereader",
    "Version": "2023.8.20555"
  }
,
  {
    "Install": "GoogleChrome",
    "Version": "143.0.7499.41"
  },
  {
    "Install": "GoogleChrome",
    "Version": "142.0.7444.176"
  },
  {
    "Install": "GoogleChrome",
    "Version": "142.0.7444.163"
  }
,
  {
    "Install": "Firefox",
    "Version": "146.0.0"
  },
  {
    "Install": "Firefox",
    "Version": "145.0.2"
  },
  {
    "Install": "Firefox",
    "Version": "145.0.1"
  }
,
  ...
,
  {
    "Install": "dropbox",
    "Version": "238.4.6075"
  },
  {
    "Install": "dropbox",
    "Version": "237.4.5655"
  },
  {
    "Install": "dropbox",
    "Version": "236.4.5918"
  }
,
  {
    "Install": "keepass",
    "Version": "2.60.0"
  },
  {
    "Install": "keepass",
    "Version": "2.58.0"
  },
  {
    "Install": "keepass",
    "Version": "2.57.1"
  }
]This list serves as the foundation for all our subsequent Chocolatey-based deployments. Code snippet:  Private Sub but_start_Click(sender As Object, e As EventArgs) Handles but_start.Click
     Dim jsonPath As String = "C:\__PPMM\SS_IIS\ChocolateyPackages.json"
     Dim outputPath As String = "C:\__PPMM\SS_IIS\ChocolateyPackagesVersions.json"
     Dim apps As List(Of ChocolateyPackageList) = LoadApplications(jsonPath)
     Dim i_Apps As Integer = apps.Count
     ProgressBar1.Minimum = 0
     ProgressBar1.Maximum = i_Apps + 1

     Dim InstallCommand As String = String.Empty
     Dim PackageName As String = String.Empty

     If File.Exists(outputPath) Then
         File.Delete(outputPath)
     End If

     Dim i_ProgCount As Integer = 0

     For Each app As ChocolateyPackageList In apps
         InstallCommand = app.Install
         PackageName = app.Name

         Dim chocoOutput As List(Of ChocolateyPackage) = GetChocolateyResults(InstallCommand)

         ' Take first 3 results
         Dim firstThree = chocoOutput.Take(3).ToList()

         ' Serialize to JSON
         Dim jsonOptions As New JsonSerializerOptions With {
         .WriteIndented = True
     }

         Dim jsonString As String = JsonSerializer.Serialize(firstThree, jsonOptions)

         File.AppendAllText(outputPath, jsonString)

         i_ProgCount = i_ProgCount + 1
         ProgressBar1.Value = i_ProgCount
     Next


     Try
         ' 1) Read the entire file content
         Dim content As String = File.ReadAllText(outputPath)
         Dim contentrep As String

         contentrep = content.Replace("][", ",")

         ' 3) Write the updated content back to the file
         File.WriteAllText(outputPath, contentrep)

         i_ProgCount = i_ProgCount + 1
         ProgressBar1.Value = i_ProgCount

         Console.WriteLine("Replacement completed successfully.")

     Catch ex As Exception
         Console.WriteLine("Error: " &amp; ex.Message)

     End Try

     lbl_success.Visible = True


 End Sub   


Private Function GetChocolateyResults(ByVal InstallCommand As String) As List(Of ChocolateyPackage)
        Dim packages As New List(Of ChocolateyPackage)

        Dim psi As New ProcessStartInfo With {
            .FileName = "choco",
            .Arguments = "search --exact " &amp; InstallCommand &amp; " -a --approved-only --not-broken --limit-output",
            .RedirectStandardOutput = True,
            .UseShellExecute = False,
            .CreateNoWindow = True
        }

        Using proc As Process = Process.Start(psi)
            Using reader As StreamReader = proc.StandardOutput
                While Not reader.EndOfStream
                    Dim line As String = reader.ReadLine()

                    If line.Contains("|") Then
                        Dim parts = line.Split("|"c)
                        packages.Add(New ChocolateyPackage With {
                            .Install = parts(0),
                            .Version = parts(1)
                        })
                    End If
                End While
            End Using
        End Using

        Return packages
End Function
 Step 3: Choosing the software packages to be installed using ChocolateyChocolatey needs a packages.config file to know which packages should be installed during the creation of the master image. Example of a packages.config  file: &lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;packages&gt;
  &lt;package id="adobereader" version="2025.1.20577" /&gt;
  &lt;package id="googlechrome" version="143.0.7499.41" /&gt;
  &lt;package id="git.install" version="2.52.0" /&gt;
  &lt;package id="sysinternals" version="2025.11.17" /&gt;
  &lt;package id="winscp.install" version="6.5.5" /&gt;
  &lt;package id="teamviewer" version="15.72.6" /&gt;
  &lt;package id="dropbox" version="238.4.6075" /&gt;
&lt;/packages&gt;Based on the package list seen earlier, you can manually create your suitable package.config file, or you can use any other mechanism. For demonstration purposes, we created a self-service web application that lets you choose packages via a web frontend. The table with the packages and their versions is dynamically created out of the JSON file mentioned above:   Important You can host your packages in an internal repository if the machines do not have access to the Chocolatey repository on the Internet. Therefore, you can install a local Chocolatey server in your LAN. You can find the detailed configuration guide on the Chocolatey homepage. Example to add a CIFS share of a locally installed Chocolatey server as a valid source: PS C:\TMM&gt; choco source add -n="corporate-smb" -s="\\fileserver\chocorepo" --user="EXxXxXxX\XxXxXxXxXxXxXxXxX" --password="XxXxXxXxXxXxXxXxX" --priority="10" --allow-self-service="true"
Chocolatey v2.4.3
Added corporate-smb - \\fileserver\chocorepo (Priority 10)
PS C:\TMM&gt;Pressing the Modify Chocolatey configuration file button creates the package.config file seen above.  Step 4: Configuring Packer to create the master image and deploy the packagesWe have now defined everything we need – the base image Packer should use to create the master image VM, and the Chocolatey packages to be deployed. Let´s look at the workflow – the GitHub action part and the Ansible part will be discussed later in this guide:  Packer uses the HashiCorp Configuration Language (HCL) to define build templates for creating machine images. HCL is a human-readable, declarative language designed by HashiCorp for defining infrastructure and automation workflows.  It is used across HashiCorp tools like Terraform, Packer, and Vault because it balances readability for humans with machine-parsability. Key Features of HCL: Declarative syntax:  You describe what you want, not how to do it  Blocks and attributes:  Resources are defined in blocks with key-value pairs  Supports variables, interpolation, and conditionals Let´s look at a valid example of an HCL to create a Windows 11-based master image on Azure, deploy the software packages using Chocolatey, and upload the final image to an Azure Image Gallery – from there, you can, for example, create an image definition and image version in Citrix DaaS to deploy a Machine catalog. win11-azure.packer.hcl: # Defining all used variables
variable "azure_clientid" {
  type        = string
  description = "azure Service Principal App ID"
  sensitive   = true
}

variable "azure_clientsecret" {
  type        = string
  description = "azure Service Principal Secret"
  sensitive   = true
}

variable "azure_subscriptionid" {
  type        = string
  description = "azure Subscription ID"
  sensitive   = true
}

variable "azure_tenantid" {
  type        = string
  description = "azure Tenant ID"
  sensitive   = true
}

...

variable "vda_location" {
  type = string
}

variable "script_installvda" {
  type    = string
  default = "install-vda.ps1"
}

variable "script_optimizer" {
  type    = string
  default = "run-optimizer.ps1"
}

variable "script_cleanup" {
  type    = string
  default = "run-cleanup.ps1"
}

variable "location_setup" {
  type    = string
  default = "c:\\setup"
}

# Defining the used Packer Plugin(s)
packer {
  required_plugins {
    azure = {
      source  = "github.com/hashicorp/azure"
      version = "&gt;= 2.5.0"
    }
  }
}

# Defining the source(s) block(s)
source "azure-arm" "W11MIWithSWPackagesWithVDA" {
  # Tagging the Azure VM
  azure_tags = {
    environment        = var.azure_tag_environment,
    environment-entity = var.azure_tag_environment_entity,
    usage              = var.azure_tag_usage
  }

  # WinRM Communicator
  communicator   = "winrm"
  winrm_use_ssl  = true
  winrm_insecure = false
  winrm_timeout  = "5m"
  winrm_username = "packer"

  # Service Principal Authentication
  client_id       = var.azure_clientid
  client_secret   = var.azure_clientsecret
  subscription_id = var.azure_subscriptionid
  tenant_id       = var.azure_tenantid

  # Source Image
  os_type         = "Windows"
  image_publisher = var.azure_imgpublisher
  image_offer     = var.azure_imgoffer
  image_sku       = var.azure_imgsku
  image_version   = var.azure_imgversion

  # Destination Image - we want to upload the artifact directly into the Azure Image Gallery, so no creation of a stand-alone image is needed
  # managed_image_resource_group_name = var.azure_RG
  # managed_image_name                = var.azure_ManagedImgName

  # Store created Image in Shared Image Gallery 
  shared_image_gallery_destination {
    resource_group       = var.azure_rg
    gallery_name         = var.azure_sig_name
    image_name           = var.azure_sig_imgname
    image_version        = var.azure_sig_imgversion
    replication_regions  = ["austriaeast"]
    storage_account_type = "Standard_LRS"
  }

  # Packer Computing Resources
  build_resource_group_name = var.azure_temprg
  vm_size                   = var.azure_vmsize

}

# Defining the building blocks
build {
  source "azure-arm.W11MIWithSWPackagesWithVDA" {}

  # Install Chocolatey: https://chocolatey.org/install#individual
  provisioner "powershell" {
    inline = ["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'))"]
  }

  # Install Chocolatey packages
  provisioner "file" {
    source      = "./scripts/SWPackagesToInstall.config"
    destination = "${var.location_setup}/scripts/SWPackagesToInstall.config"
  }

  provisioner "powershell" {
    inline = ["choco install --ignore-hash --ignore-checksums --confirm ${var.location_setup}/scripts/SWPackagesToInstall.config"]
    # See https://docs.chocolatey.org/en-us/choco/commands/install#exit-codes
    valid_exit_codes = [0, 3, 1641, 3010]
  }

  provisioner "windows-restart" {}

  provisioner "powershell" {
    inline = ["New-Item -Path ${var.location_setup} -Force -ItemType \"directory\" | Out-Null"]
  }

  provisioner "file" {
    destination = "${var.location_setup}/${var.script_installvda}"
    source      = "./scripts/${var.script_installvda}"
  }

  provisioner "file" {
    destination = "${var.location_setup}/${var.script_optimizer}"
    source      = "./scripts/${var.script_optimizer}"
  }

  provisioner "powershell" {
    inline = ["${var.location_setup}/${var.script_installvda} -VdaSetupDownloadUri ${var.vda_location}"]
  }

  provisioner "windows-restart" {
    restart_check_command = "powershell -command \"&amp; {Write-Host 'Script: computer restarted.'}\""
    restart_timeout       = "30m"
  }

  provisioner "powershell" {
    inline = ["${var.location_setup}/${var.script_installvda} -VdaSetupDownloadUri ${var.vda_location}"]
  }

  provisioner "powershell" {
    inline = ["$assemblyPath = \"C:\\Program Files (x86)\\Citrix\\ICA Client\\wfica32.exe\"", "$ngen64 = \"C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\ngen.exe\"", "$ngen32 = \"C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319\\ngen.exe\"", "Write-Host \"Running 64-bit Ngen...\"", "&amp; $ngen64 executequeueditems", "Write-Host \"Running 32-bit Ngen...\"", "&amp; $ngen32 executequeueditems"]
  }

  provisioner "powershell" {
    inline = ["${var.location_setup}/${var.script_optimizer} -OptimizerDownloadUri ${var.optimizer_location} -Template ${var.optimizer_template}"]
  }

  # Generalize image using Sysprep
  # See https://www.packer.io/docs/builders/azure/arm#windows
  # See https://docs.microsoft.com/en-us/azure/virtual-machines/windows/build-image-with-packer#define-packer-template
  provisioner "powershell" {
    inline = [
      "while ((Get-Service RdAgent).Status -ne 'Running') { Start-Sleep -s 5 }",
      "while ((Get-Service WindowsazureGuestAgent).Status -ne 'Running') { Start-Sleep -s 5 }",
      "&amp; $env:SystemRoot\\System32\\Sysprep\\Sysprep.exe /oobe /generalize /quiet /quit /mode:vm",
      "while ($true) { $imageState = Get-ItemProperty HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State | Select ImageState; if($imageState.ImageState -ne 'IMAGE_STATE_GENERALIZE_RESEAL_TO_OOBE') { Write-Output $imageState.ImageState; Start-Sleep -s 10  } else { break } }"
    ]
  }
}The variables are defined in a key-value format - example: win11-azure.auto.pkrvars.hcl: azure_clientid       = &lt;sensitive&gt;
azure_clientsecret   = &lt;sensitive&gt;
azure_subscriptionid = &lt;sensitive&gt;
azure_tenantid       = &lt;sensitive&gt;
azure_rg             = "TMM-&lt;sensitive&gt;"
azure_temprg         = "TMM-&lt;sensitive&gt;"
azure_imgpublisher   = "MicrosoftWindowsDesktop"
azure_imgoffer       = "windows-11"
azure_imgsku         = "win11-25h2-pro"
azure_imgversion     = "26200.7462.251207"
...
vda_location         = "https://&lt;sensitive&gt;.blob.core.windows.net/ctxsw/VDAWorkstationSetup_2507.exe"
script_installvda    = "install-vda.ps1"
script_optimizer     = "run-optimizer.ps1"
script_cleanup       = "run-cleanup.ps1"
location_setup       = "c:\\ttemp"
optimizer_location   = "https://&lt;sensitive&gt;.blob.core.windows.net/ctxsw/CitrixOptimizerTool.zip"
optimizer_template   = "Citrix_Windows_11_2009.xml"Most Important The Image definition on the Azure Image Gallery  MUST exist before running Packer, otherwise the deployment will fail.  Command-line-based deploymentIf you want to start the creation process manually, you can use the well-known Packer command: azadmin@az-weur-devops-automation:/etc/terraform-tmm/PACKER/_AZURE$ packer build -on-error=cleanup -force -var-file="win11-azure.auto.pkrvars.hcl" win11-azure.pkr.hclPacker writes out all relevant information during the building process: ==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Running builder ...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Creating Azure Resource Manager (ARM) client ...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: ARM Client successfully created
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Getting source image id for the deployment ...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  -&gt; SourceImageName: '/subscriptions/&lt;sensitive&gt;/providers/Microsoft.Compute/locations/austriaeast/publishers/MicrosoftWindowsDesktop/ArtifactTypes/vmimage/offers/windows-11/skus/win11-25h2-pro/versions/26200.7462.251207'

...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Getting the VM's IP address ...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  -&gt; ResourceGroupName   : 'TMM-&lt;sensitive&gt;'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  -&gt; PublicIPAddressName : 'pkripgf6zyyvkul'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  -&gt; NicName             : 'pkrnigf6zyyvkul'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  -&gt; Network Connection  : 'PublicEndpoint'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  -&gt; IP Address          : '4.210.182.179'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Waiting for WinRM to become available...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: WinRM connected.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Connected to WinRM!
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Provisioning with Powershell...
...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Forcing web requests to allow TLS v1.2 (Required for requests to Chocolatey.org)
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Getting latest version of the Chocolatey package for download.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Not using proxy.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Getting Chocolatey from https://community.chocolatey.org/api/v2/package/chocolatey/2.6.0.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Downloading https://community.chocolatey.org/api/v2/package/chocolatey/2.6.0 to C:\Users\packer\AppData\Local\Temp\chocolatey\chocoInstall\chocolatey.zip
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Not using proxy.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Extracting C:\Users\packer\AppData\Local\Temp\chocolatey\chocoInstall\chocolatey.zip to C:\Users\packer\AppData\Local\Temp\chocolatey\chocoInstall
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Installing Chocolatey on the local machine
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Creating ChocolateyInstall as an environment variable (targeting 'Machine')
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:   Setting ChocolateyInstall to 'C:\ProgramData\chocolatey'
...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: c:\ttemp/scripts/SWPackagesToInstall.config
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: By installing, you accept licenses for the packages.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Installing the following packages:
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: adobereader
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: googlechrome
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: git.install
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: sysinternals
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: winscp.install
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: teamviewer
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: dropbox
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Downloading package from source 'https://community.chocolatey.org/api/v2/'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Progress: Downloading chocolatey-compatibility.extension 1.0.0... 100%

...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Downloading package from source 'https://community.chocolatey.org/api/v2/'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Progress: Downloading GoogleChrome 143.0.7499.41... 100%
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: GoogleChrome v143.0.7499.41[Approved]
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: GoogleChrome package files install completed. Performing other installation steps.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Downloading googlechrome 64 bit
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:   from 'https://dl.google.com/dl/chrome/install/googlechromestandaloneenterprise64.msi'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Download of googlechromestandaloneenterprise64.msi (-1 B) completed.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: WARNING: Ignoring checksums due to feature checksumFiles turned off or option --ignore-checksums set.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Installing googlechrome...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: googlechrome has been installed.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:   GoogleChrome may be able to be automatically uninstalled.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  The install of GoogleChrome was successful.

...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Chocolatey installed 13/13 packages.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  See the log for details (C:\ProgramData\chocolatey\logs\chocolatey.log).
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Installed:
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  - chocolatey-compatibility.extension v1.0.0
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  - chocolatey-core.extension v1.4.0
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  - chocolatey-dotnetfx.extension v1.0.1
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  - chocolatey-visualstudio.extension v1.13.0
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  - chocolatey-windowsupdate.extension v1.0.5
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  - adobereader v2025-1-20577
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  - filezilla v3.69.5
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  - GoogleChrome v143.0.7499.41
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  - git.install v2.52.0
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  - sysinternals v2025.11.17
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  - winscp.install v6.5.5
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  - teamviewer v15.72.6
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  - dropbox v238.4.6075
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Restarting Machine
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Waiting for machine to restart...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: A system shutdown is in progress.(1115)
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: pkrvmgf6zyyvkul restarted.
...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Uploading ./scripts/install-vda.ps1 =&gt; c:\ttemp/install-vda.ps1
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Uploading ./scripts/run-optimizer.ps1 =&gt; c:\ttemp/run-optimizer.ps1
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Provisioning with Powershell...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Provisioning with powershell script: C:\Users\packer\AppData\Local\Temp\powershell-provisioner3550365223
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 01:55:50 - Initiating Ctrix VDA installation.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 01:55:50 - Adding firewall allow rules for ports 80, 443, 1494, 2598, 8008
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 01:55:53 - Finished adding firewall allow rules for ports 80, 443, 1494, 2598, 8008
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 01:55:53 - Downloading VDASetup file from blob storage
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 01:56:05 - Executing: C:\Users\packer\AppData\Local\Temp\VDASetup.exe, Components: VDA
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 01:56:05 - Parameters: /quiet /noreboot /masterimage /enable_real_time_transport /enable_hdx_ports /components vda /includeadditional "Citrix MCS IODriver","Citrix VDA Upgrade Agent"
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 01:56:40 - XenDesktopVDA installation started
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 01:56:40 - Waiting few seconds to make sure the process extracts and starts
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 01:58:40 - Checking if XenDesktopVdaSetup process is running
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 01:58:40 - Installation process still running

...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 02:00:40 - Installation process completed
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 02:00:40 - GctRegistration already enabled
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 02:00:40 - BrokerAgent found and it is running.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 02:00:40 - Installation of XenDesktopVDA completed with success
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Restarting Machine
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Waiting for machine to restart...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: A system shutdown is in progress.(1115)
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: computer restarted.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: pkrvmgf6zyyvkul restarted.
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Machine successfully restarted, moving on

...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Provisioning with Powershell...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Provisioning with powershell script: C:\Users\packer\AppData\Local\Temp\powershell-provisioner2019159959
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 02:20:55 - Downloading Citrix Optimizer from blob storage
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 02:20:55 - Executing: C:\Users\packer\AppData\Local\Temp\CitrixOptimizer\CtxOptimizerEngine.ps1, Template: Citrix_Windows_11_2009.xml
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: ------------------------------
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: | Citrix Optimization Engine |
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: | Version 2.9                |
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: ------------------------------
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Running in execute mode
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Creating Logs folder 2025-12-12_14-20-55
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Starting session log
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Checking permissions
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Processing definition file C:\Users\packer\AppData\Local\Temp\CitrixOptimizer\Templates\Citrix_Windows_11_2009.xml

...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Script: 12-12-25 02:24:38 - Optimization script completed
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Provisioning with Powershell...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: IMAGE_STATE_COMPLETE

...

==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Querying the machine's properties ...

...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Powering off machine ...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Publishing to Shared Image Gallery ...

...

==&gt; azure-arm.W11MIWithSWPackagesWithVDA:  -&gt; Shared Gallery Image Version ID : '/subscriptions/&lt;sensitive&gt;/resourceGroups/TMM-&lt;sensitive&gt;/providers/Microsoft.Compute/galleries/&lt;sensitive&gt;/images/&lt;sensitive&gt;/versions/1.0.0'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Deleting Virtual Machine deployment and its attached resources...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Deleted -&gt; Microsoft.Compute/virtualMachines : 'pkrvmgf6zyyvkul'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Deleted -&gt; Microsoft.Network/networkInterfaces : 'pkrnigf6zyyvkul'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Deleted -&gt; Microsoft.Network/virtualNetworks : 'pkrvngf6zyyvkul'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Deleted -&gt; Microsoft.Network/publicIPAddresses : 'pkripgf6zyyvkul'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Deleted -&gt; Microsoft.Network/networkSecurityGroups : 'pkrsggf6zyyvkul'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Deleted -&gt; Microsoft.Compute/disks : '/subscriptions/&lt;sensitive&gt;/resourceGroups/TMM-sensitive/providers/Microsoft.Compute/disks/pkrosgf6zyyvkul'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Removing the created Deployment object: 'pkrdpgf6zyyvkul'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Deleting KeyVault created during build
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Deleted -&gt; Microsoft.KeyVault/vaults : 'pkrkvgf6zyyvkul'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Removing the created Deployment object: 'kvpkrdpgf6zyyvkul'
==&gt; azure-arm.W11MIWithSWPackagesWithVDA:
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: The resource group was not created by Packer, not deleting ...
Build 'azure-arm.W11MIWithSWPackagesWithVDA' finished after 54 minutes 36 seconds.

==&gt; Wait completed after 54 minutes 36 seconds

==&gt; Builds finished. The artifacts of successful builds are:
--&gt; azure-arm.W11MIWithSWPackagesWithVDA: Azure.ResourceManagement.VMImage:

OSType: Windows
ManagedImageSharedImageGalleryId: /subscriptions/&lt;sensitive&gt;/resourceGroups/TMM-&lt;sensitive&gt;/providers/Microsoft.Compute/galleries/&lt;sensitive&gt;/images/&lt;sensitive&gt;/versions/1.0.0
SharedImageGalleryResourceGroup: TMM-&lt;sensitive&gt;
SharedImageGalleryName: &lt;sensitive&gt;
SharedImageGalleryImageName: &lt;sensitive&gt;
SharedImageGalleryImageVersion: 1.0.0
SharedImageGalleryReplicatedRegions: austriaeastThe creation was completed successfully. Important Please make sure you know the return code for each software package's successful installation, as it varies by package. Usually, ‘0’ indicates a successful installation, but, for example, the return code for a successful VDA deployment is ‘3’ if a reboot is required. You need to enter the correct return codes for all packages in the corresponding configuration step: valid_exit_codes = [0, 3, 1641, 3010] If Packer encounters another code, it will raise an error, and the deployment will fail.  Note If some errors occur during the creation process, you might enable Packer to write verbose logs by using: azadmin@az-weur-devops-automation:/etc/terraform-tmm/PACKER/_AZURE$ export PACKER_LOG=1 azadmin@az-weur-devops-automation:/etc/terraform-tmm/PACKER/_AZURE$ export PACKER_LOG_PATH="./packer.log" or start the building process with verbose logging: azadmin@az-weur-devops-automation:/etc/terraform-tmm/PACKER/_AZURE$ packer build -on-error=cleanup -debug -force -var-file="win11-azure.auto.pkrvars.hcl" win11-azure.pkr.hcl Depending on the underlying load on Azure, the deployment process lasts some time - in our example, it took around 55 minutes. The respective Azure Gallery reflects the newly created image and image version:   We can now use this image version, for example, to generate the corresponding image on Citrix DaaS to create a Machine Catalog. Therefore, we use Terraform: #### Creating the Image Definition based on the Azure shared gallery image definition

resource "citrix_image_definition" "CreateAzureImageDefinition" {
  name                     = var.CC-Azure-ImgDef-Name
  description              = var.CC-Azure-ImgDef-Description
  os_type                  = "Windows"
  session_support          = "SingleSession"
  hypervisor               = citrix_azure_hypervisor.CreateAzureHostingConnection.id
  hypervisor_resource_pool = citrix_azure_hypervisor_resource_pool.CreateAzureHostingConnectionPool.id

  azure_image_definition = {
    resource_group     = var.TACG-TMM-ResourceGroup-Name
    use_image_gallery  = true
    image_gallery_name = var.CC-Azure-SIG-Name
  }
}

#### Creating the Image version based on the Azure shared gallery image version
resource "citrix_image_version" "CreateAzureImageVersion" {
  depends_on               = [citrix_image_definition.CreateAzureImageDefinition]
  image_definition         = citrix_image_definition.CreateAzureImageDefinition.id
  hypervisor               = citrix_azure_hypervisor.CreateAzureHostingConnection.id
  hypervisor_resource_pool = citrix_azure_hypervisor_resource_pool.CreateAzureHostingConnectionPool.id
  description              = var.CC-Azure-ImgVer-Description

  azure_image_specs = {
    service_offering = var.CC-Azure-Img-ServiceOffering
    storage_type     = var.CC-Azure-Img-StorageType
    resource_group   = var.TACG-TMM-ResourceGroup-Name
    gallery_image = {
      gallery    = var.CC-Azure-SIG-Name
      definition = var.CC-Azure-SIGDefinition-Name
      version    = var.CC-Azure-SIGDefinition-Version
    }
    machine_profile = {
      machine_profile_resource_group = var.CC-Azure-ImgVer-MachineProfile-RG
      machine_profile_vm_name        = var.CC-Azure-ImgVer-MachineProfile-VMName
    }
  }
}

resource "null_resource" "WriteProgress2" {
  depends_on = [citrix_image_version.CreateAzureImageVersion]
  provisioner "local-exec" {
    command = "echo The Image definition and Image version were successfully created..."
  }
}

#### Creating the Machine Catalog based on the Image version just created
resource "citrix_machine_catalog" "CreateAzureMCSCatalog" {
  depends_on        = [citrix_image_version.CreateAzureImageVersion]
  name              = var.CC-Azure-MC-Name
  description       = var.CC-Azure-MC-Description
  allocation_type   = var.CC-Azure-MC-AllocationType
  session_support   = var.CC-Azure-MC-SessionType
  provisioning_type = "MCS"
  #zone                = data.local_file.LoadZoneID.content
  zone = data.citrix_zone.GetTFAzureZoneID.id
  #scopes              = "${var.CC-GetScopeToBeUsed-Names}"

  provisioning_scheme = {
    hypervisor               = citrix_azure_hypervisor.CreateAzureHostingConnection.id
    hypervisor_resource_pool = citrix_azure_hypervisor_resource_pool.CreateAzureHostingConnectionPool.id
    identity_type            = "${var.CC-Azure-MC-IDPType}"

    machine_domain_identity = {
      domain                   = "${var.CC-Azure-MC-Domain}"
      domain_ou                = "${var.CC-Azure-MC-DomainOU}"
      service_account          = "${var.CC-Azure-MC-DomainAdmin-Username-UPN}"
      service_account_password = "${var.CC-Azure-MC-DomainAdmin-Password}"
    }

    azure_machine_config = {
      storage_type      = "Premium_LRS"
      use_managed_disks = true
      service_offering  = "${var.CC-Azure-MC-VMSize}"

      machine_profile = {
        machine_profile_resource_group = var.CC-Azure-ImgVer-MachineProfile-RG
        machine_profile_vm_name        = var.CC-Azure-ImgVer-MachineProfile-VMName
      }

      prepared_image = {
        image_definition = citrix_image_definition.CreateAzureImageDefinition.id
        image_version    = citrix_image_version.CreateAzureImageVersion.id
      }
    }
    number_of_total_machines = var.CC-Azure-MC-Machine_Count

    machine_account_creation_rules = {
      naming_scheme      = "${var.CC-Azure-MC-NamingScheme-Name}"
      naming_scheme_type = "${var.CC-Azure-MC-NamingScheme-Type}"
    }
  }
}The DaaS console reflects the newly created image:   Application-based deploymentYou can integrate the workflow into any application or IaC tool – in our example, we have configured GitHub Actions to start the corresponding flows via REST API calls to GitHub. As REST APIs are an industry standard, it should not be a problem to use them wherever you need. This demo application uses this functionality—each offered integration executes the appropriate REST API call, thereby starting the corresponding action on GitHub. Snippets from a screenshot of the demo application:  ...  How does a REST API call look? The syntax and functionality can easily be checked using Postman.  Example of a GitHub workflow: packer-build.yml name: Packer building a Master Image on Azure

on:
  push:
    branches: [ "main" ]
  workflow_dispatch:

jobs:
  packer-build:
    runs-on: ubuntu-latest
    env:
      PKR_VAR_Azure_ClientID: ${{ secrets.AZURE_CLIENTID }}
      PKR_VAR_Azure_ClientSecret: ${{ secrets.AZURE_CLIENTSECRET }}
      PKR_VAR_Azure_SubscriptionID: ${{ secrets.AZURE_SUBSCRIPTIONID }}
      PKR_VAR_Azure_TenantID: ${{ secrets.AZURE_TENANTID }}
      PKR_VAR_Azure_RG: ${{ secrets.AZURE_RG }}
      PKR_VAR_Azure_TempRG: ${{ secrets.AZURE_TempRG }}

      PACKER_LOG: "1"
      PACKER_LOG_PATH: "packer.log"

    steps:
      - name: Checkout repo
        uses: actions/checkout@v4

      - name: Packer init
        run: packer init ./packer

      - name: Packer validate
        run: packer validate -var-file "./packer/win11-azure.auto.pkrvars.hcl" ./packer/win11-azure.pkr.hcl

      - name: Packer build
        run: packer build -on-error=abort -force -var-file="./packer/win11-azure.auto.pkrvars.hcl" ./packer/win11-azure.pkr.hclImportant Do not store sensitive information hard-coded in the GitHub files. Store them securely in GitHub´s vault and reference them in your .yml files. Sample screenshot:  GitHub records each workflow run and all its steps, so you can easily check whether your flow is working:  The log output is the same as in PowerShell we saw earlier: Run packer build -on-error=abort -force -var-file="./packer/win11-azure.auto.pkrvars.hcl" ./packer/win11-azure.pkr.hcl 
packer build -on-error=abort -force -var-file="./packer/win11-azure.auto.pkrvars.hcl" ./packer/win11-azure.pkr.hcl 
shell: /usr/bin/bash -e {0} 
env: 
PKR_VAR_Azure_ClientID: *** 
PKR_VAR_Azure_ClientSecret: *** 
PKR_VAR_Azure_SubscriptionID: *** 
PKR_VAR_Azure_TenantID: *** 
PKR_VAR_Azure_RG: *** 
PKR_VAR_Azure_TempRG: *** 
PACKER_LOG: 1 
PACKER_LOG_PATH: packer.log 
azure-arm.W11MIWithSWPackagesWithVDA: output will be in this color. 
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Running builder ... 
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: Creating Azure Resource Manager (ARM) client ... 
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: ARM Client successfully created
...
...
==&gt; azure-arm.W11MIWithSWPackagesWithVDA: The resource group was not created by Packer, not deleting ... 
Build 'azure-arm.W11MIWithSWPackagesWithVDA' finished after 56 minutes 11 seconds. 
==&gt; Wait completed after 56 minutes 11 seconds 
==&gt; Builds finished. The artifacts of successful builds are: 
--&gt; azure-arm.W11MIWithSWPackagesWithVDA: Azure.ResourceManagement.VMImage: 
OSType: Windows 
ManagedImageResourceGroupName: *** 
ManagedImageName: *** 
ManagedImageId: /subscriptions/&lt;sensitive&gt;/resourceGroups/***/providers/Microsoft.Compute/images/*** 
ManagedImageLocation: austriaeast Step 5: Adding AnsibleIf Chocolatey is not suitable/capable of fulfilling all software deployment needs, we can use Ansible to fill in the gaps. As Ansible is another important IaC tool, we follow our strict IaC strategy. Using Ansible, you can also install Windows features during the creation process – let´s look at some examples: It is very easy to add Ansible to Packer - just configure Packer to use the Ansible plugin and use the Ansible provisioner block in your HCL code: ...
required_plugins {
    ansible = {
      version = "~&gt; 1"
      source = "github.com/hashicorp/ansible"
    }
  }
...  provisioner "ansible" {
    playbook_file   = "ansible/windows.yml"
    extra_arguments = [
      "-e", "ansible_user=Administrator",
      "-e", "ansible_password={{user `winrm_password`}}",
      "-e", "ansible_connection=winrm",
      "-e", "ansible_winrm_transport=basic",
      "-e", "ansible_winrm_server_cert_validation=ignore"
    ]
  }
Adding the necessary changes to our Packer HCL snippet used above would result in this: # Defining all used variables
variable "azure_clientid" {
  type        = string
  description = "azure Service Principal App ID"
  sensitive   = true
}

variable "azure_clientsecret" {
  type        = string
  description = "azure Service Principal Secret"
  sensitive   = true
}

variable "azure_subscriptionid" {
  type        = string
  description = "azure Subscription ID"
  sensitive   = true
}

variable "azure_tenantid" {
  type        = string
  description = "azure Tenant ID"
  sensitive   = true
}

...
variable "ansible_user" { 
  type = string 
  description = "Ansible User"
  sensitive   = true
}

variable "ansible_password" { 
  type = string
  description = "Ansible Users Password"
  sensitive   = true
}

variable "ansible_connection" { 
  type = string
  description = "How to connect to Ansible - winrm"  
}

variable "ansible_transport"  { 
  type = string
  description = "How to connect to Ansible - SecureOrBasic"     
} 

...
variable "vda_location" {
  type = string
}

variable "script_installvda" {
  type    = string
  default = "install-vda.ps1"
}

variable "script_optimizer" {
  type    = string
  default = "run-optimizer.ps1"
}

variable "script_cleanup" {
  type    = string
  default = "run-cleanup.ps1"
}

variable "location_setup" {
  type    = string
  default = "c:\\setup"
}

# Variables defining package URLs for Ansible
variable "msi_url_7zip" {
  type        = string
  default     = "https://www.7-zip.org/a/7z2402-x64.msi"
  description = "Example MSI package URL"
}

variable "exe_url_vcredist" {
  type        = string
  default     = "https://download.visualstudio.microsoft.com/download/pr/e9.../vcredist_x64.exe"
  description = "Example EXE package URL (silent flags required)"

# Defining the used Packer Plugin(s)
packer {
  required_plugins {
    azure = {
      source  = "github.com/hashicorp/azure"
      version = "&gt;= 2.5.0"
    }
    ansible = {
      source  = "github.com/hashicorp/ansible"
      version = "~&gt; 1"
    }
  }
}

# Defining the source(s) block(s)
source "azure-arm" "W11MIWithSWPackagesWithVDA" {
  # Tagging the Azure VM
  azure_tags = {
    environment        = var.azure_tag_environment,
    environment-entity = var.azure_tag_environment_entity,
    usage              = var.azure_tag_usage
  }

  # WinRM Communicator
  communicator   = "winrm"
  winrm_use_ssl  = true
  winrm_insecure = false
  winrm_timeout  = "5m"
  winrm_username = "packer"

  # Service Principal Authentication
  client_id       = var.azure_clientid
  client_secret   = var.azure_clientsecret
  subscription_id = var.azure_subscriptionid
  tenant_id       = var.azure_tenantid

  # Source Image
  os_type         = "Windows"
  image_publisher = var.azure_imgpublisher
  image_offer     = var.azure_imgoffer
  image_sku       = var.azure_imgsku
  image_version   = var.azure_imgversion

  # Destination Image - we want to upload the artifact directly into the Azure Image Gallery, so no creation of a stand-alone image is needed
  # managed_image_resource_group_name = var.azure_RG
  # managed_image_name                = var.azure_ManagedImgName

  # Store created Image in Shared Image Gallery 
  shared_image_gallery_destination {
    resource_group       = var.azure_rg
    gallery_name         = var.azure_sig_name
    image_name           = var.azure_sig_imgname
    image_version        = var.azure_sig_imgversion
    replication_regions  = ["austriaeast"]
    storage_account_type = "Standard_LRS"
  }

  # Packer Computing Resources
  build_resource_group_name = var.azure_temprg
  vm_size                   = var.azure_vmsize

}

# Defining the building blocks
build {
  source "azure-arm.W11MIWithSWPackagesWithVDA" {}

  # Install Chocolatey: https://chocolatey.org/install#individual
  provisioner "powershell" {
    inline = ["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'))"]
  }

  # Install Chocolatey packages
  provisioner "file" {
    source      = "./scripts/SWPackagesToInstall.config"
    destination = "${var.location_setup}/scripts/SWPackagesToInstall.config"
  }

  provisioner "powershell" {
    inline = ["choco install --ignore-hash --ignore-checksums --confirm ${var.location_setup}/scripts/SWPackagesToInstall.config"]
    # See https://docs.chocolatey.org/en-us/choco/commands/install#exit-codes
    valid_exit_codes = [0, 3, 1641, 3010]
  }

  provisioner "windows-restart" {}

  provisioner "ansible" {
    playbook_file = "./scripts/ConfigW11.yml"
    groups        = ["azure"]
    user          = var.ansible_user
    extra_arguments = [
      "-e", "ansible_connection=${var.ansible_connection}",
      "-e", "ansible_user=${var.ansible_user}",
      "-e", "ansible_password=${var.ansible_password}",
      "-e", "ansible_winrm_transport=${var.ansible_transport}",
      "-e", "ansible_winrm_server_cert_validation=ignore",
      "-e", "msi_url_7zip=${var.msi_url_7zip}",
      "-e", "exe_url_vcredist=${var.exe_url_vcredist}"
    ]
    use_proxy = false
  }

  provisioner "powershell" {
    inline = ["New-Item -Path ${var.location_setup} -Force -ItemType \"directory\" | Out-Null"]
  }

  provisioner "file" {
    destination = "${var.location_setup}/${var.script_installvda}"
    source      = "./scripts/${var.script_installvda}"
  }

  provisioner "file" {
    destination = "${var.location_setup}/${var.script_optimizer}"
    source      = "./scripts/${var.script_optimizer}"
  }

  provisioner "powershell" {
    inline = ["${var.location_setup}/${var.script_installvda} -VdaSetupDownloadUri ${var.vda_location}"]
  }

  provisioner "windows-restart" {
    restart_check_command = "powershell -command \"&amp; {Write-Host 'Script: computer restarted.'}\""
    restart_timeout       = "30m"
  }

  provisioner "powershell" {
    inline = ["${var.location_setup}/${var.script_installvda} -VdaSetupDownloadUri ${var.vda_location}"]
  }

  provisioner "powershell" {
    inline = ["$assemblyPath = \"C:\\Program Files (x86)\\Citrix\\ICA Client\\wfica32.exe\"", "$ngen64 = \"C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\ngen.exe\"", "$ngen32 = \"C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319\\ngen.exe\"", "Write-Host \"Running 64-bit Ngen...\"", "&amp; $ngen64 executequeueditems", "Write-Host \"Running 32-bit Ngen...\"", "&amp; $ngen32 executequeueditems"]
  }

  provisioner "powershell" {
    inline = ["${var.location_setup}/${var.script_optimizer} -OptimizerDownloadUri ${var.optimizer_location} -Template ${var.optimizer_template}"]
  }

  # Generalize image using Sysprep
  # See https://www.packer.io/docs/builders/azure/arm#windows
  # See https://docs.microsoft.com/en-us/azure/virtual-machines/windows/build-image-with-packer#define-packer-template
  provisioner "powershell" {
    inline = [
      "while ((Get-Service RdAgent).Status -ne 'Running') { Start-Sleep -s 5 }",
      "while ((Get-Service WindowsazureGuestAgent).Status -ne 'Running') { Start-Sleep -s 5 }",
      "&amp; $env:SystemRoot\\System32\\Sysprep\\Sysprep.exe /oobe /generalize /quiet /quit /mode:vm",
      "while ($true) { $imageState = Get-ItemProperty HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State | Select ImageState; if($imageState.ImageState -ne 'IMAGE_STATE_GENERALIZE_RESEAL_TO_OOBE') { Write-Output $imageState.ImageState; Start-Sleep -s 10  } else { break } }"
    ]
  }
}Note The Ansible playbook listed below is intended solely to demonstrate Ansible's capabilities for installing various software packages and configuring Windows components and settings: It downloads and installs 2 Software packages as examples - an EXE and a MSI. It sets some registry keys. It enables the Windows Firewall and sets a firewall rule. It configures further Windows settings and installs the Windows IIS feature. Please see it as an example and tailor it according to your needs. ConfigW11.yml: ---
- name: Install packages, configure settings, and enable features
  hosts: all
  gather_facts: no

  vars:
    msi_url_7zip: "{{ msi_url_7zip }}"
    exe_url_vcredist: "{{ exe_url_vcredist }}"
    msi_dest: "C:\\Windows\\Temp\\7zip.msi"
    exe_dest: "C:\\Windows\\Temp\\vcredist_x64.exe"

  tasks:
    - name: Download MSI (7-Zip)
      ansible.windows.win_get_url:
        url:  "{{ msi_url_7zip }}"
        dest: "{{ msi_dest }}"

    - name: Install MSI (7-Zip) silently
      ansible.windows.win_package:
        path: "{{ msi_dest }}"
        state: present

    - name: Download EXE (VC++ Redistributable)
      ansible.windows.win_get_url:
        url:  "{{ exe_url_vcredist }}"
        dest: "{{ exe_dest }}"

    - name: Install EXE silently
      ansible.windows.win_package:
        path: "{{ exe_dest }}"
        arguments: "/quiet /norestart"  
        state: present

    - name: Enable long Win32 paths (Registry)
      ansible.windows.win_regedit:
        path: HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem
        name: LongPathsEnabled
        data: 1
        type: dword
        state: present

    - name: Ensure firewall profiles are enabled
      ansible.windows.win_firewall:
        state: enabled
        profiles:
          - Domain
          - Private
          - Public

    - name: Allow inbound RDP on TCP 3389 (rule)
      community.windows.win_firewall_rule:
        name: "Allow RDP Inbound"
        localport: 3389
        protocol: tcp
        direction: in
        action: allow
        state: present
        enabled: true

    - name: Ensure Windows Update service is running
      ansible.windows.win_service:
        name: wuauserv
        start_mode: auto
        state: started

    - name: Grant 'Log on as a service' to local account
      ansible.windows.win_user_right:
        name: SeServiceLogonRight
        users:
          - ".\\Administrator"
        action: add

    - name: Install IIS Web-Server + management console
      ansible.windows.win_feature:
        name:
          - Web-Server
          - Web-Mgmt-Console
        state: present
        include_management_tools: true

    - name: Reboot if feature install requires it
      ansible.windows.win_reboot:
      when: ansible_windows_feature_result is defined
            and ansible_windows_feature_result.reboot_required | default(false)Packer would now sequentially run the following tasks while creating the Windows-based Master image: Create all necessary temporary resources needed on Azure - these will be automatically deleted after completion if the on-abort parameter is set according to your needs. Create the VM based on your configuration needs. Install the chosen Windows OS. Install all Chocolatey-based software packages. Install and configure all software packages, Windows components, and Windows settings using Ansible. Install the Citrix VDA. Run the Citrix Optimizer script. Running sysprep to generalize the image. Upload the image to your Azure Image Gallery for further use.  SummaryUsing some or all of the provided steps should help create a modern image factory and therefore ease image creation: Packer’s superpower is simple: one template, many platforms. It turns a single-source configuration into identical machine images across clouds and hypervisors—your classic golden-image pipeline. That unlocks faster autoscaling (instances boot already configured), reduces runtime bootstrapping, and enforces consistency you can track in version control. In short, Packer standardizes and automates image creation, so your “master” isn’t a snowflake—it’s a known artifact. Windows lacks a native, universal package workflow across third‑party apps. Chocolatey fills that gap: it wraps installers into versioned packages, automates deployments, and integrates with enterprise tooling, so your image build and fleet updates don’t rely on ad‑hoc downloads and silent‑switch folklore. Using Chocolatey inside your Ansible/Packer pipeline brings Linux‑like package discipline to Windows—uniform installs, consistent updates, and auditable sources. With Ansible, you prevent configuration drift across fleets and reruns. Ansible’s Windows story is mature: it manages packages (MSI/EXE), services, the registry, the firewall, and IIS with supported modules—agentless over WinRM or SSH—so you can enforce consistent settings at image build time and again at runtime when needed. Treating images and configurations as code means they live in Git, go through pull requests, can be tested, diffed, and rolled back. IaC turns point‑and‑click “ClickOps” into repeatable pipelines with audit trails, security baselines, and policy checks. It also fixes the economics: cloud infrastructure is elastic and API‑driven, so the only scalable, safe way to manage it is declarative, versioned automation. IaC also gives you the best of both worlds: immutable (pre‑baked) images for speed and reliability, plus config management for last‑mile adjustments. Packer builds the artifact; Ansible and Chocolatey ensure what’s inside is correct and stays that way. The result is faster provisioning, fewer surprises, and environments that match across dev, staging, and prod.  DisclaimerDisclaimer EXCEPT WHERE EXPRESSLY PROVIDED OTHERWISE BY CLOUD SOFTWARE GROUP, THE CONTENT IN THIS DOCUMENT IS PROVIDED “AS IS” AND CLOUD SOFTWARE GROUP HEREBY DISCLAIMS ALL EXPRESS OR IMPLIED REPRESENTATIONS, WARRANTIES, GUARANTIES, AND CONDITIONS, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES OR CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD TO BE LEGALLY INVALID. CLOUD SOFTWARE GROUP MAKES NO REPRESENTATIONS, WARRANTIES, GUARANTIES, OR CONDITIONS AS TO THE QUALITY, SUITABILITY, TRUTH, ACCURACY, OR COMPLETENESS OF ANY OF THE CONTENT CONTAINED ON THE WEBSITE. The above disclaimer applies to any damages, liability, or injuries caused by any failure of performance, error, omission, interruption, deletion, defect, delay in operation or transmission, computer virus, communication line failure, theft or destruction of or unauthorized access to, alteration of, or use, whether for breach of contract, tort, negligence or any other cause of action.]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2026_01/flow-packer-chocolatey.png.f8008db9df40a4582e2b0ca3c6ab2187.png" length="115040" type="image/png"/><pubDate>Thu, 08 Jan 2026 11:40:33 +0000</pubDate></item><item><title>Deployment Guide: Using Infrastructure-as-Code for deploying Citrix&#xAE; Virtual Apps and Desktops&#x2122; 2507 LTSR on vSphere 8</title><link>https://community.stage.citrix.com/tech-zone/automation/iac-cvad2507-vsphere8/</link><description><![CDATA[Using Infrastructure-as-Code for deploying Citrix® Virtual Apps and Desktops™ 2507 LTSR on vSphere 8 Citrix’s goal is to help customers automate their environments with minimal friction. We support multiple automation interfaces, REST APIs, PowerShell SDKs, Terraform providers, Ansible playbooks, and all current CI/CD frameworks, so customers can choose the tools that fit their workflow. Automation enhances Citrix’s ability to deliver repeatable, scalable, and secure deployments across hybrid and multi-cloud environments. It supports Citrix’s broader goals of reducing time-to-value, improving customer satisfaction, and deeply integrating into customer ecosystems. IntroductionIn this guide, we cover the usage of three distinct Infrastructure-as-Code (IaC) frameworks to deploy a Citrix Virtual Desktops 2507 LTSR-based deployment on a vSphere 8 cluster. The instructions are divided into blocks that must be started in a specific, predetermined order. However, it is not necessary to start all blocks in one go—the dependencies on previous blocks are recorded during runtime, so that essential time intervals or even execution stops between the individual blocks are not a problem.   Components and frameworks used in this guideLet´s have a short overview of all the components and frameworks used in this guide. We use Hashicorp Packer as the framework for generating the Master Images and templates for deploying all needed Windows-based virtual machines on XenServer 8.4, Hashicorp Terraform as the backbone and “coordinating” framework for creating all needed Infrastructure components, and Ansible as the configuration framework for all configuration needs of the components that Terraform cannot do during the initial creation of the respective element. vSphere 8VMware vSphere 8 is a powerful hypervisor that provides a stable, scalable platform for Citrix Virtual Apps and Desktops. It combines modern virtualization features with optimized performance, high security, and broad Citrix support, providing businesses with a future-proof foundation for their virtual desktop infrastructure (VDI). With VMware vSphere 8 as the hypervisor for Citrix Virtual Apps and Desktops, organizations gain a future-proof, scalable, and secure platform for their VDI strategy. The combination enables high performance, flexible scaling, and easy management while improving the end-user experience and meeting compliance requirements. Hashicorp TerraformTerraform is one of the most widely adopted Infrastructure-as-Code (IaC) platforms for provisioning and managing cloud and on-prem infrastructure at scale. Its declarative configuration model, strong ecosystem support, and provider-agnostic architecture make it a foundational tool for organizations seeking consistency, compliance, and operational maturity in their infrastructure lifecycle.  Terraform relies on declarative code to define infrastructure in a predictable, reproducible manner. By representing compute, networking, storage, IAM, and platform services as version-controlled code artifacts, Terraform: Eliminates configuration drift Ensures consistent provisioning across development, staging, and production Produces deployments that can be rebuilt deterministically This consistency is particularly valuable in regulated environments where documentation, repeatability, and environmental fidelity are required for audits and certification processes. For highly regulated verticals—such as finance, healthcare, government, and critical infrastructure—Terraform provides the governance, repeatability, and auditability required to meet stringent compliance obligations. These regulated verticals often operate under strict frameworks, such as: Finance: PCI-DSS, FFIEC, SOX Healthcare: HIPAA, HITECH Government/Public Sector: FedRAMP, NIST 800-53, DoD SRG Energy/Critical Infrastructure: NERC CIP Global Privacy Requirements: GDPR, regional data sovereignty mandates Terraform helps organizations meet these obligations by providing: Complete Traceability and Auditability: All infrastructure changes can be captured in version control systems. This enables traceable change histories, approvals, and rollbacks—supporting SOX, NIST, and ISO 27001 requirements around change management and configuration governance. Standardized, Repeatable Environments: Terraform modules enable organizations to define compliant “building blocks” that development teams cannot inadvertently modify, ensuring every environment meets baseline controls for security, networking, identity, and data protection. Operational Efficiency and Reduced Risk: Terraform’s execution plan and state management capabilities allow organizations to preview changes before applying them, reducing misconfigurations—a key risk driver in regulated environments. Additional operational benefits include: Automated provisioning at scale Predictable change outcomes through planning and applying workflows Reduced human error, a significant source of compliance violations Consistent rollback and recovery patterns Integration With Secure DevOps and Platform Engineering Practices Terraform fits seamlessly into CI/CD, GitOps, and automated compliance frameworks, enabling regulated organizations to adopt modern DevOps practices without compromising governance.  It perfectly integrates with frameworks such as: Terraform Cloud/Enterprise for controlled workflows Vault for secure secrets injection Packer for master image pipelines GitHub Actions, GitLab, Azure DevOps for automated deployment Hashicorp PackerHashiCorp Packer is a foundational tool for organizations seeking to standardize, automate, and scale the creation of master images across multi-cloud and hybrid environments. As organizations increasingly adopt Infrastructure-as-Code (IaC) approaches, Packer provides a consistent, version-controlled, and repeatable way to build golden images aligned with modern DevOps and platform engineering practices. Standardization and Consistency Across Environments: Packer enables the creation of identically configured images for AWS, Azure, GCP, VMware, and other platforms from a single source template. This eliminates configuration drift and ensures that compute instances, virtual desktops, containers, or on-prem VMs are consistently built from the same baseline. For enterprises managing multiple environments, Packer’s multi-builder architecture ensures that every deployment starts from a deterministic, verified image. Full Infrastructure-as-Code Alignment: Packer templates are declarative and integrate seamlessly into IaC workflows using tools such as Terraform, Ansible, and even cloud-native configuration tools. This yields several architectural advantages: Versioned images: Each new image build is immutable and tied to a version-controlled configuration. Reproducibility: Builds can be recreated deterministically at any time, supporting compliance and audit requirements. Automation-ready: Image creation fits naturally into CI/CD pipelines, enabling continuous delivery of operating system and middleware baselines.  Efficient, Scalable Deployments: Master images generated by Packer accelerate workload provisioning by drastically reducing runtime bootstrap configuration. This leads to: Faster autoscaling responsiveness Reduced risk of configuration failures during provisioning Decreased cloud compute costs due to shorter initialization times Better operational consistency across development, staging, and production These benefits scale exceptionally well for enterprise cloud deployments, VDI environments, and large-scale microservice platforms.  Multi-Cloud Flexibility and Future-Proofing Packer’s provider-agnostic architecture helps organizations avoid lock-in and stay adaptable. A single template can create AMIs, Azure Managed Images, GCP images, Docker images, and VMware templates. This uniformity supports long-term cloud strategy, modernization efforts, and disaster recovery architectures that depend on identical images across regions and providers.  Integration with Modern Platform Engineering Practices Packer integrates tightly with modern platform engineering toolchains: Terraform for downstream deployment Vault for secure secrets handling GitHub Actions, GitLab CI, Azure DevOps for automated pipelines This positions Packer as a first-class component in automated golden image pipelines, enabling platform teams to provide self-service, pre-validated, compliant images to developers and operations teams. Packer is a mature, efficient, and cloud-agnostic solution that brings the rigor of Infrastructure-as-Code to master image creation. It ensures repeatability, security, compliance, and operational speed while integrating seamlessly into modern CI/CD and platform engineering ecosystems. For organizations pursuing scalable, automated, and consistent image-based deployments, Packer is not only valid but strategically advantageous. Packer is an open-source tool that automates the creation of machine images for multiple platforms from a single source configuration. It enables developers and DevOps teams to define image-creation workflows in code, resulting in consistent, repeatable, and version-controlled image builds. RedHat AnsibleMany organizations that have matured their Infrastructure-as-Code (IaC) practices increasingly require tooling that not only provisions infrastructure but also configures systems, applications, and operating environments in a controlled, repeatable manner. Ansible is a leading configuration management and automation platform that aligns naturally with IaC principles and complements provisioning tools such as Terraform, or cloud-native IaC engines. Its agentless architecture, declarative playbook model, and extensive ecosystem make it a strategic choice for enterprises seeking predictable, scalable, and compliant post-provision configuration. Ansible provides a declarative, idempotent configuration model that ensures systems converge to a desired state regardless of their current configuration. This makes it ideally suited for environments provisioned using IaC tools, allowing organizations to: Configure compute resources, operating systems, middleware, and applications immediately after provisioning Apply consistent baseline templates across development, staging, and production Avoid configuration drift and reduce manual intervention Enforce standardization across diverse platforms and environments Because Ansible uses standard protocols (SSH, WinRM, API calls) and does not require agents, it can manage virtually any resource created by IaC tooling with minimal setup. Ansible excels by enabling: OS configuration (packages, services, permissions, patching) Deployment and maintenance of middleware or applications Secure configuration of agents (monitoring, logging, EDR, secrets managers) Post-provision checks and compliance enforcement Continuous updates and lifecycle operations long after initial provisioning Ansible delivers operational benefits such as: Idempotent reruns that safely correct drift Automated configuration updates Consistent rollout of application versions or patches Reduced human error and faster recovery Centralized configuration logic rather than ad-hoc scripts These capabilities support long-term environment stability, which is often a weak point in IaC deployments if configuration is not managed separately. Better togetherTerraform and Ansible serve complementary roles in modern Infrastructure-as-Code (IaC) architectures. Terraform excels at provisioning infrastructure resources—compute, networks, storage, identity, and cloud services—while Ansible specializes in configuring and managing the software, applications, and operating system state on those resources once they are created. When combined, they form a robust, scalable, and fully automated delivery pipeline that allows organizations to provision secure, consistent environments and configure them in a modular, policy-driven manner. Terraform is primarily designed for infrastructure lifecycle management, while Ansible is engineered for post-provisioning configuration. This clean separation ensures: Terraform builds and destroys infrastructure predictably Ansible configures servers, middleware, application stacks, and OS settings Changes to infrastructure and configuration are maintained in distinct codebases, improving clarity and reducing risk IT can version, audit, and test both layers independently This division of responsibilities aligns with architectural patterns used in platform engineering and regulated environments, where infrastructure and system configuration often have separate compliance requirements. Schematic workflow in this guide:   PrerequisitesLet´s have a short overview of all the prerequisites needed: IaC Deployment MachineWe assume you have a dedicated machine or a cluster running all Automation components. If all IaC frameworks reside on the same machine/cluster, communication between them is less error-prone. We recommend an Ubuntu-based machine for various reasons: An Ubuntu VM is one of the most reliable, flexible, and well-supported environments for running IaC tools like Terraform, Ansible, Jenkins, and Packer, thanks to its stability, compatibility, and strong community support. Most IaC tools (Terraform, Ansible, Packer) are developed and tested primarily on Linux. Note: You can find a guide to create such an all-in-one deployment machine on Citrix TechZone. If you use the Ubuntu-based IaC machine, all Terraform, Packer, and Ansible snippets are running on Ubuntu. All local-exec provisioners also run on the Ubuntu machine: Example of a local-exec provisioner: ##### Connect to Ansible Interpreter and run CreateDatabase-Playbook on DDCs
resource "null_resource" "RunCreateDatabasePlaybook" {
  depends_on = [time_sleep.wait_10_seconds]

  provisioner "local-exec" {
    working_dir = "/etc/ansible"
    command     = var.TACG-TMM-XS-CL-Ansible-CreateDatabase-CMD
  }
} Caution: Due to current limitations in Citrix StoreFront´s automation capabilities, you must run the automated deployment and configuration of Citrix StoreFront™ from a domain-joined Windows machine with Terraform installed.  Important: Suppose you decide to switch to running all Terraform snippets on a Windows machine. In that case, you need to change the provisioner types and the code from local-exec provisioners to remote-exec provisioners, as Ansible cannot run on a Windows machine! Example of the local-exec provisioner mentioned above - converted to a remote-exec provisioner: ##### Connect to Ansible Interpreter and run CreateDatabase-Playbook on DDCs
resource "null_resource" "RunCreateDatabasePlaybook" {
  depends_on = [time_sleep.wait_10_seconds]
  
 connection {
    type            = var.Provisioner_Type
    user            = var.Provisioner_SecureAdmin-Username
    password        = var.Provisioner_SecureAdmin-Password
    host            = var.Provisioner_IP1
    port            = var.Provisioner_Port
    https           = var.Provisioner_HTTPS
    timeout         = var.Provisioner_Timeout
  }

  provisioner "remote-exec" {
    inline     = [ var.TACG-TMM-XS-CL-Ansible-CreateDatabase-CMD ]
  }
}You can find instructions for setting up the necessary WinRM/SSH configuration later in this guide.  vSphere 8We assume that you already have a vSphere 8 cluster in place, which will serve as the Hypervisor host for all required virtual machines.   Important: There is no need to have template VMs or template snapshots configured if you want to follow our IaC-based approach to create and configure all needed Master images using Packer. If you do not want to use Packer but have already configured Master images, you can use them and reference them in the corresponding Terraform variables later.  Communication Flow between Terraform, Ansible, and the Target MachinesAll communication between Terraform, Packer, and Ansible on the IaC-VM and the target VMs on the XenServer cluster uses SSH and WinRM. WinRM (Windows Remote Management) is a Microsoft protocol that enables remote management of Windows systems by allowing access to remote computers to perform management tasks. It is Microsoft's implementation of the WS-Management (Web Services Management Protocol) standard. It often uses SOAP (Simple Object Access Protocol) for communication over HTTP or HTTPS, using ports 5985 (HTTP) and 5986 (HTTPS) by default. WinRM provides the basis for remote management with PowerShell and is used for tasks such as running remote scripts, automating, configuring, and performing system inventory. Therefore, WinRM and SSH must be configured correctly. Important: We emphasize ensuring the communication flow works as intended from the outset of the pre-domain-joined stage; otherwise, Terraform and Ansible will fail to configure the VMs.  During the pre-domain-join stage, you can configure these settings using the local Group Policy Editor on the VM or in the autounattend.xml file as we do in our Packer scripts. After a successful domain join, you can set all relevant settings using GPOs. To provide maximum flexibility and a fallback mechanism, you can use both listeners - HTTP and HTTPS - but we recommend using the secure transport whenever and wherever possible. You can check all available listeners using PowerShell:  PS &gt; winrm e winrm/config/listener
Listener [Source="GPO"]
    Address = *
    Transport = HTTP
    Port = 5985
    Hostname
    Enabled = true
    URLPrefix = wsman
    CertificateThumbprint
    ListeningOn = 10.10.119.31, 127.0.0.1, ::1, fe80::287c:216e:9bd9:18e5%7

Listener [Source="GPO"]
    Address = *
    Transport = HTTPS
    Port = 5986
    Hostname = tf-cvad-v-ddc1.the-austrian-citrix-guy.at
    Enabled = true
    URLPrefix = wsman
    CertificateThumbprint = e3bf89dd77c95dd6c0f1b94480ea63782df4279e
    ListeningOn = 10.10.119.31, 127.0.0.1, ::1, fe80::287c:216e:9bd9:18e5%7To check the communication, you can use a Terraform snippet: Unsecure Communication - connecting to the HTTP listener: resource "null_resource" "UploadTestFileToIP1" {
 connection {
    type            = var.Provisioner_Type
    user            = var.Provisioner_Admin-Username
    password        = var.Provisioner_Admin-Password
    host            = var.Provisioner_IP1
    timeout         = var.Provisioner_Timeout
  }

###### Upload Test script to IP1
  provisioner "file" {
    source      = "${path.module}/DATA/Test-Script.ps1"
    destination = "c:/temp/Test-Script.ps1"
  } 
}

resource "null_resource" "InstallonIP1" {
depends_on = [ null_resource.UploadTestFileToIP1 ]
connection {
    type            = var.Provisioner_Type
    user            = var.Provisioner_Admin-Username
    password        = var.Provisioner_Admin-Password
    host            = var.Provisioner_IP1
    timeout         = var.Provisioner_Timeout
  }
 provisioner "remote-exec" {
    inline = [
      "powershell -File c:/temp/Test-Script.ps1"
    ]
  }
}Output: ...
null_resource.InstallonIP1: Creating...
null_resource.InstallonIP1: Provisioning with 'remote-exec'...
null_resource.InstallonIP1 (remote-exec): Connecting to remote host via WinRM...
null_resource.InstallonIP1 (remote-exec):   Host: tf-cvad-v-ddc1
null_resource.InstallonIP1 (remote-exec):   Port: 5985
null_resource.InstallonIP1 (remote-exec):   User: administrator
null_resource.InstallonIP1 (remote-exec):   Password: true
null_resource.InstallonIP1 (remote-exec):   HTTPS: false
null_resource.InstallonIP1 (remote-exec):   Insecure: false
null_resource.InstallonIP1 (remote-exec):   NTLM: false
null_resource.InstallonIP1 (remote-exec):   CACert: false
null_resource.InstallonIP1 (remote-exec): Connected!
... Secure Communication - connecting to the HTTPS listener: resource "null_resource" "UploadTestFileToIP1" {
 connection {
    type            = var.Provisioner_Type
    user            = var.Provisioner_SecureAdmin-Username
    password        = var.Provisioner_SecureAdmin-Password
    host            = var.Provisioner_IP1
    port            = var.Provisioner_Port
    https           = var.Provisioner_HTTPS
    timeout         = var.Provisioner_Timeout
  }

###### Upload Test script to IP1
  provisioner "file" {
    source      = "${path.module}/DATA/Test-Script.ps1"
    destination = "c:/temp/Test-Script.ps1"
    
  } 
}

resource "null_resource" "InstallonIP1" {
depends_on = [ null_resource.UploadTestFileToIP1 ]
connection {
    type            = var.Provisioner_Type
    user            = var.Provisioner_SecureAdmin-Username
    password        = var.Provisioner_SecureAdmin-Password
    host            = var.Provisioner_IP1
    port            = var.Provisioner_Port
    https           = var.Provisioner_HTTPS
    timeout         = var.Provisioner_Timeout

  }
 provisioner "remote-exec" {
    inline = [
      "powershell -File c:/temp/Test-Script.ps1"
    ]
  }
}Output: ...
null_resource.InstallonIP1: Creating...
null_resource.InstallonIP1: Provisioning with 'remote-exec'...
null_resource.InstallonIP1 (remote-exec): Connecting to remote host via WinRM...
null_resource.InstallonIP1 (remote-exec):   Host: tf-cvad-v-ddc1
null_resource.InstallonIP1 (remote-exec):   Port: 5986
null_resource.InstallonIP1 (remote-exec):   User: svc_ansible
null_resource.InstallonIP1 (remote-exec):   Password: true
null_resource.InstallonIP1 (remote-exec):   HTTPS: true
null_resource.InstallonIP1 (remote-exec):   Insecure: false
null_resource.InstallonIP1 (remote-exec):   NTLM: false
null_resource.InstallonIP1 (remote-exec):   CACert: false
null_resource.InstallonIP1 (remote-exec): Connected!
...Important: Configuring secure communication, especially for Ansible on the Ubuntu machine, is a complex task that involves certificates and user mappings – you can find many comprehensive configuration guides online. A good start can be found in Ansible´s own documentation.  Active Directory DomainAn existing AD domain is imperative for a successful CVAD deployment. We assume you have a domain with all the needed configurations in place.  Creation of the CVAD Environment using IaCAs all prerequisites are met, we can now start with the workflow to create our Citrix Virtual Apps and Desktops deployment. As already stated, the workflow is divided into blocks that must be started in a specific, predetermined order: Caution: As already mentioned above, Module 9 - Using Terraform to create the StoreFront deployment and all its necessities, must be run on a Windows-based, domain-joined VM to be supported. This is due to limitations in StoreFront´s automation capabilities.  Using Packer to create the Master Images  In this IaC block, we use Packer to create the Master Images for the VDI machines and the Windows Servers.  Using Terraform to create the needed VMs based on the Master Images Terraform will create these VMs: a.       Two Windows Server 2025-based VMs for the Delivery Controllers b.       One Windows Server 2025-based VM for the StoreFront™ server c.       One Windows Server 2025-based VM for the WebStudio™ server d.       One Windows 11-based VM as a VDI Worker  Using Terraform and Ansible to configure the VMs Terraform and Ansible take further configuration steps: a.       Change the IP addresses to known IPs b.       Change the Ansible configuration file to reflect the IP changes c.       Put the VMs into the AD domain  Using Terraform and Ansible to deploy the Delivery Controllers Terraform and Ansible take further configuration steps: a.       Mount the CVAD ISO installer b.       Copy the publicly-signed SSL certificate to the DDCs c.       Install all required server roles and features before installing the DDCs d.       Change some necessary registry settings  Using Terraform and Ansible to deploy the StoreFront server Terraform and Ansible take further configuration steps: a.       Mount the CVAD ISO installer b.       Copy the publicly-signed SSL certificate to the StoreFront server c.       Install all required server roles and features before installing the StoreFront d.       Change some necessary registry settings  Using Terraform and Ansible to deploy the WebStudio server Terraform and Ansible take further configuration steps: a.       Mount the CVAD ISO installer b.       Copy the publicly-signed SSL certificate to the WebStudio server c.       Install all required server roles and features before installing the WebStudio server d.       Change some necessary registry settings e.       Configure WebStudio server as proxy for the DDCs  Using Terraform and Ansible to change the SSL Bindings Terraform and Ansible take further configuration steps: a.       Change the SSL bindings on all servers and listeners to achieve complete HTTPS communication  Using Terraform and Ansible to create the CVAD site and all its necessities Terraform and Ansible take further configuration steps: a.       Create all databases b.       Create the site c.       Add the second DDC to the site d.       Configure additional site configurations e.       Configure a dedicated Administrators group f.        Configure the licensing  Using Terraform to create the StoreFront deployment and all its necessities Terraform takes further configuration steps: a.       Create the StoreFront deployment b.       Create a StoreFront Authentication service c.       Create a StoreFront Store service d.       Create a StoreFront WebReceiver service  Using Terraform to create all CVAD entities and all their necessities Terraform takes further configuration steps: a.       Create a StoreFront Server object b.       Create a Hypervisor connection c.       Create a Hypervisor connection resource pool d.       Create a Machine Catalog e.       Create a Delivery Group f.        Create a Policy Set and its policies   Note: All Ansible playbooks are directly called from Terraform. Terraform stops the workflow if an error occurs by checking Ansible's return code.  Important: All shown Terraform codes, Ansible Playbooks, and PowerShell scripts are tailored exclusively for the specific environment used for this guide. You need to adjust all settings, values, snippets, etc., in the most suitable way tailored to your requirements, regulations, and existing environments. All settings, scripts, and code examples listed in this document are intended only to illustrate the possibilities and serve as a basis for your own deployment. Using all the provided code snippets is at your own risk – please read the disclaimer at the end of the document for further information. Terraform verifies the successful execution of each Ansible playbook. If an error occurs, Terraform will halt the deployment and write out the failed Playbook: Example snippet: ##### Create Databases Start
##### Connect to Ansible Interpreter and run CreateDatabase-Playbook on DDCs
resource "null_resource" "RunCreateDatabasePlaybook" {
  depends_on = [time_sleep.wait_10_seconds]

  provisioner "local-exec" {
    working_dir = "/etc/ansible"
    command     = var.TACG-TMM-XS-CL-Ansible-CreateDatabase-CMD
  }
}

##### Check Ansible Return Code
data "external" "CheckAnsibleReturnCodeCDB" {
  depends_on = [null_resource.RunCreateDatabasePlaybook]
  program    = ["bash", "/etc/ansible/assets/CheckAnsibleReturnCode.sh", "/etc/ansible/assets/ansible_exit_code.txt", "0"]
}

##### Terminate if Return Code is not 0
resource "null_resource" "IfExitCodeIsNotZeroThenHaltCDB" {
  depends_on = [data.external.CheckAnsibleReturnCodeCDB]
  provisioner "local-exec" {
    interpreter = ["/bin/bash", "-c"]
    command     = "[ \"${data.external.CheckAnsibleReturnCodeCDB.result.found}\" = \"1\" ] &amp;&amp; echo \"Create Databases - Everything OK\" || { echo \"Deploying the CVAD Databases failed. Please review the settings. Halting Terraform.\"; exit 1; }"
  }
}In this example, Terraform runs the Ansible playbook to create the CVAD databases. If an error occurs, Terraform writes an error message indicating that deploying the CVAD databases failed. You would find more information in the LogFile directory on the corresponding server. Terraform then stops the further execution of the snippets.  Part 1: Using Packer to create the Master ImagesHashicorp Packer is our standard framework for creating, maintaining, and deploying Master Images using IaC.  Our vSphere cluster has no VDI-related VMs before running the snippet:  Example Packer HCL file to create a Windows 11-based on vSphere: locals {
  WinRMUser        = "administrator"
  WinRMPass        = "!XxXxXxXxXxXxXxXxX!"
  vcenter_admin    = "administrator@vsphere.local"
  vcenter_password = "!XxXxXxXxXxXxXxXxX!"
  timestamp        = regex_replace(timestamp(), "[- TZ:01]", "")
  buildtime        = formatdate("DD-MM-YYYY hh:mm ZZZ", timestamp())
  manifest_date    = formatdate("DD-MM-YYYY hh:mm:ss", timestamp())
}

packer {
  required_version = "&gt;= 1.9.0"
  required_plugins {
    vsphere = {
      version = "&gt;= v1.4.2"
      source  = "github.com/vmware/vsphere"
    }
  }
  required_plugins {
    windows-update = {
      version = "&gt;= 0.16.8"
      source  = "github.com/rgl/windows-update"
    }
  }
}

variable "host" {
  type        = string
  description = "VMware ESXi host"
}

variable "name" {
  type        = string
  description = "Golden Image name"
}

variable "operating_system_vm" {
  type        = string
  description = "OS Guest OS"
}

variable "vcenter_cluster" {
  type        = string
  description = "vCenter cluster name"
}

variable "vcenter_datastore" {
  type        = string
  description = "vSphere datastore"
}

variable "vcenter_host" {
  type        = string
  description = "vCenter Server"
}

variable "vcenter_network" {
  type        = string
  description = "Portgroup name"
}

variable "vm_cores" {
  type        = string
  description = "Amount of cores"
}

variable "vm_cpus" {
  type        = string
  description = "amount of vCPUs"
}

variable "vm_disk_controller_type" {
  type        = list(string)
  description = "Controller type"
}

variable "vm_disk_size" {
  type        = string
  description = "Harddisk size"
}

variable "vm_disk_thin" {
  type        = string
  description = "Enable/Disable thin provisioning"
}

variable "vm_hardwareversion" {
  type        = string
  description = "VM hardware version"
}

variable "vm_firmware" {
  type        = string
  description = "The virtual machine firmware. (e.g. 'efi-secure'. 'efi', or 'bios')"
  default     = "efi-secure"
}

variable "vm_cdrom_type" {
  type        = string
  description = "The virtual machine CD-ROM type. (e.g. 'sata', or 'ide')"
  default     = "sata"
}

variable "common_remove_cdrom" {
  type        = bool
  description = "Remove the virtual CD-ROM(s)."
  default     = true
}

variable "vm_hotplug" {
  type        = string
  description = "Enable/Disable hotplug?"
}

variable "vm_logging" {
  type        = string
  description = "Enable/Disable VM Logging"
}

variable "vm_memory" {
  type        = string
  description = "VM Memory"
}

variable "vm_memory_reserve_all" {
  type        = string
  description = "Reserve all memory?"
}

variable "vm_network_card" {
  type        = string
  description = "Networkcard type"
}

variable "winsrv_iso" {
  type        = string
  description = "Windows Server ISO location"
}

variable "vm_vgpu" {
  type        = string
  description = "VM vGPU size"
}

variable "vm_boot_order" {
  type        = string
  description = "The boot order for virtual machines devices. (e.g. 'disk,cdrom')"
  default     = "disk,cdrom"
}

variable "vm_boot_wait" {
  type        = string
  description = "The time to wait before boot."
}

variable "vm_boot_command" {
  type        = list(string)
  description = "The virtual machine boot command."
  default     = []
}

variable "vm_shutdown_command" {
  type        = string
  description = "Command(s) for guest operating system shutdown."
}

variable "vm_tpm" {
  type    = string
  default = "true"
}

variable "create_snapshot" {
  type = string
}

variable "remove_cdrom" {
  type = string
}

variable "vbs_enabled" {
  type = string
}

variable "vvtd_enabled" {
  type = string
}

source "vsphere-iso" "win11_iso" {
  CPUs     = var.vm_cpus
  notes    = "Built by HashiCorp Packer on ${local.buildtime}."
  RAM      = var.vm_memory
  firmware = var.vm_firmware
  NestedHV        = true
  RAM_reserve_all = var.vm_memory_reserve_all
  cluster         = var.vcenter_cluster
  configuration_parameters = {
    "devices.hotplug"  = var.vm_hotplug
    "logging"          = var.vm_logging
    "svga.autodetect"  = "FALSE"
    "svga.numDisplays" = "2"
  }
  create_snapshot      = var.create_snapshot
  remove_cdrom         = var.remove_cdrom
  cpu_cores            = var.vm_cores
  datastore            = var.vcenter_datastore
  disk_controller_type = var.vm_disk_controller_type
  floppy_files         = ["${path.root}/setup/"]
  guest_os_type        = var.operating_system_vm
  host                 = var.host
  iso_paths            = [var.winsrv_iso]
  network_adapters {
    network      = var.vcenter_network
    network_card = var.vm_network_card
  }
  storage {
    disk_size             = var.vm_disk_size
    disk_thin_provisioned = var.vm_disk_thin
  }
  username       = local.vcenter_admin
  password       = local.vcenter_password
  vcenter_server = var.vcenter_host
  vm_name        = var.name
  vm_version     = var.vm_hardwareversion
  vTPM = var.vm_tpm
  vbs_enabled  = var.vbs_enabled
  vvtd_enabled = var.vvtd_enabled
  insecure_connection = "true"
  communicator        = "winrm"
  winrm_port          = "5985"
  winrm_username      = local.WinRMUser
  winrm_password      = local.WinRMPass
  winrm_timeout       = "12h"
  boot_order       = var.vm_boot_order
  boot_wait        = var.vm_boot_wait
  boot_command     = var.vm_boot_command
  shutdown_command = var.vm_shutdown_command
}

build {
  sources = ["source.vsphere-iso.win11_iso"]

  provisioner "windows-restart" {
    restart_check_command = "powershell -command \"&amp; {Write-Output 'restarted.'}\""
    restart_timeout       = "20m"
  }

  provisioner "powershell" {
    elevated_user     = local.WinRMUser
    elevated_password = local.WinRMPass
    valid_exit_codes  = [0]
    scripts = [
      "./setup/install-vda.ps1",
    ]
  }

  provisioner "powershell" {
    elevated_user     = local.WinRMUser
    elevated_password = local.WinRMPass
    valid_exit_codes  = [0, 267014]
    scripts = [
      "./setup/windows-init.ps1",
    ]
  }

  post-processors {
      post-processor "vsphere-template"{
          host                = var.vcenter_host
          insecure            = true
          username            = local.vcenter_admin
          password            = local.vcenter_password
          datacenter          = var.vcenter_datacenter
          folder              = "/vmfs/volumes/XxXxXxXxXxXxXxX"
      }
    }
}
In this example, we use a modified autounattend.xml file to configure the Windows VM during creation. &lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;unattend xmlns="urn:schemas-microsoft-com:unattend"&gt;
    &lt;settings pass="windowsPE"&gt;
        &lt;component name="Microsoft-Windows-PnpCustomizationsWinPE" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;
            &lt;DriverPaths&gt;
                &lt;PathAndCredentials wcm:action="add" wcm:keyValue="1"&gt;
                    &lt;Path&gt;E:\&lt;/Path&gt;
                &lt;/PathAndCredentials&gt;
            &lt;/DriverPaths&gt;
        &lt;/component&gt;
        &lt;component name="Microsoft-Windows-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;
            &lt;DiskConfiguration&gt;
                &lt;Disk wcm:action="add"&gt;
                    &lt;CreatePartitions&gt;
                        &lt;CreatePartition wcm:action="add"&gt;
                            &lt;Order&gt;1&lt;/Order&gt;
                            &lt;Type&gt;EFI&lt;/Type&gt;
                            &lt;Size&gt;100&lt;/Size&gt;
                        &lt;/CreatePartition&gt;
                        &lt;CreatePartition wcm:action="add"&gt;
                            &lt;Order&gt;2&lt;/Order&gt;
                            &lt;Type&gt;MSR&lt;/Type&gt;
                            &lt;Size&gt;16&lt;/Size&gt;
                        &lt;/CreatePartition&gt;
                        &lt;CreatePartition wcm:action="add"&gt;
                            &lt;Order&gt;3&lt;/Order&gt;
                            &lt;Type&gt;Primary&lt;/Type&gt;
                            &lt;Extend&gt;true&lt;/Extend&gt;
                        &lt;/CreatePartition&gt;
                    &lt;/CreatePartitions&gt;
                    &lt;ModifyPartitions&gt;
                        &lt;ModifyPartition wcm:action="add"&gt;
                            &lt;Order&gt;1&lt;/Order&gt;
                            &lt;Format&gt;FAT32&lt;/Format&gt;
                            &lt;Label&gt;System&lt;/Label&gt;
                            &lt;PartitionID&gt;1&lt;/PartitionID&gt;
                        &lt;/ModifyPartition&gt;
                        &lt;ModifyPartition wcm:action="add"&gt;
                            &lt;Order&gt;2&lt;/Order&gt;
                            &lt;PartitionID&gt;2&lt;/PartitionID&gt;
                        &lt;/ModifyPartition&gt;
                        &lt;ModifyPartition wcm:action="add"&gt;
                            &lt;Order&gt;3&lt;/Order&gt;
                            &lt;Format&gt;NTFS&lt;/Format&gt;
                            &lt;Label&gt;Windows&lt;/Label&gt;
                            &lt;Letter&gt;C&lt;/Letter&gt;
                            &lt;PartitionID&gt;3&lt;/PartitionID&gt;
                        &lt;/ModifyPartition&gt;
                    &lt;/ModifyPartitions&gt;
                    &lt;DiskID&gt;0&lt;/DiskID&gt;
                    &lt;WillWipeDisk&gt;true&lt;/WillWipeDisk&gt;
                &lt;/Disk&gt;
            &lt;/DiskConfiguration&gt;
            &lt;ImageInstall&gt;
                &lt;OSImage&gt;
                    &lt;InstallTo&gt;
                        &lt;DiskID&gt;0&lt;/DiskID&gt;
                        &lt;PartitionID&gt;3&lt;/PartitionID&gt;
                    &lt;/InstallTo&gt;
                    &lt;InstallFrom&gt;
                        &lt;MetaData wcm:action="add"&gt;
                            &lt;Key&gt;/IMAGE/INDEX&lt;/Key&gt;
                            &lt;Value&gt;1&lt;/Value&gt;
                        &lt;/MetaData&gt;
                    &lt;/InstallFrom&gt;
                &lt;/OSImage&gt;
            &lt;/ImageInstall&gt;
            &lt;UserData&gt;
                &lt;ProductKey&gt;
                    &lt;Key&gt;VK7JG-NPHTM-C97JM-9MPGT-3V66T&lt;/Key&gt;
                    &lt;WillShowUI&gt;OnError&lt;/WillShowUI&gt;
                &lt;/ProductKey&gt;
                &lt;AcceptEula&gt;true&lt;/AcceptEula&gt;
                &lt;FullName&gt;TACG&lt;/FullName&gt;
                &lt;Organization&gt;TACG&lt;/Organization&gt;
            &lt;/UserData&gt;
            &lt;RunSynchronous&gt;
                &lt;RunSynchronousCommand wcm:action="add"&gt;
                    &lt;Order&gt;1&lt;/Order&gt;
                    &lt;Path&gt;reg add HKLM\SYSTEM\Setup\LabConfig /v BypassTPMCheck /t REG_DWORD /d 1&lt;/Path&gt;
                &lt;/RunSynchronousCommand&gt;
                &lt;RunSynchronousCommand wcm:action="add"&gt;
                    &lt;Order&gt;2&lt;/Order&gt;
                    &lt;Path&gt;reg add HKLM\SYSTEM\Setup\LabConfig /v BypassSecureBootCheck /t REG_DWORD /d 1&lt;/Path&gt;
                &lt;/RunSynchronousCommand&gt;
                &lt;RunSynchronousCommand wcm:action="add"&gt;
                    &lt;Order&gt;3&lt;/Order&gt;
                    &lt;Path&gt;reg add HKLM\SYSTEM\Setup\LabConfig /v BypassRAMCheck /t REG_DWORD /d 1&lt;/Path&gt;
                &lt;/RunSynchronousCommand&gt;
            &lt;/RunSynchronous&gt;
        &lt;/component&gt;
        &lt;component name="Microsoft-Windows-International-Core-WinPE" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;
            &lt;SetupUILanguage&gt;
                &lt;UILanguage&gt;en-US&lt;/UILanguage&gt;
            &lt;/SetupUILanguage&gt;
            &lt;InputLocale&gt;en-US&lt;/InputLocale&gt;
            &lt;SystemLocale&gt;en-US&lt;/SystemLocale&gt;
            &lt;UILanguage&gt;en-US&lt;/UILanguage&gt;
            &lt;UILanguageFallback&gt;en-US&lt;/UILanguageFallback&gt;
            &lt;UserLocale&gt;en-US&lt;/UserLocale&gt;
        &lt;/component&gt;
    &lt;/settings&gt;
    &lt;settings pass="oobeSystem"&gt;
        &lt;component name="Microsoft-Windows-International-Core" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;
            &lt;InputLocale&gt;en-US&lt;/InputLocale&gt;
            &lt;SystemLocale&gt;en-US&lt;/SystemLocale&gt;
            &lt;UILanguage&gt;en-US&lt;/UILanguage&gt;
            &lt;UILanguageFallback&gt;en-US&lt;/UILanguageFallback&gt;
            &lt;UserLocale&gt;en-US&lt;/UserLocale&gt;
        &lt;/component&gt;
        &lt;component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;
            &lt;OOBE&gt;
                &lt;HideEULAPage&gt;true&lt;/HideEULAPage&gt;
                &lt;HideLocalAccountScreen&gt;true&lt;/HideLocalAccountScreen&gt;
                &lt;HideOEMRegistrationScreen&gt;true&lt;/HideOEMRegistrationScreen&gt;
                &lt;HideOnlineAccountScreens&gt;true&lt;/HideOnlineAccountScreens&gt;
                &lt;HideWirelessSetupInOOBE&gt;true&lt;/HideWirelessSetupInOOBE&gt;
                &lt;ProtectYourPC&gt;1&lt;/ProtectYourPC&gt;
            &lt;/OOBE&gt;
            &lt;TimeZone&gt;UTC&lt;/TimeZone&gt;
            &lt;UserAccounts&gt;
                &lt;AdministratorPassword&gt;
                    &lt;Value&gt;!XxXxXxXxXxXxXxXxX!&lt;/Value&gt;
                    &lt;PlainText&gt;true&lt;/PlainText&gt;
                &lt;/AdministratorPassword&gt;
                &lt;LocalAccounts&gt;
                    &lt;LocalAccount wcm:action="add"&gt;
                        &lt;Name&gt;azadmin&lt;/Name&gt;
                        &lt;DisplayName&gt;&lt;/DisplayName&gt;
                        &lt;Group&gt;Administrators&lt;/Group&gt;
                        &lt;Password&gt;
                            &lt;Value&gt;!XxXxXxXxXxXxXxXxX!&lt;/Value&gt;
                            &lt;PlainText&gt;true&lt;/PlainText&gt;
                        &lt;/Password&gt;
                    &lt;/LocalAccount&gt;
                    &lt;LocalAccount wcm:action="add"&gt;
                        &lt;Name&gt;User&lt;/Name&gt;
                        &lt;DisplayName&gt;&lt;/DisplayName&gt;
                        &lt;Group&gt;Users&lt;/Group&gt;
                        &lt;Password&gt;
                            &lt;Value&gt;&lt;/Value&gt;
                            &lt;PlainText&gt;true&lt;/PlainText&gt;
                        &lt;/Password&gt;
                    &lt;/LocalAccount&gt;
                &lt;/LocalAccounts&gt;
            &lt;/UserAccounts&gt;
            &lt;AutoLogon&gt;
                &lt;Username&gt;Administrator&lt;/Username&gt;
                &lt;Enabled&gt;true&lt;/Enabled&gt;
                &lt;LogonCount&gt;1&lt;/LogonCount&gt;
                &lt;Password&gt;
                    &lt;Value&gt;!XxXxXxXxXxXxXxXxX!&lt;/Value&gt;
                    &lt;PlainText&gt;true&lt;/PlainText&gt;
                &lt;/Password&gt;
            &lt;/AutoLogon&gt;
            &lt;FirstLogonCommands&gt;
                &lt;SynchronousCommand wcm:action="add"&gt;
                    &lt;Order&gt;1&lt;/Order&gt;
                    &lt;CommandLine&gt;PowerShell "Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Force"&lt;/CommandLine&gt;
                    &lt;Description&gt;Change the default PowerShell Execution Policy from Restricted to RemoteSigned&lt;/Description&gt;
                &lt;/SynchronousCommand&gt;
                &lt;SynchronousCommand wcm:action="add"&gt;
                    &lt;Order&gt;2&lt;/Order&gt;
                    &lt;CommandLine&gt;C:\Windows\SysWOW64\cmd /c PowerShell -Command "Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Force"&lt;/CommandLine&gt;
                    &lt;Description&gt;Change the default PowerShell (32-bit) Execution Policy from Restricted to RemoteSigned&lt;/Description&gt;
                &lt;/SynchronousCommand&gt;
                &lt;SynchronousCommand wcm:action="add"&gt;
                    &lt;Order&gt;3&lt;/Order&gt;
                    &lt;CommandLine&gt;PowerShell -WindowStyle Maximized -Command "Get-PSDrive -PSProvider FileSystem | ForEach-Object {$p = Join-Path $_.Root provision-autounattend.ps1; if (Test-Path $p) {&amp;amp;$p}}"&lt;/CommandLine&gt;
                &lt;/SynchronousCommand&gt;
                &lt;SynchronousCommand wcm:action="add"&gt;
               &lt;CommandLine&gt;powershell -ExecutionPolicy Bypass -File a:\install-vmwtools.ps1&lt;/CommandLine&gt;
               &lt;Order&gt;4&lt;/Order&gt;
               &lt;Description&gt;Install VMware Tools&lt;/Description&gt;
            &lt;/SynchronousCommand&gt;
            &lt;SynchronousCommand wcm:action="add"&gt;
               &lt;CommandLine&gt;%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -File a:\windows-init.ps1&lt;/CommandLine&gt;
               &lt;Order&gt;5&lt;/Order&gt;
               &lt;Description&gt;Initial Configuration&lt;/Description&gt;
            &lt;/SynchronousCommand&gt;
            &lt;SynchronousCommand wcm:action="add"&gt;
               &lt;Description&gt;Sysprep Generalize&lt;/Description&gt;
               &lt;CommandLine&gt;cmd /c C:\Windows\System32\Sysprep\Sysprep.exe /generalize /oobe /shutdown&lt;/CommandLine&gt;
               &lt;Order&gt;5&lt;/Order&gt;
            &lt;/SynchronousCommand&gt;
            &lt;/FirstLogonCommands&gt;
        &lt;/component&gt;
    &lt;/settings&gt;
    &lt;settings pass="offlineServicing"&gt;
        &lt;component name="Microsoft-Windows-LUA-Settings" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;
            &lt;EnableLUA&gt;false&lt;/EnableLUA&gt;
        &lt;/component&gt;
    &lt;/settings&gt;
&lt;/unattend&gt;As the syntax of the autounattend.xml is very sensitive to errors, we use a self-written configuration tool to create the autounattend.xml more comfortably:  Example: Running the Windows 11-related Packer IaC snippet should create a vSphere VM that is converted to a template: azadmin@tacg-cicd:~$ packer build -force -var-file=".\win11-std.auto.pkrvars.hcl" win11-std-config.pkr.hcl
vsphere-iso.win11_iso: output will be in this color.

==&gt; vsphere-iso.win11_iso: Creating virtual machine...
==&gt; vsphere-iso.win11_iso: Customizing hardware...
==&gt; vsphere-iso.win11_iso: Adding virtual machine flags...
==&gt; vsphere-iso.win11_iso: Mounting ISO images...
==&gt; vsphere-iso.win11_iso: Adding configuration parameters...
==&gt; vsphere-iso.win11_iso: Creating floppy disk...
==&gt; vsphere-iso.win11_iso: Copying files flatly from floppy_files
==&gt; vsphere-iso.win11_iso: Copying directory: ./setup/
==&gt; vsphere-iso.win11_iso: Adding file: setup\add-domain.ps1
==&gt; vsphere-iso.win11_iso: Adding file: setup\autounattend.xml
==&gt; vsphere-iso.win11_iso: Adding file: setup\autounattend.xml-orig
==&gt; vsphere-iso.win11_iso: Adding file: setup\disable-autolog.ps1
==&gt; vsphere-iso.win11_iso: Adding file: setup\disable-network-discovery.cmd
==&gt; vsphere-iso.win11_iso: Adding file: setup\disable-ssh.ps1
==&gt; vsphere-iso.win11_iso: Adding file: setup\enable-rdp.cmd
==&gt; vsphere-iso.win11_iso: Adding file: setup\enable-ssh.ps1
==&gt; vsphere-iso.win11_iso: Adding file: setup\enable-winrm.ps1
==&gt; vsphere-iso.win11_iso: Adding file: setup\install-vmwtools.ps1
==&gt; vsphere-iso.win11_iso: Adding file: setup\install_windowsfeatures.ps1
==&gt; vsphere-iso.win11_iso: Adding file: setup\windows-init.ps1
==&gt; vsphere-iso.win11_iso: Done copying files from floppy_files
==&gt; vsphere-iso.win11_iso: Collecting paths from floppy_dirs
==&gt; vsphere-iso.win11_iso: Resulting paths from floppy_dirs : []
==&gt; vsphere-iso.win11_iso: Done copying paths from floppy_dirs
==&gt; vsphere-iso.win11_iso: Copying files from floppy_content
==&gt; vsphere-iso.win11_iso: Done copying files from floppy_content
==&gt; vsphere-iso.win11_iso: Uploading floppy image...
==&gt; vsphere-iso.win11_iso: Adding generated floppy image...
==&gt; vsphere-iso.win11_iso: Setting boot order...
==&gt; vsphere-iso.win11_iso: Powering on virtual machine...
==&gt; vsphere-iso.win11_iso: Waiting 3s for boot...
==&gt; vsphere-iso.win11_iso: Typing boot command...
==&gt; vsphere-iso.win11_iso: Waiting for IP...
==&gt; vsphere-iso.win11_iso: IP address: 10.10.11.186
==&gt; vsphere-iso.win11_iso: Using WinRM communicator to connect: 10.10.11.186
==&gt; vsphere-iso.win11_iso: Waiting for WinRM to become available...
==&gt; vsphere-iso.win11_iso: WinRM connected.
==&gt; vsphere-iso.win11_iso: Connected to WinRM!
==&gt; vsphere-iso.win11_iso: Restarting Machine
==&gt; vsphere-iso.win11_iso: Waiting for machine to restart...
==&gt; vsphere-iso.win11_iso: A system shutdown is in progress.(1115)
==&gt; vsphere-iso.win11_iso: A system shutdown is in progress.(1115)
==&gt; vsphere-iso.win11_iso: restarted.
==&gt; vsphere-iso.win11_iso: TACGTAC-8KAGCAS restarted.
==&gt; vsphere-iso.win11_iso: Machine successfully restarted, moving on
==&gt; vsphere-iso.win11_iso: Provisioning with Powershell...
==&gt; vsphere-iso.win11_iso: Provisioning with powershell script: ./setup/windows-init.ps1
==&gt; vsphere-iso.win11_iso: Configuring important Windows Features...
==&gt; vsphere-iso.win11_iso: Setting the network connection profiles to Private...
==&gt; vsphere-iso.win11_iso: Setting the Windows Remote Management configuration...
==&gt; vsphere-iso.win11_iso: WinRM service is already running on this machine.
==&gt; vsphere-iso.win11_iso: WinRM is already set up for remote management on this computer.
==&gt; vsphere-iso.win11_iso: Service
==&gt; vsphere-iso.win11_iso:     RootSDDL = O:NSG:BAD:P(A;;GA;;;BA)(A;;GR;;;IU)S:P(AU;FA;GA;;;WD)(AU;SA;GXGW;;;WD)
==&gt; vsphere-iso.win11_iso:     MaxConcurrentOperations = 4294967295
==&gt; vsphere-iso.win11_iso:     MaxConcurrentOperationsPerUser = 1500
==&gt; vsphere-iso.win11_iso:     EnumerationTimeoutms = 240000
==&gt; vsphere-iso.win11_iso:     MaxConnections = 300
==&gt; vsphere-iso.win11_iso:     MaxPacketRetrievalTimeSeconds = 120
==&gt; vsphere-iso.win11_iso:     AllowUnencrypted = true
==&gt; vsphere-iso.win11_iso:     Auth
==&gt; vsphere-iso.win11_iso:         Basic = true
==&gt; vsphere-iso.win11_iso:         Kerberos = true
==&gt; vsphere-iso.win11_iso:         Negotiate = true
==&gt; vsphere-iso.win11_iso:         Certificate = false
==&gt; vsphere-iso.win11_iso:         CredSSP = false
==&gt; vsphere-iso.win11_iso:         CbtHardeningLevel = Relaxed
==&gt; vsphere-iso.win11_iso:     DefaultPorts
==&gt; vsphere-iso.win11_iso:         HTTP = 5985
==&gt; vsphere-iso.win11_iso:         HTTPS = 5986
==&gt; vsphere-iso.win11_iso:     IPv4Filter = *
==&gt; vsphere-iso.win11_iso:     IPv6Filter = *
==&gt; vsphere-iso.win11_iso:     EnableCompatibilityHttpListener = false
==&gt; vsphere-iso.win11_iso:     EnableCompatibilityHttpsListener = false
==&gt; vsphere-iso.win11_iso:     CertificateThumbprint
==&gt; vsphere-iso.win11_iso:     AllowRemoteAccess = true
==&gt; vsphere-iso.win11_iso:
==&gt; vsphere-iso.win11_iso: Auth
==&gt; vsphere-iso.win11_iso:     Basic = true
==&gt; vsphere-iso.win11_iso:     Kerberos = true
==&gt; vsphere-iso.win11_iso:     Negotiate = true
==&gt; vsphere-iso.win11_iso:     Certificate = false
==&gt; vsphere-iso.win11_iso:     CredSSP = false
==&gt; vsphere-iso.win11_iso:     CbtHardeningLevel = Relaxed
==&gt; vsphere-iso.win11_iso:
==&gt; vsphere-iso.win11_iso: Allowing Windows Remote Management in the Windows Firewall...
==&gt; vsphere-iso.win11_iso:
==&gt; vsphere-iso.win11_iso: No rules match the specified criteria.
==&gt; vsphere-iso.win11_iso:
==&gt; vsphere-iso.win11_iso:
==&gt; vsphere-iso.win11_iso: Updated 2 rule(s).
==&gt; vsphere-iso.win11_iso: Ok.
==&gt; vsphere-iso.win11_iso:
==&gt; vsphere-iso.win11_iso: Running shutdown command...
==&gt; vsphere-iso.win11_iso: Deleting floppy drives...
==&gt; vsphere-iso.win11_iso: Deleting floppy image...
==&gt; vsphere-iso.win11_iso: Ejecting CD-ROM media...
==&gt; vsphere-iso.win11_iso: Removing CD-ROM devices...
==&gt; vsphere-iso.win11_iso: Closing sessions...
Build 'vsphere-iso.win11_iso' finished after 20 minutes 40 seconds.

==&gt; Wait completed after 20 minutes 40 seconds

==&gt; Builds finished. The artifacts of successful builds are:
--&gt; vsphere-iso.win11_iso: win11-p01
azadmin@tacg-cicd:~$ The exact process is also done to create the Windows Server Master Image and Template. After a successful run, vSphere should reflect the created VMs, and after conversion, the corresponding templates:  Since the Master Images have been created, we can proceed to the next step.  Part 2: Using Terraform to create the needed VMs based on the Master ImagesTerraform now creates all the required Infrastructure VMs for deploying CVAD: Two Windows Server 2025-based VMs for the Delivery Controllers One Windows Server 2025-based VM for the StoreFront server One Windows Server 2025-based VM for the WebStudio server. All needed settings are defined in a dedicated JSON file, where all variables and their values are stored. This allows maximum code flexibility and reusability, as it does not need to be changed when other VM configurations arise. Note: Those are the key principles of IaC – repeatability, reusability, and flexibility. Example of the VM settings in Terraform: {
    "vsphere_W2K25_template_name":"winsrv2025-02wi",
    "vsphere_ddc1_vmname":"TF-CVAD-V-DDC1",
    "vsphere_ddc2_vmname":"TF-CVAD-V-DDC2",
    "vsphere_sf_vmname":"TF-CVAD-V-SF",
    "vsphere_ws_vmname":"TF-CVAD-V-WS",
    "vsphere_ddc1_cpus":2,
    "vsphere_ddc2_cpus":2,
    "vsphere_sf_cpus":2,
    "vsphere_ws_cpus":2,
    "vsphere_ddc1_ram":8192,
    "vsphere_ddc2_ram":8192,
    "vsphere_sf_ram":8192,
    "vsphere_ws_ram":8192,
    "vsphere_ddc1_disk_name":0,
    "vsphere_ddc2_disk_name":0,
    "vsphere_sf_disk_name":0,
    "vsphere_ws_disk_name":0,
    "vsphere_ddc1_disk_size":80,
    "vsphere_ddc2_disk_size":80,
    "vsphere_ws_disk_size":80,
    "vsphere_sf_disk_size":80,
    "vsphere_ddc1_disk_provtype":true,
    "vsphere_ddc2_disk_provtype":true,
    "vsphere_sf_disk_provtype":true,
    "vsphere_ws_disk_provtype":true,
    "vsphere_local_admin_pw":"!XxXxXxXxXxXxXxXxX!",
    "vsphere_ddc1_ipv4":"10.10.119.31",
    "vsphere_ddc2_ipv4":"10.10.119.32",
    "vsphere_sf_ipv4":"10.10.119.33",
    "vsphere_ws_ipv4":"10.10.119.34", 
    "vsphere_ddc_ipv4netmask":16,
    "vsphere_ddc_ipv4_gateway":"10.10.0.1"
}For example, if you want to change the main disk size, you only need to change the corresponding value: From  to  would double the disk size to 160GB. Important: The Terraform provider for vSphere supports joining a newly created VM to an AD domain, but we want our code to be as Hypervisor-agnostic as possible – that is one of the main principles of Automation – Code reusability. The Domain join will be performed in the next block using an Ansible Playbook. Example Terraform snippet for VM creation: # Terraform Deployment of Citrix CVAD on vSphere 8 - 25.11 
## Definition of vSphere Provider Variables - not all available variables are defined, only these we use
### Reference https://registry.terraform.io/providers/vmware/vsphere/latest/docs

data "vsphere_datacenter" "datacenter" {
  name = var.vsphere_datacenter
}

data "vsphere_datastore" "datastore" {
  name          = var.vsphere_datastore
  datacenter_id = data.vsphere_datacenter.datacenter.id
}

data "vsphere_network" "network" {
  name          = var.vsphere_network
  datacenter_id = data.vsphere_datacenter.datacenter.id
}

data "vsphere_host" "host" {
  name          = var.vsphere_host
  datacenter_id = data.vsphere_datacenter.datacenter.id
}

### If a cluster is available, uncomment this block
data "vsphere_compute_cluster" "cluster" {
  name          = "TACG"
  datacenter_id = data.vsphere_datacenter.datacenter.id
}

data "vsphere_virtual_machine" "W2K22_Template" {
  name          = var.vsphere_W2K22_template_name
  datacenter_id = data.vsphere_datacenter.datacenter.id
}

### Create DDC1-VM
resource "vsphere_virtual_machine" "ddc1-vm" {
  name             = var.vsphere_ddc1_vmname
  resource_pool_id = data.vsphere_compute_cluster.cluster.resource_pool_id
  datastore_id     = data.vsphere_datastore.datastore.id
  num_cpus         = var.vsphere_ddc1_cpus
  memory           = var.vsphere_ddc1_ram
  guest_id         = data.vsphere_virtual_machine.W2K22_Template.guest_id
  scsi_type        = data.vsphere_virtual_machine.W2K22_Template.scsi_type
  firmware         = "efi"

  network_interface {
    network_id   = data.vsphere_network.network.id
    adapter_type = data.vsphere_virtual_machine.W2K22_Template.network_interface_types[0]
  }

  disk {
    label = var.vsphere_ddc1_disk_name

    ### If you want to use the same disk size and type as hte template, uncomment this block
    size             = data.vsphere_virtual_machine.W2K22_Template.disks.0.size
    thin_provisioned = data.vsphere_virtual_machine.W2K22_Template.disks.0.thin_provisioned

    ### If you want to override the disk size and disk provisioning type, uncomment this block and set the corresponding variables
    #size             = var.vsphere_ddc1_disk_size
    #thin_provisioned = var.vsphere_ddc1_disk_provtype
  }

  cdrom {
    datastore_id = data.vsphere_datastore.datastore.id
    path         = var.vsphere_cvad_iso_destination_path
  }

  clone {
    template_uuid = data.vsphere_virtual_machine.W2K22_Template.id
    customize {
      network_interface {
        ipv4_address    = var.vsphere_ddc1_ipv4
        ipv4_netmask    = var.vsphere_ddc_ipv4netmask
        dns_server_list = [var.vsphere_ddc_dns[0]]
      }
      windows_options {
        computer_name = var.vsphere_computernames[0]
      }
      ipv4_gateway = var.vsphere_ddc_ipv4_gateway
    }
  }

}

### Create DDC2-VM
resource "vsphere_virtual_machine" "ddc2-vm" {
  name             = var.vsphere_ddc2_vmname
  resource_pool_id = data.vsphere_compute_cluster.cluster.resource_pool_id
  datastore_id     = data.vsphere_datastore.datastore.id
  num_cpus         = var.vsphere_ddc2_cpus
  memory           = var.vsphere_ddc2_ram
  guest_id         = data.vsphere_virtual_machine.W2K22_Template.guest_id
  scsi_type        = data.vsphere_virtual_machine.W2K22_Template.scsi_type
  firmware         = "efi"

  network_interface {
    network_id   = data.vsphere_network.network.id
    adapter_type = data.vsphere_virtual_machine.W2K22_Template.network_interface_types[0]
  }

  disk {
    label = var.vsphere_ddc2_disk_name

    ### If you want to use the same disk size and type as hte template, uncomment this block
    size             = data.vsphere_virtual_machine.W2K22_Template.disks.0.size
    thin_provisioned = data.vsphere_virtual_machine.W2K22_Template.disks.0.thin_provisioned

    ### If you want to override the disk size and disk provisioning type, uncomment this block and set the corresponding variables
    #size             = var.vsphere_ddc2_disk_size
    #thin_provisioned = var.vsphere_ddc2_disk_provtype
  }
  cdrom {
    datastore_id = data.vsphere_datastore.datastore.id
    path         = var.vsphere_cvad_iso_destination_path
  }

  clone {
    template_uuid = data.vsphere_virtual_machine.W2K22_Template.id
    customize {
      network_interface {
        ipv4_address    = var.vsphere_ddc2_ipv4
        ipv4_netmask    = var.vsphere_ddc_ipv4netmask
        dns_server_list = [var.vsphere_ddc_dns[0]]
      }
      windows_options {
        computer_name = var.vsphere_computernames[0]
      }
      ipv4_gateway = var.vsphere_ddc_ipv4_gateway
    }
  }
}

### Create SF-VM
resource "vsphere_virtual_machine" "sf-vm" {
  name             = var.vsphere_sf_vmname
  resource_pool_id = data.vsphere_compute_cluster.cluster.resource_pool_id
  datastore_id     = data.vsphere_datastore.datastore.id
  num_cpus         = var.vsphere_sf_cpus
  memory           = var.vsphere_sf_ram
  guest_id         = data.vsphere_virtual_machine.W2K22_Template.guest_id
  scsi_type        = data.vsphere_virtual_machine.W2K22_Template.scsi_type
  firmware         = "efi"

  network_interface {
    network_id   = data.vsphere_network.network.id
    adapter_type = data.vsphere_virtual_machine.W2K22_Template.network_interface_types[0]
  }

  disk {
    label = var.vsphere_sf_disk_name

    ### If you want to use the same disk size and type as hte template, uncomment this block
    size             = data.vsphere_virtual_machine.W2K22_Template.disks.0.size
    thin_provisioned = data.vsphere_virtual_machine.W2K22_Template.disks.0.thin_provisioned

    ### If you want to override the disk size and disk provisioning type, uncomment this block and set the corresponding variables
    #size             = var.vsphere_sf_disk_size
    #thin_provisioned = var.vsphere_sf_disk_provtype
  }
  cdrom {
    datastore_id = data.vsphere_datastore.datastore.id
    path         = var.vsphere_cvad_iso_destination_path
  }

  clone {
    template_uuid = data.vsphere_virtual_machine.W2K22_Template.id
    customize {
      network_interface {
        ipv4_address    = var.vsphere_sf_ipv4
        ipv4_netmask    = var.vsphere_ddc_ipv4netmask
        dns_server_list = [var.vsphere_ddc_dns[0]]
      }
      windows_options {
        computer_name = var.vsphere_computernames[0]
      }
      ipv4_gateway = var.vsphere_ddc_ipv4_gateway
    }
  }
}

### Create WS-VM
resource "vsphere_virtual_machine" "ws-vm" {
  name             = var.vsphere_ws_vmname
  resource_pool_id = data.vsphere_compute_cluster.cluster.resource_pool_id
  datastore_id     = data.vsphere_datastore.datastore.id
  num_cpus         = var.vsphere_ws_cpus
  memory           = var.vsphere_ws_ram
  guest_id         = data.vsphere_virtual_machine.W2K22_Template.guest_id
  scsi_type        = data.vsphere_virtual_machine.W2K22_Template.scsi_type
  firmware         = "efi"

  network_interface {
    network_id   = data.vsphere_network.network.id
    adapter_type = data.vsphere_virtual_machine.W2K22_Template.network_interface_types[0]
  }

  disk {
    label = var.vsphere_ws_disk_name

    ### If you want to use the same disk size and type as hte template, uncomment this block
    size             = data.vsphere_virtual_machine.W2K22_Template.disks.0.size
    thin_provisioned = data.vsphere_virtual_machine.W2K22_Template.disks.0.thin_provisioned

    ### If you want to override the disk size and disk provisioning type, uncomment this block and set the corresponding variables
    #size             = var.vsphere_ws_disk_size
    #thin_provisioned = var.vsphere_ws_disk_provtype
  }
  cdrom {
    datastore_id = data.vsphere_datastore.datastore.id
    path         = var.vsphere_cvad_iso_destination_path
  }

  clone {
    template_uuid = data.vsphere_virtual_machine.W2K22_Template.id
    customize {
      network_interface {
        ipv4_address    = var.vsphere_ws_ipv4
        ipv4_netmask    = var.vsphere_ddc_ipv4netmask
        dns_server_list = [var.vsphere_ddc_dns[0]]
      }
      windows_options {
        computer_name = var.vsphere_computernames[0]
      }
      ipv4_gateway = var.vsphere_ddc_ipv4_gateway
    }
  }
}Running this snippet should create all 4 VMs on vSphere: azadmin@tacg-cicd:/etc/terraform-tmm/CVADVS/_Creation$ terraform apply

data.vsphere_datacenter.datacenter: Reading...
data.vsphere_datacenter.datacenter: Read complete after 0s [id=datacenter-3]
data.vsphere_network.network: Reading...
data.vsphere_virtual_machine.W2K22_Template: Reading...
data.vsphere_host.host: Reading...
data.vsphere_compute_cluster.cluster: Reading...
data.vsphere_datastore.datastore: Reading...
data.vsphere_network.network: Read complete after 0s [id=network-12]
data.vsphere_datastore.datastore: Read complete after 0s [id=datastore-11]
data.vsphere_host.host: Read complete after 0s [id=host-8]
data.vsphere_virtual_machine.W2K22_Template: Read complete after 0s [id=422a5cdf-8e0a-339e-e7d4-a674484d2eba]
data.vsphere_compute_cluster.cluster: Read complete after 0s [id=domain-c17]

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:

  # vsphere_virtual_machine.ddc1-vm will be created
  + resource "vsphere_virtual_machine" "ddc1-vm" {
      + annotation                              = (known after apply)
      + boot_retry_delay                        = 10000
      + change_version                          = (known after apply)
      + cpu_limit                               = -1
      + cpu_share_count                         = (known after apply)
      + cpu_share_level                         = "normal"
      + datastore_id                            = "datastore-11"
      + default_ip_address                      = (known after apply)
      + ept_rvi_mode                            = (known after apply)
      + extra_config_reboot_required            = true
      + firmware                                = "efi"
      + force_power_off                         = true
      + guest_id                                = "windows2019srv_64Guest"
      + guest_ip_addresses                      = (known after apply)
      + hardware_version                        = (known after apply)
      + host_system_id                          = (known after apply)
      + hv_mode                                 = (known after apply)
      + id                                      = (known after apply)
      + ide_controller_count                    = 2
      + imported                                = (known after apply)
      + latency_sensitivity                     = "normal"
      + memory                                  = 8192
      + memory_limit                            = -1
      + memory_share_count                      = (known after apply)
      + memory_share_level                      = "normal"
      + migrate_wait_timeout                    = 30
      + moid                                    = (known after apply)
      + name                                    = "TF-CVAD-V-DDC1"
      + num_cores_per_socket                    = 1
      + num_cpus                                = 2
      + nvme_controller_count                   = 0
      + power_state                             = (known after apply)
      + poweron_timeout                         = 300
      + reboot_required                         = (known after apply)
      + resource_pool_id                        = "resgroup-18"
      + run_tools_scripts_after_power_on        = true
      + run_tools_scripts_after_resume          = true
      + run_tools_scripts_before_guest_shutdown = true
      + run_tools_scripts_before_guest_standby  = true
      + sata_controller_count                   = 0
      + scsi_bus_sharing                        = "noSharing"
      + scsi_controller_count                   = 1
      + scsi_type                               = "lsilogic-sas"
      + shutdown_wait_timeout                   = 3
      + storage_policy_id                       = (known after apply)
      + swap_placement_policy                   = "inherit"
      + sync_time_with_host                     = true
      + tools_upgrade_policy                    = "manual"
      + uuid                                    = (known after apply)
      + vapp_transport                          = (known after apply)
      + vmware_tools_status                     = (known after apply)
      + vmx_path                                = (known after apply)
      + wait_for_guest_ip_timeout               = 0
      + wait_for_guest_net_routable             = true
      + wait_for_guest_net_timeout              = 5

      + cdrom {
          + datastore_id   = "datastore-11"
          + device_address = (known after apply)
          + key            = (known after apply)
          + path           = "TF-CVAD-V-ISO/Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso"
        }

      + clone {
          + template_uuid = "422a5cdf-8e0a-339e-e7d4-a674484d2eba"
          + timeout       = 30

          + customize {
              + ipv4_gateway = "10.10.0.1"
              + timeout      = 10

              + network_interface {
                  + dns_server_list = [
                      + "10.10.100.1",
                    ]
                  + ipv4_address    = "10.10.119.31"
                  + ipv4_netmask    = 16
                }

              + windows_options {
                  + auto_logon_count  = 1
                  + computer_name     = "tf-cvad-v-ddc1"
                  + full_name         = "Administrator"
                  + organization_name = "Managed by Terraform"
                  + time_zone         = 85
                }
            }
        }

      + disk {
          + attach            = false
          + controller_type   = "scsi"
          + datastore_id      = "&lt;computed&gt;"
          + device_address    = (known after apply)
          + disk_mode         = "persistent"
          + disk_sharing      = "sharingNone"
          + eagerly_scrub     = false
          + io_limit          = -1
          + io_reservation    = 0
          + io_share_count    = 0
          + io_share_level    = "normal"
          + keep_on_remove    = false
          + key               = 0
          + label             = "0"
          + path              = (known after apply)
          + size              = 90
          + storage_policy_id = (known after apply)
          + thin_provisioned  = true
          + unit_number       = 0
          + uuid              = (known after apply)
          + write_through     = false
        }

      + network_interface {
          + adapter_type          = "vmxnet3"
          + bandwidth_limit       = -1
          + bandwidth_reservation = 0
          + bandwidth_share_count = (known after apply)
          + bandwidth_share_level = "normal"
          + device_address        = (known after apply)
          + key                   = (known after apply)
          + mac_address           = (known after apply)
          + network_id            = "network-12"
        }
    }

  # vsphere_virtual_machine.ddc2-vm will be created
  + resource "vsphere_virtual_machine" "ddc2-vm" {
      + annotation                              = (known after apply)
      + boot_retry_delay                        = 10000
      + change_version                          = (known after apply)
      + cpu_limit                               = -1
      + cpu_share_count                         = (known after apply)
      + cpu_share_level                         = "normal"
      + datastore_id                            = "datastore-11"
      + default_ip_address                      = (known after apply)
      + ept_rvi_mode                            = (known after apply)
      + extra_config_reboot_required            = true
      + firmware                                = "efi"
      + force_power_off                         = true
      + guest_id                                = "windows2019srv_64Guest"
      + guest_ip_addresses                      = (known after apply)
      + hardware_version                        = (known after apply)
      + host_system_id                          = (known after apply)
      + hv_mode                                 = (known after apply)
      + id                                      = (known after apply)
      + ide_controller_count                    = 2
      + imported                                = (known after apply)
      + latency_sensitivity                     = "normal"
      + memory                                  = 8192
      + memory_limit                            = -1
      + memory_share_count                      = (known after apply)
      + memory_share_level                      = "normal"
      + migrate_wait_timeout                    = 30
      + moid                                    = (known after apply)
      + name                                    = "TF-CVAD-V-DDC2"
      + num_cores_per_socket                    = 1
      + num_cpus                                = 2
      + nvme_controller_count                   = 0
      + power_state                             = (known after apply)
      + poweron_timeout                         = 300
      + reboot_required                         = (known after apply)
      + resource_pool_id                        = "resgroup-18"
      + run_tools_scripts_after_power_on        = true
      + run_tools_scripts_after_resume          = true
      + run_tools_scripts_before_guest_shutdown = true
      + run_tools_scripts_before_guest_standby  = true
      + sata_controller_count                   = 0
      + scsi_bus_sharing                        = "noSharing"
      + scsi_controller_count                   = 1
      + scsi_type                               = "lsilogic-sas"
      + shutdown_wait_timeout                   = 3
      + storage_policy_id                       = (known after apply)
      + swap_placement_policy                   = "inherit"
      + sync_time_with_host                     = true
      + tools_upgrade_policy                    = "manual"
      + uuid                                    = (known after apply)
      + vapp_transport                          = (known after apply)
      + vmware_tools_status                     = (known after apply)
      + vmx_path                                = (known after apply)
      + wait_for_guest_ip_timeout               = 0
      + wait_for_guest_net_routable             = true
      + wait_for_guest_net_timeout              = 5

      + cdrom {
          + datastore_id   = "datastore-11"
          + device_address = (known after apply)
          + key            = (known after apply)
          + path           = "TF-CVAD-V-ISO/Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso"
        }

      + clone {
          + template_uuid = "422a5cdf-8e0a-339e-e7d4-a674484d2eba"
          + timeout       = 30

          + customize {
              + ipv4_gateway = "10.10.0.1"
              + timeout      = 10

              + network_interface {
                  + dns_server_list = [
                      + "10.10.100.1",
                    ]
                  + ipv4_address    = "10.10.119.32"
                  + ipv4_netmask    = 16
                }

              + windows_options {
                  + auto_logon_count  = 1
                  + computer_name     = "tf-cvad-v-ddc2"
                  + full_name         = "Administrator"
                  + organization_name = "Managed by Terraform"
                  + time_zone         = 85
                }
            }
        }

      + disk {
          + attach            = false
          + controller_type   = "scsi"
          + datastore_id      = "&lt;computed&gt;"
          + device_address    = (known after apply)
          + disk_mode         = "persistent"
          + disk_sharing      = "sharingNone"
          + eagerly_scrub     = false
          + io_limit          = -1
          + io_reservation    = 0
          + io_share_count    = 0
          + io_share_level    = "normal"
          + keep_on_remove    = false
          + key               = 0
          + label             = "0"
          + path              = (known after apply)
          + size              = 90
          + storage_policy_id = (known after apply)
          + thin_provisioned  = true
          + unit_number       = 0
          + uuid              = (known after apply)
          + write_through     = false
        }

      + network_interface {
          + adapter_type          = "vmxnet3"
          + bandwidth_limit       = -1
          + bandwidth_reservation = 0
          + bandwidth_share_count = (known after apply)
          + bandwidth_share_level = "normal"
          + device_address        = (known after apply)
          + key                   = (known after apply)
          + mac_address           = (known after apply)
          + network_id            = "network-12"
        }
    }

  # vsphere_virtual_machine.sf-vm will be created
  + resource "vsphere_virtual_machine" "sf-vm" {
      + annotation                              = (known after apply)
      + boot_retry_delay                        = 10000
      + change_version                          = (known after apply)
      + cpu_limit                               = -1
      + cpu_share_count                         = (known after apply)
      + cpu_share_level                         = "normal"
      + datastore_id                            = "datastore-11"
      + default_ip_address                      = (known after apply)
      + ept_rvi_mode                            = (known after apply)
      + extra_config_reboot_required            = true
      + firmware                                = "efi"
      + force_power_off                         = true
      + guest_id                                = "windows2019srv_64Guest"
      + guest_ip_addresses                      = (known after apply)
      + hardware_version                        = (known after apply)
      + host_system_id                          = (known after apply)
      + hv_mode                                 = (known after apply)
      + id                                      = (known after apply)
      + ide_controller_count                    = 2
      + imported                                = (known after apply)
      + latency_sensitivity                     = "normal"
      + memory                                  = 8192
      + memory_limit                            = -1
      + memory_share_count                      = (known after apply)
      + memory_share_level                      = "normal"
      + migrate_wait_timeout                    = 30
      + moid                                    = (known after apply)
      + name                                    = "TF-CVAD-V-SF"
      + num_cores_per_socket                    = 1
      + num_cpus                                = 2
      + nvme_controller_count                   = 0
      + power_state                             = (known after apply)
      + poweron_timeout                         = 300
      + reboot_required                         = (known after apply)
      + resource_pool_id                        = "resgroup-18"
      + run_tools_scripts_after_power_on        = true
      + run_tools_scripts_after_resume          = true
      + run_tools_scripts_before_guest_shutdown = true
      + run_tools_scripts_before_guest_standby  = true
      + sata_controller_count                   = 0
      + scsi_bus_sharing                        = "noSharing"
      + scsi_controller_count                   = 1
      + scsi_type                               = "lsilogic-sas"
      + shutdown_wait_timeout                   = 3
      + storage_policy_id                       = (known after apply)
      + swap_placement_policy                   = "inherit"
      + sync_time_with_host                     = true
      + tools_upgrade_policy                    = "manual"
      + uuid                                    = (known after apply)
      + vapp_transport                          = (known after apply)
      + vmware_tools_status                     = (known after apply)
      + vmx_path                                = (known after apply)
      + wait_for_guest_ip_timeout               = 0
      + wait_for_guest_net_routable             = true
      + wait_for_guest_net_timeout              = 5

      + cdrom {
          + datastore_id   = "datastore-11"
          + device_address = (known after apply)
          + key            = (known after apply)
          + path           = "TF-CVAD-V-ISO/Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso"
        }

      + clone {
          + template_uuid = "422a5cdf-8e0a-339e-e7d4-a674484d2eba"
          + timeout       = 30

          + customize {
              + ipv4_gateway = "10.10.0.1"
              + timeout      = 10

              + network_interface {
                  + dns_server_list = [
                      + "10.10.100.1",
                    ]
                  + ipv4_address    = "10.10.119.33"
                  + ipv4_netmask    = 16
                }

              + windows_options {
                  + auto_logon_count  = 1
                  + computer_name     = "tf-cvad-v-sf"
                  + full_name         = "Administrator"
                  + organization_name = "Managed by Terraform"
                  + time_zone         = 85
                }
            }
        }

      + disk {
          + attach            = false
          + controller_type   = "scsi"
          + datastore_id      = "&lt;computed&gt;"
          + device_address    = (known after apply)
          + disk_mode         = "persistent"
          + disk_sharing      = "sharingNone"
          + eagerly_scrub     = false
          + io_limit          = -1
          + io_reservation    = 0
          + io_share_count    = 0
          + io_share_level    = "normal"
          + keep_on_remove    = false
          + key               = 0
          + label             = "0"
          + path              = (known after apply)
          + size              = 90
          + storage_policy_id = (known after apply)
          + thin_provisioned  = true
          + unit_number       = 0
          + uuid              = (known after apply)
          + write_through     = false
        }

      + network_interface {
          + adapter_type          = "vmxnet3"
          + bandwidth_limit       = -1
          + bandwidth_reservation = 0
          + bandwidth_share_count = (known after apply)
          + bandwidth_share_level = "normal"
          + device_address        = (known after apply)
          + key                   = (known after apply)
          + mac_address           = (known after apply)
          + network_id            = "network-12"
        }
    }

  # vsphere_virtual_machine.ws-vm will be created
  + resource "vsphere_virtual_machine" "ws-vm" {
      + annotation                              = (known after apply)
      + boot_retry_delay                        = 10000
      + change_version                          = (known after apply)
      + cpu_limit                               = -1
      + cpu_share_count                         = (known after apply)
      + cpu_share_level                         = "normal"
      + datastore_id                            = "datastore-11"
      + default_ip_address                      = (known after apply)
      + ept_rvi_mode                            = (known after apply)
      + extra_config_reboot_required            = true
      + firmware                                = "efi"
      + force_power_off                         = true
      + guest_id                                = "windows2019srv_64Guest"
      + guest_ip_addresses                      = (known after apply)
      + hardware_version                        = (known after apply)
      + host_system_id                          = (known after apply)
      + hv_mode                                 = (known after apply)
      + id                                      = (known after apply)
      + ide_controller_count                    = 2
      + imported                                = (known after apply)
      + latency_sensitivity                     = "normal"
      + memory                                  = 8192
      + memory_limit                            = -1
      + memory_share_count                      = (known after apply)
      + memory_share_level                      = "normal"
      + migrate_wait_timeout                    = 30
      + moid                                    = (known after apply)
      + name                                    = "TF-CVAD-V-WS"
      + num_cores_per_socket                    = 1
      + num_cpus                                = 2
      + nvme_controller_count                   = 0
      + power_state                             = (known after apply)
      + poweron_timeout                         = 300
      + reboot_required                         = (known after apply)
      + resource_pool_id                        = "resgroup-18"
      + run_tools_scripts_after_power_on        = true
      + run_tools_scripts_after_resume          = true
      + run_tools_scripts_before_guest_shutdown = true
      + run_tools_scripts_before_guest_standby  = true
      + sata_controller_count                   = 0
      + scsi_bus_sharing                        = "noSharing"
      + scsi_controller_count                   = 1
      + scsi_type                               = "lsilogic-sas"
      + shutdown_wait_timeout                   = 3
      + storage_policy_id                       = (known after apply)
      + swap_placement_policy                   = "inherit"
      + sync_time_with_host                     = true
      + tools_upgrade_policy                    = "manual"
      + uuid                                    = (known after apply)
      + vapp_transport                          = (known after apply)
      + vmware_tools_status                     = (known after apply)
      + vmx_path                                = (known after apply)
      + wait_for_guest_ip_timeout               = 0
      + wait_for_guest_net_routable             = true
      + wait_for_guest_net_timeout              = 5

      + cdrom {
          + datastore_id   = "datastore-11"
          + device_address = (known after apply)
          + key            = (known after apply)
          + path           = "TF-CVAD-V-ISO/Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso"
        }

      + clone {
          + template_uuid = "422a5cdf-8e0a-339e-e7d4-a674484d2eba"
          + timeout       = 30

          + customize {
              + ipv4_gateway = "10.10.0.1"
              + timeout      = 10

              + network_interface {
                  + dns_server_list = [
                      + "10.10.100.1",
                    ]
                  + ipv4_address    = "10.10.119.34"
                  + ipv4_netmask    = 16
                }

              + windows_options {
                  + auto_logon_count  = 1
                  + computer_name     = "tf-cvad-v-ws"
                  + full_name         = "Administrator"
                  + organization_name = "Managed by Terraform"
                  + time_zone         = 85
                }
            }
        }

      + disk {
          + attach            = false
          + controller_type   = "scsi"
          + datastore_id      = "&lt;computed&gt;"
          + device_address    = (known after apply)
          + disk_mode         = "persistent"
          + disk_sharing      = "sharingNone"
          + eagerly_scrub     = false
          + io_limit          = -1
          + io_reservation    = 0
          + io_share_count    = 0
          + io_share_level    = "normal"
          + keep_on_remove    = false
          + key               = 0
          + label             = "0"
          + path              = (known after apply)
          + size              = 90
          + storage_policy_id = (known after apply)
          + thin_provisioned  = true
          + unit_number       = 0
          + uuid              = (known after apply)
          + write_through     = false
        }

      + network_interface {
          + adapter_type          = "vmxnet3"
          + bandwidth_limit       = -1
          + bandwidth_reservation = 0
          + bandwidth_share_count = (known after apply)
          + bandwidth_share_level = "normal"
          + device_address        = (known after apply)
          + key                   = (known after apply)
          + mac_address           = (known after apply)
          + network_id            = "network-12"
        }
    }

Plan: 4 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

vsphere_virtual_machine.ws-vm: Creating...
vsphere_virtual_machine.ddc1-vm: Creating...
vsphere_virtual_machine.ddc2-vm: Creating...
vsphere_virtual_machine.sf-vm: Creating...
vsphere_virtual_machine.ws-vm: Still creating... [00m10s elapsed]
vsphere_virtual_machine.ddc1-vm: Still creating... [00m10s elapsed]
vsphere_virtual_machine.ddc2-vm: Still creating... [00m10s elapsed]
vsphere_virtual_machine.sf-vm: Still creating... [00m10s elapsed]
vsphere_virtual_machine.ws-vm: Still creating... [00m20s elapsed]
vsphere_virtual_machine.ddc1-vm: Still creating... [00m20s elapsed]
vsphere_virtual_machine.ddc2-vm: Still creating... [00m20s elapsed]
vsphere_virtual_machine.sf-vm: Still creating... [00m20s elapsed]
vsphere_virtual_machine.ws-vm: Still creating... [00m30s elapsed]
vsphere_virtual_machine.ddc1-vm: Still creating... [00m30s elapsed]
vsphere_virtual_machine.ddc2-vm: Still creating... [00m30s elapsed]
vsphere_virtual_machine.sf-vm: Still creating... [00m30s elapsed]
vsphere_virtual_machine.ws-vm: Still creating... [00m40s elapsed]
vsphere_virtual_machine.ddc1-vm: Still creating... [00m40s elapsed]
vsphere_virtual_machine.ddc2-vm: Still creating... [00m40s elapsed]
vsphere_virtual_machine.sf-vm: Still creating... [00m40s elapsed]
vsphere_virtual_machine.ws-vm: Still creating... [00m50s elapsed]
vsphere_virtual_machine.ddc1-vm: Still creating... [00m50s elapsed]
vsphere_virtual_machine.ddc2-vm: Still creating... [00m50s elapsed]
vsphere_virtual_machine.sf-vm: Still creating... [00m50s elapsed]
vsphere_virtual_machine.ws-vm: Still creating... [01m00s elapsed]
vsphere_virtual_machine.ddc1-vm: Still creating... [01m00s elapsed]
vsphere_virtual_machine.ddc2-vm: Still creating... [01m00s elapsed]
vsphere_virtual_machine.sf-vm: Still creating... [01m00s elapsed]
vsphere_virtual_machine.ws-vm: Still creating... [01m10s elapsed]
vsphere_virtual_machine.ddc1-vm: Still creating... [01m10s elapsed]
vsphere_virtual_machine.ddc2-vm: Still creating... [01m10s elapsed]
vsphere_virtual_machine.sf-vm: Still creating... [01m10s elapsed]
vsphere_virtual_machine.ws-vm: Still creating... [01m20s elapsed]
vsphere_virtual_machine.ddc1-vm: Still creating... [01m20s elapsed]
vsphere_virtual_machine.ddc2-vm: Still creating... [01m20s elapsed]
vsphere_virtual_machine.sf-vm: Still creating... [01m20s elapsed]
vsphere_virtual_machine.ws-vm: Still creating... [01m30s elapsed]
vsphere_virtual_machine.ddc1-vm: Still creating... [01m30s elapsed]
vsphere_virtual_machine.ddc2-vm: Still creating... [01m30s elapsed]
vsphere_virtual_machine.sf-vm: Still creating... [01m30s elapsed]
vsphere_virtual_machine.ws-vm: Still creating... [01m40s elapsed]
vsphere_virtual_machine.ddc1-vm: Still creating... [01m40s elapsed]
vsphere_virtual_machine.ddc2-vm: Still creating... [01m40s elapsed]
vsphere_virtual_machine.sf-vm: Still creating... [01m40s elapsed]
vsphere_virtual_machine.ws-vm: Still creating... [01m50s elapsed]
vsphere_virtual_machine.ddc1-vm: Still creating... [01m50s elapsed]
vsphere_virtual_machine.ddc2-vm: Still creating... [01m50s elapsed]
vsphere_virtual_machine.sf-vm: Still creating... [01m50s elapsed]
vsphere_virtual_machine.ws-vm: Still creating... [02m00s elapsed]
vsphere_virtual_machine.ddc1-vm: Still creating... [02m00s elapsed]
vsphere_virtual_machine.ddc2-vm: Still creating... [02m00s elapsed]
vsphere_virtual_machine.sf-vm: Still creating... [02m00s elapsed]
vsphere_virtual_machine.ws-vm: Still creating... [02m10s elapsed]
vsphere_virtual_machine.ddc1-vm: Still creating... [02m10s elapsed]
vsphere_virtual_machine.ddc2-vm: Still creating... [02m10s elapsed]
vsphere_virtual_machine.sf-vm: Still creating... [02m10s elapsed]
vsphere_virtual_machine.ddc2-vm: Creation complete after 2m17s [id=422aea50-ccb4-77c2-a72e-dea631b95b29]
vsphere_virtual_machine.ws-vm: Still creating... [02m20s elapsed]
vsphere_virtual_machine.ddc1-vm: Still creating... [02m20s elapsed]
vsphere_virtual_machine.sf-vm: Still creating... [02m20s elapsed]
vsphere_virtual_machine.sf-vm: Creation complete after 2m26s [id=422adb85-f8e7-5582-de6d-06b339bd2c26]
vsphere_virtual_machine.ddc1-vm: Creation complete after 2m26s [id=422a3cc3-34b9-d92c-d507-1d13995645a5]
vsphere_virtual_machine.ws-vm: Still creating... [02m30s elapsed]
vsphere_virtual_machine.ws-vm: Creation complete after 2m36s [id=422a774a-03fc-1cfd-a07e-f36471098f09]

Apply complete! Resources: 4 added, 0 changed, 0 destroyed.

azadmin@tacg-cicd:/etc/terraform-tmm/CVADVS/_Creation$vSphere reflects the successful run:  Since the VMs have been created, we can proceed to the next step.  Part 3: Using Terraform and Ansible to configure the VMsThis configuration block now uses the combination of Terraform and Ansible for all needed configuration steps. As mentioned, Terraform excels at creating components; Ansible excels at configuring them. Together, we have everything we need for a successful deployment. Note: In all our guides, we try to be as Hypervisor- and Hyperscaler-agnostic as possible. For example, the vSphere provider could add a VM to an AD domain, but not all providers support this, and each provider uses a different syntax. Using the same Ansible playbook across all Hypervisors/Hyperscalers, regardless of built-in capabilities, also improves code reuse, as no specific code needs to be changed for different Hypervisor-/Hyperscaler deployments. This block also shows the main flow: Terraform decides which steps are necessary and starts the corresponding Ansible playbooks to achieve the needed outcome.  The following configuration steps are sequentially done: Change the Ansible configuration file to reflect the IP changes Put the VMs into the AD domain Usually, when VMs are created, they will receive an IP address from a DHCP server. This is not true for the vSphere provider – there, you can provide the fixed IP addresses during creation. As Ansible needs to know how to contact the VM, the IP settings must be reflected in Ansible´s etc/ansible/HOSTS file: ...
[ddc1_ipaddr]
10.10.119.31

[ddc2_ipaddr]
10.10.119.32

[sf_ipaddr]
10.10.119.33

[ws_ipaddr]
10.10.119.34
...Typically, our agnostic Ansible snippet would take care of DHCP-based IP addresses and would change all relevant settings to ensure the usage of all communications using the fixed IP addresses, You can find more information about that process in our guide, "Using Infrastructure-as-Code to deploy Citrix Virtual Apps and Desktops 2507 LTSR on XenServer 8.4". Terraform usually takes three steps to correct that: 1.        It changes the IPs to fixed IPs defined in the Ansible playbook's corresponding variables 2.       The etc/ansible/HOSTS file is updated with the correct IPs 3.      The VMs are added to the domain. In the vSphere case, only the third step is used. Terraform monitors each Ansible Playbook run and stops further steps if a Playbook fails. Example Terraform snippet: # Terraform Deployment of Citrix CVAD on vSphere 8 - 25.11 
## Definition of vSphere Provider Variables - not all available variables are defined, only these we use
### Reference https://registry.terraform.io/providers/vmware/vsphere/latest/docs

## Definition of all required local variables
### Get Needed Data
#### Call Ansible to Join VM to Domain
##### Copy Ansible Playbooks to Ansible Server

###### ***** IMPORTANT: You must set the permissions to the destination directory accordingly (sudo) chmod 777 otherwise the provisioning will fail *****
resource "null_resource" "CopyPlaybooksForCC1ToAnsibleServer" {
  connection {
    type     = var.TACG-TMM-VSP-CL-Ansible-Connection-Type
    user     = var.TACG-TMM-VSP-CL-Ansible-Connection-User
    password = var.TACG-TMM-VSP-CL-Ansible-Connection-Password
    host     = var.TACG-TMM-VSP-CL-Ansible-Connection-HostIP
    port     = var.TACG-TMM-VSP-CL-Ansible-Connection-Port
    https    = var.TACG-TMM-VSP-CL-Ansible-Connection-HTTPS
    timeout  = var.TACG-TMM-VSP-CL-Ansible-Connection-Timeout
  }

  provisioner "file" {
    source      = var.TACG-TMM-VSP-CL-Ansible-DomainJoinPlaybook-CC1-Source
    destination = var.TACG-TMM-VSP-CL-Ansible-DomainJoinPlaybook-CC1-Destination
  }
}

resource "null_resource" "CopyPlaybooksForCC2ToAnsibleServer" {
  connection {
    type     = var.TACG-TMM-VSP-CL-Ansible-Connection-Type
    user     = var.TACG-TMM-VSP-CL-Ansible-Connection-User
    password = var.TACG-TMM-VSP-CL-Ansible-Connection-Password
    host     = var.TACG-TMM-VSP-CL-Ansible-Connection-HostIP
    port     = var.TACG-TMM-VSP-CL-Ansible-Connection-Port
    https    = var.TACG-TMM-VSP-CL-Ansible-Connection-HTTPS
    timeout  = var.TACG-TMM-VSP-CL-Ansible-Connection-Timeout
  }

  provisioner "file" {
    source      = var.TACG-TMM-VSP-CL-Ansible-DomainJoinPlaybook-CC2-Source
    destination = var.TACG-TMM-VSP-CL-Ansible-DomainJoinPlaybook-CC2-Destination
  }

}

resource "null_resource" "CopyPlaybooksForSFToAnsibleServer" {
  connection {
    type     = var.TACG-TMM-VSP-CL-Ansible-Connection-Type
    user     = var.TACG-TMM-VSP-CL-Ansible-Connection-User
    password = var.TACG-TMM-VSP-CL-Ansible-Connection-Password
    host     = var.TACG-TMM-VSP-CL-Ansible-Connection-HostIP
    port     = var.TACG-TMM-VSP-CL-Ansible-Connection-Port
    https    = var.TACG-TMM-VSP-CL-Ansible-Connection-HTTPS
    timeout  = var.TACG-TMM-VSP-CL-Ansible-Connection-Timeout
  }

  provisioner "file" {
    source      = var.TACG-TMM-VSP-CL-Ansible-DomainJoinPlaybook-SF-Source
    destination = var.TACG-TMM-VSP-CL-Ansible-DomainJoinPlaybook-SF-Destination
  }

}

resource "null_resource" "CopyPlaybooksForWebStudioToAnsibleServer" {
  connection {
    type     = var.TACG-TMM-VSP-CL-Ansible-Connection-Type
    user     = var.TACG-TMM-VSP-CL-Ansible-Connection-User
    password = var.TACG-TMM-VSP-CL-Ansible-Connection-Password
    host     = var.TACG-TMM-VSP-CL-Ansible-Connection-HostIP
    port     = var.TACG-TMM-VSP-CL-Ansible-Connection-Port
    https    = var.TACG-TMM-VSP-CL-Ansible-Connection-HTTPS
    timeout  = var.TACG-TMM-VSP-CL-Ansible-Connection-Timeout
  }

  provisioner "file" {
    source      = var.TACG-TMM-VSP-CL-Ansible-DomainJoinPlaybook-WS-Source
    destination = var.TACG-TMM-VSP-CL-Ansible-DomainJoinPlaybook-WS-Destination
  }
}

#### Wait 10s Minute for Background Processes to Settle
resource "time_sleep" "wait_10_seconds" {
  depends_on = [null_resource.CopyPlaybooksForCC1ToAnsibleServer, null_resource.CopyPlaybooksForCC2ToAnsibleServer, null_resource.CopyPlaybooksForSFToAnsibleServer, null_resource.CopyPlaybooksForWebStudioToAnsibleServer]

  create_duration = "10s"
}

##### Connect to Ansible Interpreter and Add DDC1 To Domain
resource "null_resource" "AddCC1ToDomain" {
  depends_on = [time_sleep.wait_10_seconds]

  provisioner "local-exec" {
    working_dir = "/etc/ansible"
    command     = var.TACG-TMM-VSP-CL-Ansible-DomainJoinCMDForCC1
  }
}

##### Check Ansible Return Code
data "external" "CheckAnsibleReturnCodeD1" {
  depends_on = [null_resource.AddCC1ToDomain]
  program    = ["bash", "/etc/ansible/assets/CheckAnsibleReturnCode.sh", "/etc/ansible/assets/ansible_exit_code.txt", "0"]
}

##### Terminate if Return Code is not 0
resource "null_resource" "IfExitCodeIsNotZeroThenHaltD1" {
  depends_on = [data.external.CheckAnsibleReturnCodeD1]
  provisioner "local-exec" {
    interpreter = ["/bin/bash", "-c"]
    command     = "[ \"${data.external.CheckAnsibleReturnCodeD1.result.found}\" = \"1\" ] &amp;&amp; echo \"Adding DDC1 to Domain: Everything OK\" || { echo \"Adding DDC1 to the domain failed. Please review the settings. Halting Terraform.\"; exit 1; }"
  }
}

##### End of necessary changes on DDC1 

#### Wait 10s Minute for Background Processes to Settle
resource "time_sleep" "wait_10_seconds_afterddc1" {
  depends_on = [null_resource.IfExitCodeIsNotZeroThenHaltD1]

  create_duration = "10s"
}

...
The same steps are needed for all the other servers
...

##### All necessary changes on the VMs are completeExample Ansible Playbook for adding the VM to the AD domain: ...
- name: join host to domain with automatic reboot
  hosts: ddc1_ipaddr

  tasks:
  - name: join host to domain with automatic reboot
    microsoft.ad.membership:
      dns_domain_name: the-austrian-citrix-guy.at
      hostname: tf-cvad-v-ddc1
      domain_admin_user: XxXxXxXxX@the-austrian-citrix-guy.at
      domain_admin_password: "!XxXxXxXxXxXxXxXxX!"
      domain_ou_path: "OU=_COMPUTER,OU=TACG-CVAD,DC=the-austrian-citrix-guy,DC=at"
      state: domain
      reboot: trueRunning the snippet should fulfill all needed steps: azadmin@tacg-cicd:/etc/terraform-tmm/CVADVS/_UseAnsibleToJoinDomain$ terraform apply

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create
 &lt;= read (data resources)

Terraform will perform the following actions:

  # data.external.CheckAnsibleReturnCode1 will be read during apply
  # (depends on a resource or a module with changes pending)
 &lt;= data "external" "CheckAnsibleReturnCode1" {
      + id      = (known after apply)
      + program = [
          + "bash",
          + "/etc/ansible/assets/CheckAnsibleReturnCode.sh",
          + "/etc/ansible/assets/ansible_exit_code.txt",
          + "0",
        ]
      + result  = (known after apply)
    }

  # data.external.CheckAnsibleReturnCode2 will be read during apply
  # (depends on a resource or a module with changes pending)
 &lt;= data "external" "CheckAnsibleReturnCode2" {
      + id      = (known after apply)
      + program = [
          + "bash",
          + "/etc/ansible/assets/CheckAnsibleReturnCode.sh",
          + "/etc/ansible/assets/ansible_exit_code.txt",
          + "0",
        ]
      + result  = (known after apply)
    }

  ...

  # time_sleep.wait_10_seconds_afterddc2 will be created
  + resource "time_sleep" "wait_10_seconds_afterddc2" {
      + create_duration = "10s"
      + id              = (known after apply)
    }

  # time_sleep.wait_10_seconds_aftersf will be created
  + resource "time_sleep" "wait_10_seconds_aftersf" {
      + create_duration = "10s"
      + id              = (known after apply)
    }

Plan: 32 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.CopyPlaybooksForWebStudioToAnsibleServer: Creating...
null_resource.CopyPlaybooksForCC2ToAnsibleServer: Creating...
null_resource.CopyPlaybooksForCC1ToAnsibleServer: Creating...
null_resource.CopyPlaybooksForSFToAnsibleServer: Creating...
null_resource.CopyPlaybooksForCC2ToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPlaybooksForCC1ToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPlaybooksForWebStudioToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPlaybooksForSFToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPlaybooksForSFToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPlaybooksForCC2ToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPlaybooksForWebStudioToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPlaybooksForCC1ToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPlaybooksForSFToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPlaybooksForCC2ToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPlaybooksForCC1ToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPlaybooksForWebStudioToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPlaybooksForSFToAnsibleServer: Creation complete after 0s [id=9099395716454281124]
null_resource.CopyPlaybooksForCC2ToAnsibleServer: Creation complete after 1s [id=2239027084142302025]
null_resource.CopyPlaybooksForCC1ToAnsibleServer: Creation complete after 1s [id=8241870746583503880]
null_resource.CopyPlaybooksForWebStudioToAnsibleServer: Creation complete after 1s [id=85414301571074153]
...
time_sleep.wait_10_seconds: Creating...
time_sleep.wait_10_seconds: Still creating... [00m10s elapsed]
time_sleep.wait_10_seconds: Creation complete after 10s [id=2025-11-03T12:50:35Z]
null_resource.AddCC1ToDomain: Creating...
null_resource.AddCC1ToDomain: Provisioning with 'local-exec'...
null_resource.AddCC1ToDomain (local-exec): Executing: ["/bin/sh" "-c" "ansible-playbook /etc/ansible/assets/Join-VMToDomain-DDC1.microsoft.ad.ansible.yml -v -c winrm; echo $? &gt; /etc/ansible/assets/ansible_exit_code.txt"]

null_resource.AddCC1ToDomain (local-exec): PLAY [join host to domain with automatic reboot] *******************************

null_resource.AddCC1ToDomain (local-exec): TASK [Gathering Facts] *********************************************************
null_resource.AddCC1ToDomain (local-exec): ok: [10.10.119.31]

null_resource.AddCC1ToDomain (local-exec): TASK [join host to domain with automatic reboot] *******************************
null_resource.AddCC1ToDomain: Still creating... [00m10s elapsed]
null_resource.AddCC1ToDomain: Still creating... [00m20s elapsed]
null_resource.AddCC1ToDomain (local-exec): changed: [10.10.119.31] =&gt; {"changed": true, "reboot_required": false}

null_resource.AddCC1ToDomain (local-exec): PLAY RECAP *********************************************************************
null_resource.AddCC1ToDomain (local-exec): 10.10.119.31               : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

null_resource.AddCC1ToDomain: Creation complete after 28s [id=6625761552218837946]
data.external.CheckAnsibleReturnCodeD1: Reading...
data.external.CheckAnsibleReturnCodeD1: Read complete after 0s [id=-]
null_resource.IfExitCodeIsNotZeroThenHaltD1: Creating...
null_resource.IfExitCodeIsNotZeroThenHaltD1: Provisioning with 'local-exec'...
null_resource.IfExitCodeIsNotZeroThenHaltD1 (local-exec): Executing: ["/bin/bash" "-c" "[ \"1\" = \"1\" ] &amp;&amp; echo \"Adding DDC1 to Domain: Everything OK\" || { echo \"Adding DDC1 to the domain failed. Please review the settings. Halting Terraform.\"; exit 1; }"]
null_resource.IfExitCodeIsNotZeroThenHaltD1 (local-exec): Adding DDC1 to Domain: Everything OK
null_resource.IfExitCodeIsNotZeroThenHaltD1: Creation complete after 0s [id=7818126381059512591]
time_sleep.wait_10_seconds_afterddc1: Creating...
time_sleep.wait_10_seconds_afterddc1: Still creating... [00m10s elapsed]
time_sleep.wait_10_seconds_afterddc1: Creation complete after 10s [id=2025-11-16T12:51:13Z]
...
All these steps are the same for the other three servers.
Output omitted due to length
...
data.external.CheckAnsibleReturnCodeDWS: Reading...
data.external.CheckAnsibleReturnCodeDWS: Read complete after 0s [id=-]
null_resource.IfExitCodeIsNotZeroThenHaltDWS: Creating...
null_resource.IfExitCodeIsNotZeroThenHaltDWS: Provisioning with 'local-exec'...
null_resource.IfExitCodeIsNotZeroThenHaltDWS (local-exec): Executing: ["/bin/bash" "-c" "[ \"1\" = \"1\" ] &amp;&amp; echo \"Adding WebStudio to Domain: Everything OK\" || { echo \"Adding WebStudio to the domain failed. Please review the settings. Halting Terraform.\"; exit 1; }"]
null_resource.IfExitCodeIsNotZeroThenHaltDWS (local-exec): Adding WebStudio to Domain: Everything OK
null_resource.IfExitCodeIsNotZeroThenHaltDWS: Creation complete after 0s [id=975638781208917805]

Apply complete! Resources: 32 added, 0 changed, 0 destroyed.
azadmin@tacg-cicd:/etc/terraform-tmm/CVADVS/_UseAnsibleToJoinDomain$All VMs are added to the AD domain:  Ansible´s further used inventory.ini file also reflects all these changes: [cvad:children]
cvad-ddcs
cvad-sf-vs
cvad-ws
cvad-ddcs-v
cvad-sf-vs-v
cvad-ws-v

...
[cvad-ddcs-v]
tf-cvad-v-ddc1.the-austrian-citrix-guy.at
tf-cvad-v-ddc2.the-austrian-citrix-guy.at

[cvad-ddc1-v]
tf-cvad-v-ddc1.the-austrian-citrix-guy.at

[cvad-ddc2-v]
tf-cvad-v-ddc2.the-austrian-citrix-guy.at

[cvad-ddc1-v-nb]
TF-CVAD-V-DDC1

[cvad-ddc2-v-nb]
TF-CVAD-V-DDC2

[cvad-sf-vs-v]
tf-cvad-v-sf.the-austrian-citrix-guy.at

[cvad-ws-v]
tf-cvad-v-ws.the-austrian-citrix-guy.at
...

[all:vars]
ansible_user="svc_ansible"
ansible_domainuser="XxXxXxXxX@the-austrian-citrix-guy.at"
ansible_password="!XxXxXxXxXxXxXxXxXx!”
ansible_connection=winrm
ansible_winrm_scheme=https
ansible_winrm_transport=certificate
ansible_winrm_port=5986
ansible_winrm_cert_pem=/etc/ssl/certs/svc_ansible.pem
ansible_winrm_cert_key_pem=/etc/ssl/certs/svc_ansible.pemSince the VMs and their dependencies were successfully reconfigured, we can proceed to the next step.  Part 4: Using Terraform and Ansible to deploy the Delivery ControllersThe VMs dedicated to holding the Delivery Controller™ roles are now ready for the deployment of the Delivery Controller components. This is a multi-step approach: Mount the CVAD ISO installer Copy the publicly signed SSL certificate to the DDCs Install all required server roles and features before installing the DDCs Change some necessary registry settings Therefore, we again use the well-proven combination of Terraform and Ansible. Initially, no Citrix software is installed on the VMs:  Terraform monitors each Ansible Playbook run and stops further steps if a Playbook fails. Example Terraform snippet: # Terraform Deployment of Citrix CVAD on vSphere 8 - 25.11 
## Definition of vSphere Provider Variables - not all available variables are defined, only these we use
### Reference https://registry.terraform.io/providers/vmware/vsphere/latest/docs
#### Copy the required Ansible Assets to the Ansible Server
###### ***** IMPORTANT: You must set the permissions to the destination directory accordingly (sudo) chmod 777 otherwise the provisioning will fail *****
resource "null_resource" "CopyAnsibleDDCAssetsToAnsibleServer" {
  connection {
    type     = var.TACG-TMM-VSP-CL-Ansible-Connection-Type
    user     = var.TACG-TMM-VSP-CL-Ansible-Connection-User
    password = var.TACG-TMM-VSP-CL-Ansible-Connection-Password
    host     = var.TACG-TMM-VSP-CL-Ansible-Connection-HostIP
    port     = var.TACG-TMM-VSP-CL-Ansible-Connection-Port
    https    = var.TACG-TMM-VSP-CL-Ansible-Connection-HTTPS
    timeout  = var.TACG-TMM-VSP-CL-Ansible-Connection-Timeout
  }

  # Copy vars-ddcs.yml file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-VSP-CL-Ansible-Vars-Source
    destination = var.TACG-TMM-VSP-CL-Ansible-Vars-Destination
  }

  # Copy inventory.ini file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-VSP-CL-Ansible-Inventory-Source
    destination = var.TACG-TMM-VSP-CL-Ansible-Inventory-Destination
  }

  # Copy InitialConfig Playbook file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-VSP-CL-Ansible-InitialPlaybookForDDCs-Source
    destination = var.TACG-TMM-VSP-CL-Ansible-InitialPlaybookForDDCs-Destination
  }
}

#### Wait 10s for Backgroud Processes to Settle
resource "time_sleep" "wait_10_seconds" {
  depends_on      = [null_resource.CopyAnsibleDDCAssetsToAnsibleServer]
  create_duration = "10s"
}

##### Connect to Ansible Interpreter and run Initial Configuration Playbook on DDCs
resource "null_resource" "RunInitialPlaybook" {
  depends_on = [time_sleep.wait_10_seconds]

  provisioner "local-exec" {
    working_dir = "/etc/ansible"
    command     = var.TACG-TMM-VSP-CL-Ansible-InitialCMDForDDCs
  }
}

##### Check Ansible Return Code
data "external" "CheckAnsibleReturnCode" {
  depends_on = [null_resource.RunInitialPlaybook]
  program    = ["bash", "/etc/ansible/assets/CheckAnsibleReturnCode.sh", "/etc/ansible/assets/ansible_exit_code.txt", "0"]
}

##### Terminate if Return Code is not 0
resource "null_resource" "IfExitCodeIsNotZeroThenHalt" {
  depends_on = [data.external.CheckAnsibleReturnCode]
  provisioner "local-exec" {
    interpreter = ["/bin/bash", "-c"]
    command     = "[ \"${data.external.CheckAnsibleReturnCode.result.found}\" = \"1\" ] &amp;&amp; echo \"Deploying CVAD: Everything OK\" || { echo \"Deploying the CVAD software and its prerequisites failed. Please review the settings. Halting Terraform.\"; exit 1; }"
  }
}

Example Ansible Playbook for deploying and configuring the DDCs and all needed further configurations: ---
# Terraform Deployment of Citrix CVAD on vSphere 8 - 25.11 
## For installation and configuration of the Delivery Controllers, StoreFront-Servers and WebStudio-Servers, we use Ansible Playbooks 
### Reference https://docs.ansible.com/ansible/latest/getting_started/index.html
#### We assume that the communication between the Ansible Server and all needed Hosts via WinRM is working as intended - check your Group Policies
##### All hosts and its corresponding communication-related Ansible variables are stored in assets/inventory.ini

###### Prepare the Delivery Controllers
- name: Configure the DDCs initially
  hosts: cvad-ddcs-v
  vars_files:
    - ./vars-ddcs.yml
  tasks:

  - name: Create Installer directory  
    ansible.windows.win_file: 
      path: "{{ installdir }}"
      state: directory

  - name: Create Log Folder
    ansible.windows.win_file:
      path: "{{ logdir }}"
      state: directory

  - name: Copy Citrix iso to the host
    ansible.windows.win_copy:
      src: "{{ sourceiso }}"
      dest: "{{ destiso }}" 
      force: no

  - name: Copy the SSL certificate to the hosts
    ansible.windows.win_copy:
      src: "{{ certificate_source_path }}"
      dest: "{{ certificate_destination_path }}" 
      force: no

  - name: Install Required Server Roles for Citrix
    ansible.windows.win_feature:
      name: "{{ ddc_req_roles }}"
      state: present
    register: requirements

  - name: Reboot when Required Roles need a restart
    ansible.windows.win_reboot:
      post_reboot_delay: 120
    when: requirements.reboot_required

  - name: Install SSL-Certificate on Servers
    ansible.windows.win_certificate_store:
      path: "{{ certificate_destination_path }}" 
      password: "{{ certificate_pfx_password }}"
      key_exportable: yes
      file_type: pkcs12
      store_location: LocalMachine
      key_storage: machine
      state: present
    become: true
    become_method: runas
    become_user: SYSTEM

  - name: Run installer from mounted ISO
    ansible.windows.win_package:
      path: "{{ install_exe }} "
      arguments: "{{ install_params }}"
      state: present
      expected_return_code: [0, 3, 3010]
      creates_path: C:\Program Files\Citrix\Desktop Studio
    register: ddc_install

  - name: Reboot after DDC
    ansible.windows.win_reboot:
    when: ddc_install.changed

  - name: Remove DDC RunOnce Key
    ansible.windows.win_regedit:
      path: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce
      name: "!XenDesktopSetup"
      state: absent
    register: ddc_resume

  - name: Resume DDC Install
    ansible.windows.win_package:
      path: C:\ProgramData\Citrix\XenDesktopSetup\XenDesktopServerSetup.exe
      state: present
      expected_return_code: [0, 3, 3010]
      creates_path: C:\Program Files\Citrix\Desktop Studio
    when: ddc_resume.changed

  - name: Alter unwanted Registry Keys-UserIEESC
    ansible.windows.win_regedit:
      path: HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A8-37EF-4b3f-8CFC-4F3A74704073}
      name: IsInstalled
      data: 0

  - name: Alter unwanted Registry Keys-AdminIEESC
    ansible.windows.win_regedit:
      path: HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A7-37EF-4b3f-8CFC-4F3A74704073}
      name: IsInstalled
      data: 0

  - name: Restart a service
    ansible.windows.win_service:
      name: CitrixHostService
      state: startedRunning the snippet should fulfill all needed steps: azadmin@tacg-cicd:/etc/terraform-tmm/CVADVS/_InstallCVAD$ terraform apply

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
  + create
 &lt;= read (data resources)

Terraform will perform the following actions:

  # data.external.CheckAnsibleReturnCode will be read during apply
  # (depends on a resource or a module with changes pending)
 &lt;= data "external" "CheckAnsibleReturnCode" {
      + id      = (known after apply)
      + program = [
          + "bash",
          + "/etc/ansible/assets/CheckAnsibleReturnCode.sh",
          + "/etc/ansible/assets/ansible_exit_code.txt",
          + "0",
        ]
      + result  = (known after apply)
    }

  # null_resource.CopyAnsibleDDCAssetsToAnsibleServer will be created
  + resource "null_resource" "CopyAnsibleDDCAssetsToAnsibleServer" {
      + id = (known after apply)
    }

  # null_resource.IfExitCodeIsNotZeroThenHalt will be created
  + resource "null_resource" "IfExitCodeIsNotZeroThenHalt" {
      + id = (known after apply)
    }

  # null_resource.RunInitialPlaybook will be created
  + resource "null_resource" "RunInitialPlaybook" {
      + id = (known after apply)
    }

  # time_sleep.wait_10_seconds will be created
  + resource "time_sleep" "wait_10_seconds" {
      + create_duration = "10s"
      + id              = (known after apply)
    }

Plan: 4 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.CopyAnsibleDDCAssetsToAnsibleServer: Creating...
null_resource.CopyAnsibleDDCAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyAnsibleDDCAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyAnsibleDDCAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyAnsibleDDCAssetsToAnsibleServer: Creation complete after 0s [id=3392935108841645613]
time_sleep.wait_10_seconds: Creating...
time_sleep.wait_10_seconds: Still creating... [00m10s elapsed]
time_sleep.wait_10_seconds: Creation complete after 10s [id=2025-11-18T07:13:29Z]
null_resource.RunInitialPlaybook: Creating...
null_resource.RunInitialPlaybook: Provisioning with 'local-exec'...
null_resource.RunInitialPlaybook (local-exec): Executing: ["/bin/sh" "-c" "ansible-playbook /etc/ansible/assets/InitialConfigurationForDDCs.ansible.yml -i /etc/ansible/assets/inventory.ini -v -c winrm; echo $? &gt; /etc/ansible/assets/ansible_exit_code.txt"]
null_resource.RunInitialPlaybook (local-exec): Using /etc/ansible/ansible.cfg as config file

null_resource.RunInitialPlaybook (local-exec): PLAY [Configure the DDCs initially] ********************************************

null_resource.RunInitialPlaybook (local-exec): TASK [Gathering Facts] *********************************************************
null_resource.RunInitialPlaybook (local-exec): ok: [tf-cvad-v-ddc1.the-austrian-citrix-guy.at]
null_resource.RunInitialPlaybook (local-exec): ok: [tf-cvad-v-ddc2.the-austrian-citrix-guy.at]

null_resource.RunInitialPlaybook (local-exec): TASK [Create Installer directory] **********************************************
null_resource.RunInitialPlaybook (local-exec): ok: [tf-cvad-v-ddc2.the-austrian-citrix-guy.at] =&gt; {"changed": false}
null_resource.RunInitialPlaybook (local-exec): ok: [tf-cvad-v-ddc1.the-austrian-citrix-guy.at] =&gt; {"changed": false}

null_resource.RunInitialPlaybook (local-exec): TASK [Create Log Folder] *******************************************************
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-v-ddc1.the-austrian-citrix-guy.at] =&gt; {"changed": true}
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-v-ddc2.the-austrian-citrix-guy.at] =&gt; {"changed": true}

null_resource.RunInitialPlaybook (local-exec): TASK [Copy Citrix iso to the host] *********************************************
null_resource.RunInitialPlaybook: Still creating... [00m10s elapsed]
null_resource.RunInitialPlaybook: Still creating... [00m20s elapsed]

...

null_resource.RunInitialPlaybook: Still creating... [12m10s elapsed]
null_resource.RunInitialPlaybook: Still creating... [12m20s elapsed]
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-v-ddc1.the-austrian-citrix-guy.at] =&gt; {"changed": true, "checksum": null, "dest": "C:\\InstallCVAD\\Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso", "operation": "file_copy", "original_basename": "Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso", "size": 4124442624, "src": "/etc/ansible/isos/Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso"}
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-v-ddc2.the-austrian-citrix-guy.at] =&gt; {"changed": true, "checksum": null, "dest": "C:\\InstallCVAD\\Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso", "operation": "file_copy", "original_basename": "Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso", "size": 4124442624, "src": "/etc/ansible/isos/Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso"}

null_resource.RunInitialPlaybook (local-exec): TASK [Copy the SSL certificate to the hosts] ***********************************
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-v-ddc2.the-austrian-citrix-guy.at] =&gt; {"changed": true, "checksum": null, "dest": "C:\\InstallCVAD\\2025-wildcard.the-austrian-citrix-guy.at.PFX", "operation": "file_copy", "original_basename": "2025-wildcard.the-austrian-citrix-guy.at.PFX", "size": 3593, "src": "/etc/ansible/assets/2025-wildcard.the-austrian-citrix-guy.at.PFX"}
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-v-ddc1.the-austrian-citrix-guy.at] =&gt; {"changed": true, "checksum": null, "dest": "C:\\InstallCVAD\\2025-wildcard.the-austrian-citrix-guy.at.PFX", "operation": "file_copy", "original_basename": "2025-wildcard.the-austrian-citrix-guy.at.PFX", "size": 3593, "src": "/etc/ansible/assets/2025-wildcard.the-austrian-citrix-guy.at.PFX"}

null_resource.RunInitialPlaybook (local-exec): TASK [Install Required Server Roles for Citrix] ********************************

null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-v-ddc1.the-austrian-citrix-guy.at] =&gt; {"changed": true, "exitcode": "Success", "feature_result": [{"display_name": "Group Policy Management", "id": 69, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ASP.NET 4.8", "id": 429, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Remote Server Administration Tools", "id": 67, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "AD DS and AD LDS Tools", "id": 329, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "AD DS Tools", "id": 257, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "AD DS Snap-Ins and Command-Line Tools", "id": 299, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Role Administration Tools", "id": 256, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Application Development", "id": 147, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ASP.NET 4.8", "id": 413, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Common HTTP Features", "id": 141, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Default Document", "id": 143, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Directory Browsing", "id": 144, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Request Filtering", "id": 169, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Health and Diagnostics", "id": 155, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "HTTP Errors", "id": 145, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "HTTP Logging", "id": 156, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ISAPI Extensions", "id": 152, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ISAPI Filters", "id": 153, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "IIS Management Console", "id": 175, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Management Tools", "id": 174, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": ".NET Extensibility 4.8", "id": 414, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Performance", "id": 171, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Security", "id": 162, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Web Server (IIS)", "id": 2, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Static Content Compression", "id": 172, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Static Content", "id": 142, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Web Server", "id": 140, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Windows Authentication", "id": 164, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}], "reboot_required": false, "success": true}
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-v-ddc2.the-austrian-citrix-guy.at] =&gt; {"changed": true, "exitcode": "Success", "feature_result": [{"display_name": "Group Policy Management", "id": 69, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ASP.NET 4.8", "id": 429, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Remote Server Administration Tools", "id": 67, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "AD DS and AD LDS Tools", "id": 329, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "AD DS Tools", "id": 257, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "AD DS Snap-Ins and Command-Line Tools", "id": 299, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Role Administration Tools", "id": 256, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Application Development", "id": 147, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ASP.NET 4.8", "id": 413, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Common HTTP Features", "id": 141, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Default Document", "id": 143, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Directory Browsing", "id": 144, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Request Filtering", "id": 169, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Health and Diagnostics", "id": 155, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "HTTP Errors", "id": 145, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "HTTP Logging", "id": 156, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ISAPI Extensions", "id": 152, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ISAPI Filters", "id": 153, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "IIS Management Console", "id": 175, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Management Tools", "id": 174, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": ".NET Extensibility 4.8", "id": 414, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Performance", "id": 171, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Security", "id": 162, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Web Server (IIS)", "id": 2, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Static Content Compression", "id": 172, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Static Content", "id": 142, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Web Server", "id": 140, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Windows Authentication", "id": 164, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}], "reboot_required": false, "success": true}

null_resource.RunInitialPlaybook (local-exec): TASK [Reboot when Required Roles need a restart] *******************************
null_resource.RunInitialPlaybook (local-exec): skipping: [tf-cvad-v-ddc1.the-austrian-citrix-guy.at] =&gt; {"changed": false, "false_condition": "requirements.reboot_required", "skip_reason": "Conditional result was False"}
null_resource.RunInitialPlaybook (local-exec): skipping: [tf-cvad-v-ddc2.the-austrian-citrix-guy.at] =&gt; {"changed": false, "false_condition": "requirements.reboot_required", "skip_reason": "Conditional result was False"}

null_resource.RunInitialPlaybook (local-exec): TASK [Install SSL-Certificate on Servers] **************************************
null_resource.RunInitialPlaybook: Still creating... [15m00s elapsed]
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-v-ddc1.the-austrian-citrix-guy.at] =&gt; {"changed": true, "thumbprints": ["0BB52BDDEB78E6E8E32FF3B1949CAA462B8EC386"]}
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-v-ddc2.the-austrian-citrix-guy.at] =&gt; {"changed": true, "thumbprints": ["0BB52BDDEB78E6E8E32FF3B1949CAA462B8EC386"]}

null_resource.RunInitialPlaybook (local-exec): TASK [Run installer from mounted ISO] ******************************************
null_resource.RunInitialPlaybook: Still creating... [15m10s elapsed]
null_resource.RunInitialPlaybook: Still creating... [15m20s elapsed]

...

null_resource.RunInitialPlaybook: Still creating... [25m10s elapsed]
null_resource.RunInitialPlaybook: Still creating... [25m20s elapsed]
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-v-ddc1.the-austrian-citrix-guy.at] =&gt; {"changed": true, "checksum": "EADE21C7B09DED5626F3BB51D4DF7A4C0E7AC607", "rc": 0, "reboot_required": false}
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-v-ddc2.the-austrian-citrix-guy.at] =&gt; {"changed": true, "checksum": "EADE21C7B09DED5626F3BB51D4DF7A4C0E7AC607", "rc": 0, "reboot_required": false}

null_resource.RunInitialPlaybook (local-exec): TASK [Reboot after DDC] ********************************************************
null_resource.RunInitialPlaybook: Still creating... [25m30s elapsed]
null_resource.RunInitialPlaybook: Still creating... [25m40s elapsed]
null_resource.RunInitialPlaybook: Still creating... [25m50s elapsed]
null_resource.RunInitialPlaybook: Still creating... [26m00s elapsed]
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-v-ddc1.the-austrian-citrix-guy.at] =&gt; {"changed": true, "elapsed": 34, "rebooted": true, "unreachable": false}
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-v-ddc2.the-austrian-citrix-guy.at] =&gt; {"changed": true, "elapsed": 43, "rebooted": true, "unreachable": false}

null_resource.RunInitialPlaybook (local-exec): TASK [Remove DDC RunOnce Key] **************************************************
null_resource.RunInitialPlaybook (local-exec): ok: [tf-cvad-v-ddc2.the-austrian-citrix-guy.at] =&gt; {"changed": false, "data_changed": false, "data_type_changed": false}
null_resource.RunInitialPlaybook (local-exec): ok: [tf-cvad-v-ddc1.the-austrian-citrix-guy.at] =&gt; {"changed": false, "data_changed": false, "data_type_changed": false}

null_resource.RunInitialPlaybook (local-exec): TASK [Resume DDC Install] ******************************************************
null_resource.RunInitialPlaybook (local-exec): skipping: [tf-cvad-v-ddc1.the-austrian-citrix-guy.at] =&gt; {"changed": false, "false_condition": "ddc_resume.changed", "skip_reason": "Conditional result was False"}
null_resource.RunInitialPlaybook (local-exec): skipping: [tf-cvad-v-ddc2.the-austrian-citrix-guy.at] =&gt; {"changed": false, "false_condition": "ddc_resume.changed", "skip_reason": "Conditional result was False"}

null_resource.RunInitialPlaybook (local-exec): TASK [Alter unwanted Registry Keys-UserIEESC] **********************************
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-v-ddc1.the-austrian-citrix-guy.at] =&gt; {"changed": true, "data_changed": false, "data_type_changed": true}
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-v-ddc2.the-austrian-citrix-guy.at] =&gt; {"changed": true, "data_changed": false, "data_type_changed": true}

null_resource.RunInitialPlaybook (local-exec): TASK [Alter unwanted Registry Keys-AdminIEESC] *********************************
null_resource.RunInitialPlaybook: Still creating... [26m10s elapsed]
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-v-ddc1.the-austrian-citrix-guy.at] =&gt; {"changed": true, "data_changed": false, "data_type_changed": true}
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-v-ddc2.the-austrian-citrix-guy.at] =&gt; {"changed": true, "data_changed": false, "data_type_changed": true}

null_resource.RunInitialPlaybook (local-exec): PLAY RECAP *********************************************************************
null_resource.RunInitialPlaybook (local-exec): tf-cvad-v-ddc1.the-austrian-citrix-guy.at : ok=12   changed=9    unreachable=0    failed=0    skipped=2    rescued=0    ignored=0
null_resource.RunInitialPlaybook (local-exec): tf-cvad-v-ddc2.the-austrian-citrix-guy.at : ok=12   changed=9    unreachable=0    failed=0    skipped=2    rescued=0    ignored=0

null_resource.RunInitialPlaybook: Creation complete after 26m11s [id=3927491137125445482]
data.external.CheckAnsibleReturnCode: Reading...
data.external.CheckAnsibleReturnCode: Read complete after 0s [id=-]
null_resource.IfExitCodeIsNotZeroThenHalt: Creating...
null_resource.IfExitCodeIsNotZeroThenHalt: Provisioning with 'local-exec'...
null_resource.IfExitCodeIsNotZeroThenHalt (local-exec): Executing: ["/bin/bash" "-c" "[ \"1\" = \"1\" ] &amp;&amp; echo \"Deploying CVAD: Everything OK\" || { echo \"Deploying the CVAD software and its prerequisites failed. Please review the settings. Halting Terraform.\"; exit 1; }"]
null_resource.IfExitCodeIsNotZeroThenHalt (local-exec): Deploying CVAD: Everything OK
null_resource.IfExitCodeIsNotZeroThenHalt: Creation complete after 0s [id=8978265277076340502]

Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
azadmin@tacg-cicd:/etc/terraform-tmm/CVADVS/_InstallCVAD$ After successful deployment, the CVAD software is installed and can be configured later. Caution: We have noticed that in some environments, the Citrix Host Service does not start after the initial reboot, even though it is set to Automatic start. Therefore, we added a new task to the Ansible Playbook to ensure that the Citrix Host Service starts after the initial reboot and before the following configuration steps.  Since the CVAD software and its dependencies were successfully deployed, we can proceed to the next step.  Part 5: Using Terraform and Ansible to deploy the StoreFront serverThe VM dedicated to holding the StoreFront™ role is now ready for the deployment of the StoreFront components. This is a multi-step approach: Mount the CVAD ISO installer Copy the publicly signed SSL certificate to the DDCs Install all required server roles and features before installing the DDCs Change some necessary registry settings Therefore, we again use the well-proven combination of Terraform and Ansible. Initially, no Citrix software is installed on the VMs. Terraform monitors each Ansible Playbook run and stops further steps if a Playbook fails. Example Terraform snippet: # Terraform Deployment of Citrix CVAD on vSphere 8 - 25.11 
## Definition of vSphere Provider Variables - not all available variables are defined, only these we use
### Reference https://registry.terraform.io/providers/vmware/vsphere/latest/docs
#### Copy the required Ansible Assets for installing StoreFront to the Ansible Server
###### ***** IMPORTANT: You must set the permissions to the destination directory accordingly (sudo) chmod 777 otherwise the provisioning will fail *****
resource "null_resource" "CopyAnsibleSFAssetsToAnsibleServer" {
  connection {
    type     = var.TACG-TMM-VSP-CL-Ansible-Connection-Type
    user     = var.TACG-TMM-VSP-CL-Ansible-Connection-User
    password = var.TACG-TMM-VSP-CL-Ansible-Connection-Password
    host     = var.TACG-TMM-VSP-CL-Ansible-Connection-HostIP
    port     = var.TACG-TMM-VSP-CL-Ansible-Connection-Port
    https    = var.TACG-TMM-VSP-CL-Ansible-Connection-HTTPS
    timeout  = var.TACG-TMM-VSP-CL-Ansible-Connection-Timeout
  }

  # Copy vars-sf.yml file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-VSP-CL-Ansible-Vars-Source
    destination = var.TACG-TMM-VSP-CL-Ansible-Vars-Destination
  }

  # Copy inventory.ini file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-VSP-CL-Ansible-Inventory-Source
    destination = var.TACG-TMM-VSP-CL-Ansible-Inventory-Destination
  }

  # Copy InitialConfig Playbook file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-VSP-CL-Ansible-InitialPlaybookForSF-Source
    destination = var.TACG-TMM-VSP-CL-Ansible-InitialPlaybookForSF-Destination
  }
}

#### Wait 10s for Backgroud Processes to Settle
resource "time_sleep" "wait_10_seconds" {
  depends_on      = [null_resource.CopyAnsibleSFAssetsToAnsibleServer]
  create_duration = "10s"
}

##### Connect to Ansible Interpreter and run Initial Configuration Playbook on SF
resource "null_resource" "RunInitialPlaybook" {
  depends_on = [time_sleep.wait_10_seconds]

  provisioner "local-exec" {
    working_dir = "/etc/ansible"
    command     = var.TACG-TMM-VSP-CL-Ansible-InitialCMDForSF
  }
}

##### Check Ansible Return Code
data "external" "CheckAnsibleReturnCode" {
  depends_on = [null_resource.RunInitialPlaybook]
  program    = ["bash", "/etc/ansible/assets/CheckAnsibleReturnCode.sh", "/etc/ansible/assets/ansible_exit_code.txt", "0"]
}

##### Terminate if Return Code is not 0
resource "null_resource" "IfExitCodeIsNotZeroThenHalt" {
  depends_on = [data.external.CheckAnsibleReturnCode]
  provisioner "local-exec" {
    interpreter = ["/bin/bash", "-c"]
    command     = "[ \"${data.external.CheckAnsibleReturnCode.result.found}\" = \"1\" ] &amp;&amp; echo \"Deploying StoreFront - Everything OK\" || { echo \"Deploying the StoreFront software and/or its prerequisites failed. Please review the settings. Halting Terraform.\"; exit 1; }"
  }
}Example Ansible Playbook for deploying and configuring Storefront and all needed further configurations: # Terraform Deployment of Citrix CVAD on vSphere 8 - 25.11 
## For installation and configuration of the Delivery Controllers, StoreFront-Servers and WebStudio-Servers, we use Ansible Playbooks 
### Reference https://docs.ansible.com/ansible/latest/getting_started/index.html
#### We assume that the communication between the Ansible Server and all needed Hosts via WinRM is working as intended - check your Group Policies
##### All hosts and its corresponding communication-related Ansible variables are stored in assets/inventory.ini

###### Prepare the StoreFront Server
- name: Configure the StoreFront Server initially
  hosts: cvad-sf-v
  vars_files:
    - ./vars-sf.yml
  tasks:

  - name: Create Installer directory  
    ansible.windows.win_file: 
      path: "{{ installdir }}"
      state: directory

  - name: Create Log Folder
    ansible.windows.win_file:
      path: "{{ logdir }}"
      state: directory

  - name: Alter unwanted Registry Keys-UserIEESC
    ansible.windows.win_regedit:
      path: HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A8-37EF-4b3f-8CFC-4F3A74704073}
      name: IsInstalled
      data: 0

  - name: Alter unwanted Registry Keys-AdminIEESC
    ansible.windows.win_regedit:
      path: HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A7-37EF-4b3f-8CFC-4F3A74704073}
      name: IsInstalled
      data: 0

  - name: Copy Citrix iso to the host
    ansible.windows.win_copy:
      src: "{{ sourceiso }}"
      dest: "{{ destiso }}" 
      force: no

  - name: Copy the SSL certificate to the hosts
    ansible.windows.win_copy:
      src: "{{ certificate_source_path }}"
      dest: "{{ certificate_destination_path }}" 
      force: no

  - name: Install Required Server Roles for Citrix
    ansible.windows.win_feature:
      name: "{{ sf_req_roles }}"
      state: present
    register: requirements
    become: true
    become_method: runas
    become_user: SYSTEM

  - name: Reboot when Required Roles need a restart
    ansible.windows.win_reboot:
      post_reboot_delay: 120
    when: requirements.reboot_required

  - name: Install SSL-Certificate on Servers
    ansible.windows.win_certificate_store:
      path: "{{ certificate_destination_path }}" 
      password: "{{ certificate_pfx_password }}"
      key_exportable: yes
      file_type: pkcs12
      store_location: LocalMachine
      key_storage: machine
      state: present
    become: true
    become_method: runas
    become_user: SYSTEM

  - name: Create database server record
    ansible.windows.win_dns_record:
      computer_name: "{{ sf_dns_ip }}"
      name: "{{ sf_dns_name }}"
      type: "A"
      value: "{{ sf_dns_value }}"
      zone: "{{ sf_dns_zone }}"
      state: present
    become: true
    become_method: runas
    become_user: SYSTEM  

  - name: Run installer from mounted ISO
    ansible.windows.win_package:
      path: "{{ install_exe }} "
      arguments: "{{ install_params }}"
      state: present
      expected_return_code: [0, 3, 4, 3010]
      creates_path: C:\ProgramData\Citrix\Storefront Install
    register: sf_install
    become: true
    become_method: runas
    become_user: "{{ ansible_domainuser }}"

  - name: Reboot after sf
    ansible.windows.win_reboot:
    when: sf_install.changedRunning the snippet should fulfill all required steps: At first, all missing Windows features are installed. azadmin@tacg-cicd:/etc/terraform-tmm/CVADVS/_InstallStoreFront$ terraform apply

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create
 &lt;= read (data resources)

Terraform will perform the following actions:

  # data.external.CheckAnsibleReturnCode will be read during apply
  # (depends on a resource or a module with changes pending)
 &lt;= data "external" "CheckAnsibleReturnCode" {
      + id      = (known after apply)
      + program = [
          + "bash",
          + "/etc/ansible/assets/CheckAnsibleReturnCode.sh",
          + "/etc/ansible/assets/ansible_exit_code.txt",
          + "0",
        ]
      + result  = (known after apply)
    }

  # null_resource.CopyAnsibleSFAssetsToAnsibleServer will be created
  + resource "null_resource" "CopyAnsibleSFAssetsToAnsibleServer" {
      + id = (known after apply)
    }

  # null_resource.IfExitCodeIsNotZeroThenHalt will be created
  + resource "null_resource" "IfExitCodeIsNotZeroThenHalt" {
      + id = (known after apply)
    }

  # null_resource.RunInitialPlaybook will be created
  + resource "null_resource" "RunInitialPlaybook" {
      + id = (known after apply)
    }

  # time_sleep.wait_10_seconds will be created
  + resource "time_sleep" "wait_10_seconds" {
      + create_duration = "10s"
      + id              = (known after apply)
    }

Plan: 4 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.CopyAnsibleSFAssetsToAnsibleServer: Creating...
null_resource.CopyAnsibleSFAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyAnsibleSFAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyAnsibleSFAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyAnsibleSFAssetsToAnsibleServer: Creation complete after 0s [id=3119681656948818723]
time_sleep.wait_10_seconds: Creating...
time_sleep.wait_10_seconds: Still creating... [00m10s elapsed]
time_sleep.wait_10_seconds: Creation complete after 10s [id=2025-11-15T16:59:29Z]
null_resource.RunInitialPlaybook: Creating...
null_resource.RunInitialPlaybook: Provisioning with 'local-exec'...
null_resource.RunInitialPlaybook (local-exec): Executing: ["/bin/sh" "-c" "ansible-playbook /etc/ansible/assets/InitialConfigurationForStoreFront.ansible.yml -i /etc/ansible/assets/inventory.ini -v -c winrm; echo $? &gt; /etc/ansible/assets/ansible_exit_code.txt"]
null_resource.RunInitialPlaybook (local-exec): Using /etc/ansible/ansible.cfg as config file

null_resource.RunInitialPlaybook (local-exec): PLAY [Configure the StoreFront Server initially] *******************************

null_resource.RunInitialPlaybook (local-exec): TASK [Gathering Facts] *********************************************************
null_resource.RunInitialPlaybook (local-exec): ok: [tf-cvad-v-sf.the-austrian-citrix-guy.at]

null_resource.RunInitialPlaybook (local-exec): TASK [Create Installer directory] **********************************************
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-v-sf.the-austrian-citrix-guy.at] =&gt; {"changed": true}

null_resource.RunInitialPlaybook (local-exec): TASK [Create Log Folder] *******************************************************
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-v-sf.the-austrian-citrix-guy.at] =&gt; {"changed": true}

null_resource.RunInitialPlaybook (local-exec): TASK [Alter unwanted Registry Keys-UserIEESC] **********************************
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-v-sf.the-austrian-citrix-guy.at] =&gt; {"changed": true, "data_changed": false, "data_type_changed": true}

null_resource.RunInitialPlaybook (local-exec): TASK [Alter unwanted Registry Keys-AdminIEESC] *********************************
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-v-sf.the-austrian-citrix-guy.at] =&gt; {"changed": true, "data_changed": false, "data_type_changed": true}

null_resource.RunInitialPlaybook (local-exec): TASK [Copy Citrix iso to the host] *********************************************
null_resource.RunInitialPlaybook: Still creating... [00m10s elapsed]
null_resource.RunInitialPlaybook: Still creating... [00m20s elapsed]
...
null_resource.RunInitialPlaybook: Still creating... [09m50s elapsed]
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-v-sf.the-austrian-citrix-guy.at] =&gt; {"changed": true, "checksum": null, "dest": "C:\\InstallCVAD\\Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso", "operation": "file_copy", "original_basename": "Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso", "size": 4124442624, "src": "/etc/ansible/isos/Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso"}

null_resource.RunInitialPlaybook (local-exec): TASK [Copy the SSL certificate to the hosts] ***********************************
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-v-sf.the-austrian-citrix-guy.at] =&gt; {"changed": true, "checksum": null, "dest": "C:\\InstallCVAD\\2025-wildcard.the-austrian-citrix-guy.at.PFX", "operation": "file_copy", "original_basename": "2025-wildcard.the-austrian-citrix-guy.at.PFX", "size": 3593, "src": "/etc/ansible/assets/2025-wildcard.the-austrian-citrix-guy.at.PFX"}

null_resource.RunInitialPlaybook (local-exec): TASK [Install Required Server Roles for Citrix] ********************************
null_resource.RunInitialPlaybook: Still creating... [10m00s elapsed]
null_resource.RunInitialPlaybook: Still creating... [10m10s elapsed]
...
null_resource.RunInitialPlaybook: Still creating... [19m00s elapsed]
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-v-sf.the-austrian-citrix-guy.at] =&gt; {"changed": true, "exitcode": "Success", "feature_result": [{"display_name": "Message Queuing", "id": 49, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Message Queuing Server", "id": 191, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Message Queuing Services", "id": 190, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ASP.NET 4.8", "id": 429, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": ".NET Framework 3.5 (includes .NET 2.0 and 3.0)", "id": 220, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": ".NET Framework 3.5 Features", "id": 475, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "HTTP Activation", "id": 421, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Message Queuing (MSMQ) Activation", "id": 422, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Named Pipe Activation", "id": 423, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "TCP Activation", "id": 424, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Telnet Client", "id": 44, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Windows Process Activation Service", "id": 41, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Configuration APIs", "id": 217, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Process Model", "id": 219, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Application Development", "id": 147, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Application Initialization", "id": 445, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ASP", "id": 150, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ASP.NET 4.8", "id": 413, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Basic Authentication", "id": 163, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "CGI", "id": 151, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Common HTTP Features", "id": 141, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Default Document", "id": 143, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Directory Browsing", "id": 144, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Dynamic Content Compression", "id": 173, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Request Filtering", "id": 169, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Health and Diagnostics", "id": 155, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "HTTP Errors", "id": 145, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "HTTP Logging", "id": 156, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "HTTP Redirection", "id": 146, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Tracing", "id": 159, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Server Side Includes", "id": 154, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ISAPI Extensions", "id": 152, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ISAPI Filters", "id": 153, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Logging Tools", "id": 157, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "IIS 6 Metabase Compatibility", "id": 179, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "IIS 6 Management Compatibility", "id": 178, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "IIS Management Console", "id": 175, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Management Tools", "id": 174, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": ".NET Extensibility 4.8", "id": 414, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Performance", "id": 171, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "IIS Management Scripts and Tools", "id": 176, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Security", "id": 162, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Web Server (IIS)", "id": 2, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Static Content Compression", "id": 172, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Static Content", "id": 142, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Web Server", "id": 140, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Windows Authentication", "id": 164, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "IIS 6 WMI Compatibility", "id": 180, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}], "reboot_required": false, "success": true}

null_resource.RunInitialPlaybook (local-exec): TASK [Reboot when Required Roles need a restart] *******************************
null_resource.RunInitialPlaybook (local-exec): skipping: [tf-cvad-v-sf.the-austrian-citrix-guy.at] =&gt; {"changed": false, "false_condition": "requirements.reboot_required", "skip_reason": "Conditional result was False"}

null_resource.RunInitialPlaybook (local-exec): TASK [Install SSL-Certificate on Servers] **************************************
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-v-sf.the-austrian-citrix-guy.at] =&gt; {"changed": true, "thumbprints": ["0BB52BDDEB78E6E8E32FF3B1949CAA462B8EC386"]}

null_resource.RunInitialPlaybook (local-exec): TASK [Run installer from mounted ISO] ******************************************
null_resource.RunInitialPlaybook: Still creating... [19m10s elapsed]
null_resource.RunInitialPlaybook: Still creating... [19m20s elapsed]
null_resource.RunInitialPlaybook: Still creating... [19m30s elapsed]
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-v-sf.the-austrian-citrix-guy.at] =&gt; {"changed": true, "checksum": "22A6C5D5BB516ABD6DF5DC6E6CA0C0CEFBB19082", "rc": 3010, "reboot_required": true}

null_resource.RunInitialPlaybook (local-exec): TASK [Reboot after sf] *********************************************************
null_resource.RunInitialPlaybook: Still creating... [19m40s elapsed]
null_resource.RunInitialPlaybook: Still creating... [19m50s elapsed]
null_resource.RunInitialPlaybook: Still creating... [20m00s elapsed]
null_resource.RunInitialPlaybook: Still creating... [20m10s elapsed]
null_resource.RunInitialPlaybook: Still creating... [20m20s elapsed]
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-v-sf.the-austrian-citrix-guy.at] =&gt; {"changed": true, "elapsed": 42, "rebooted": true, "unreachable": false}

null_resource.RunInitialPlaybook (local-exec): PLAY RECAP *********************************************************************
null_resource.RunInitialPlaybook (local-exec): tf-cvad-v-sf.the-austrian-citrix-guy.at : ok=11   changed=10   unreachable=0    failed=0    skipped=1    rescued=0    ignored=0

null_resource.RunInitialPlaybook: Creation complete after 20m24s [id=7847329303251523226]
data.external.CheckAnsibleReturnCode: Reading...
data.external.CheckAnsibleReturnCode: Read complete after 0s [id=-]
null_resource.IfExitCodeIsNotZeroThenHalt: Creating...
null_resource.IfExitCodeIsNotZeroThenHalt: Provisioning with 'local-exec'...
null_resource.IfExitCodeIsNotZeroThenHalt (local-exec): Executing: ["/bin/bash" "-c" "[ \"1\" = \"1\" ] &amp;&amp; echo \"Deploying StoreFront - Everything OK\" || { echo \"Deploying the StoreFront software and/or its prerequisites failed. Please review the settings. Halting Terraform.\"; exit 1; }"]
null_resource.IfExitCodeIsNotZeroThenHalt (local-exec): Deploying StoreFront - Everything OK
null_resource.IfExitCodeIsNotZeroThenHalt: Creation complete after 0s [id=6843415180162545622]

Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
azadmin@tacg-cicd:/etc/terraform-tmm/CVADVS/_InstallStoreFront$ After successful deployment, the StoreFront software is installed and can be configured later.  Since the StoreFront software and its dependencies were successfully deployed, we can proceed to the next step.  Part 6: Using Terraform and Ansible to deploy the Web Studio ServerThe VM dedicated to holding the Web Studio role is now ready for the deployment of the Web Studio components. This is a multi-step approach: Mount the CVAD ISO installer Copy the publicly signed SSL certificate to the Web Studio server Install all required server roles and features before installing the Web Studio server Change some necessary registry settings Configure the Web Studio server as a proxy for the DDCs Therefore, we again use the well-proven combination of Terraform and Ansible. Initially, no Citrix software is installed on the VM again. Terraform monitors each Ansible Playbook run and stops further steps if a Playbook fails. Example Terraform snippet: # Terraform Deployment of Citrix CVAD on vSphere 8 - 25.11 
## Definition of vSphere Provider Variables - not all available variables are defined, only these we use
### Reference https://registry.terraform.io/providers/vmware/vsphere/latest/docs
#### Copy the required Ansible Assets for installing Web Studio to the Ansible Server
###### ***** IMPORTANT: You must set the permissions to the destination directory accordingly (sudo) chmod 777 otherwise the provisioning will fail *****
resource "null_resource" "CopyAnsibleWSAssetsToAnsibleServer" {
  connection {
    type     = var.TACG-TMM-VSP-CL-Ansible-Connection-Type
    user     = var.TACG-TMM-VSP-CL-Ansible-Connection-User
    password = var.TACG-TMM-VSP-CL-Ansible-Connection-Password
    host     = var.TACG-TMM-VSP-CL-Ansible-Connection-HostIP
    port     = var.TACG-TMM-VSP-CL-Ansible-Connection-Port
    https    = var.TACG-TMM-VSP-CL-Ansible-Connection-HTTPS
    timeout  = var.TACG-TMM-VSP-CL-Ansible-Connection-Timeout
  }

  # Copy vars-ws.yml file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-VSP-CL-Ansible-Vars-Source
    destination = var.TACG-TMM-VSP-CL-Ansible-Vars-Destination
  }

  # Copy inventory.ini file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-VSP-CL-Ansible-Inventory-Source
    destination = var.TACG-TMM-VSP-CL-Ansible-Inventory-Destination
  }

  # Copy InitialConfig Playbook file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-VSP-CL-Ansible-InitialPlaybookForWS-Source
    destination = var.TACG-TMM-VSP-CL-Ansible-InitialPlaybookForWS-Destination
  }

  # Copy SetProxy Playbook file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-VSP-CL-Ansible-SetProxyPlaybookWS-Source
    destination = var.TACG-TMM-VSP-CL-Ansible-InitialPlaybookForWS-Destination
  }
}

#### Wait 10s for Backgroud Processes to Settle
resource "time_sleep" "wait_10_seconds" {
  depends_on      = [null_resource.CopyAnsibleWSAssetsToAnsibleServer]
  create_duration = "10s"
}

#### Run initial configuration
##### Connect to Ansible Interpreter and run Initial Configuration Playbook on WS
resource "null_resource" "RunInitialPlaybook" {
  depends_on = [time_sleep.wait_10_seconds]

  provisioner "local-exec" {
    working_dir = "/etc/ansible"
    command     = var.TACG-TMM-VSP-CL-Ansible-InitialCMDForWS
  }
}

##### Check Ansible Return Code
data "external" "CheckAnsibleReturnCode" {
  depends_on = [null_resource.RunInitialPlaybook]
  program    = ["bash", "/etc/ansible/assets/CheckAnsibleReturnCode.sh", "/etc/ansible/assets/ansible_exit_code.txt", "0"]
}

##### Terminate if Return Code is not 0
resource "null_resource" "IfExitCodeIsNotZeroThenHalt" {
  depends_on = [data.external.CheckAnsibleReturnCode]
  provisioner "local-exec" {
    interpreter = ["/bin/bash", "-c"]
    command     = "[ \"${data.external.CheckAnsibleReturnCode.result.found}\" = \"1\" ] &amp;&amp; echo \"Deploying WebStudio: Everything OK\" || { echo \"Deploying the WebStudio software and/or its prerequisites failed. Please review the settings. Halting Terraform.\"; exit 1; }"
  }
}

#### Wait 10s for Backgroud Processes to Settle
resource "time_sleep" "wait_10_seconds_IC" {
  depends_on      = [null_resource.IfExitCodeIsNotZeroThenHalt]
  create_duration = "10s"
}
##### End of intitial configuration

#### Run SetProxy configuration
##### Connect to Ansible Interpreter and run SetProxy Playbook on WS
resource "null_resource" "RunSetProxyConfigurationPlaybook" {
  depends_on = [time_sleep.wait_10_seconds_IC]

  provisioner "local-exec" {
    working_dir = "/etc/ansible"
    command     = var.TACG-TMM-VSP-CL-Ansible-SetProxyPlaybookWS
  }
}

##### Check Ansible Return Code
data "external" "CheckAnsibleReturnCodePC" {
  depends_on = [null_resource.RunSetProxyConfigurationPlaybook]
  program    = ["bash", "/etc/ansible/assets/CheckAnsibleReturnCode.sh", "/etc/ansible/assets/ansible_exit_code.txt", "0"]
}

##### Terminate if Return Code is not 0
resource "null_resource" "IfExitCodeIsNotZeroThenHaltPC" {
  depends_on = [data.external.CheckAnsibleReturnCodePC]
  provisioner "local-exec" {
    interpreter = ["/bin/bash", "-c"]
    command     = "[ \"${data.external.CheckAnsibleReturnCodePC.result.found}\" = \"1\" ] &amp;&amp; echo \"Configuring WebStudio as Proxy: Everything OK\" || { echo \"Configuring WebStudio as Proxy failed. Please review the settings. Halting Terraform.\"; exit 1; }"
  }
}

#### Wait 10s for Backgroud Processes to Settle
resource "time_sleep" "wait_10_seconds_PC" {
  depends_on      = [null_resource.IfExitCodeIsNotZeroThenHaltPC]
  create_duration = "10s"
}
##### End of initial configurationExample Ansible Playbook for deploying and configuring Web Studio and all needed further configurations: # Terraform Deployment of Citrix CVAD on vSphere 8 - 25.11 
## For installation and configuration of the Delivery Controllers, StoreFront-Servers and WebStudio-Servers, we use Ansible Playbooks 
### Reference https://docs.ansible.com/ansible/latest/getting_started/index.html
#### We assume that the communication between the Ansible Server and all needed Hosts via WinRM is working as intended - check your Group Policies
##### All hosts and its corresponding communication-related Ansible variables are stored in assets/inventory.ini

###### Prepare the Web Studio Server
- name: Configure the WebStudio Server initially
  hosts: cvad-ws-v
  vars_files:
    - ./vars-ws.yml
  tasks:

  - name: Create Installer directory  
    ansible.windows.win_file: 
      path: "{{ installdir }}"
      state: directory

  - name: Create Log Folder
    ansible.windows.win_file:
      path: "{{ logdir }}"
      state: directory

  - name: Alter unwanted Registry Keys-UserIEESC
    ansible.windows.win_regedit:
      path: HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A8-37EF-4b3f-8CFC-4F3A74704073}
      name: IsInstalled
      data: 0

  - name: Alter unwanted Registry Keys-AdminIEESC
    ansible.windows.win_regedit:
      path: HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A7-37EF-4b3f-8CFC-4F3A74704073}
      name: IsInstalled
      data: 0

  - name: Copy Citrix iso to the host
    ansible.windows.win_copy:
      src: "{{ sourceiso }}"
      dest: "{{ destiso }}" 
      force: no

  - name: Copy the SSL certificate to the hosts
    ansible.windows.win_copy:
      src: "{{ certificate_source_path }}"
      dest: "{{ certificate_destination_path }}" 
      force: no

  - name: Install Required Server Roles for Citrix
    ansible.windows.win_feature:
      name: "{{ ws_req_roles }}"
      state: present
    register: requirements
    become: true
    become_method: runas
    become_user: SYSTEM

  - name: Reboot when Required Roles need a restart
    ansible.windows.win_reboot:
      post_reboot_delay: 120
    when: requirements.reboot_required

  - name: Install SSL-Certificate on Servers
    ansible.windows.win_certificate_store:
      path: "{{ certificate_destination_path }}" 
      password: "{{ certificate_pfx_password }}"
      key_exportable: yes
      file_type: pkcs12
      store_location: LocalMachine
      key_storage: machine
      state: present
    become: true
    become_method: runas
    become_user: SYSTEM

  - name: Run installer from mounted ISO
    ansible.windows.win_package:
      path: "{{ install_exe }} "
      arguments: "{{ install_params }}"
      state: present
      expected_return_code: [0, 3, 4, 3010]
      creates_path: C:\ProgramData\Citrix\WebStudio Install
    register: ws_install
    become: true
    become_method: runas
    become_user: "{{ ansible_domainuser }}"

  - name: Reboot after ws
    ansible.windows.win_reboot:
    when: ws_install.changedExample Ansible Playbook for configuring Web Studio as Proxy: # Terraform Deployment of Citrix CVAD on vSphere 8 - 25.11 
## For installation and configuration of the Delivery Controllers, StoreFront-Servers and WebStudio-Servers, we use Ansible Playbooks 
### Reference https://docs.ansible.com/ansible/latest/getting_started/index.html
#### We assume that the communication between the Ansible Server and all needed Hosts via WinRM is working as intended - check your Group Policies
##### All hosts and its corresponding communication-related Ansible variables are stored in assets/inventory.ini

###### Configure WS as Proxy
- name: Configure WebStudio as Proxy
  hosts: cvad-ws
  gather_facts: no

  tasks:
    - name: Run PS to enable Proxy Mode
      ansible.windows.win_powershell:
        script: |
          &amp; "c:\Program Files\Citrix\Web Studio\Tool\StudioConfig.exe" --enableproxy --proxyserver "tf-cvad-v-ws.the-austrian-citrix-guy.at"
      register: runps

    - name: Print results
      ansible.builtin.debug:
        var: runpsRunning the snippet should fulfill all needed steps: azadmin@tacg-cicd:/etc/terraform-tmm/CVADVS/_InstallWebStudio$ terraform apply

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
  + create
 &lt;= read (data resources)

Terraform will perform the following actions:

  # data.external.CheckAnsibleReturnCode will be read during apply
  # (depends on a resource or a module with changes pending)
 &lt;= data "external" "CheckAnsibleReturnCode" {
      + id      = (known after apply)
      + program = [
          + "bash",
          + "/etc/ansible/assets/CheckAnsibleReturnCode.sh",
          + "/etc/ansible/assets/ansible_exit_code.txt",
          + "0",
        ]
      + result  = (known after apply)
    }

  # null_resource.CopyAnsibleWSAssetsToAnsibleServer will be created
  + resource "null_resource" "CopyAnsibleWSAssetsToAnsibleServer" {
      + id = (known after apply)
    }

  # null_resource.IfExitCodeIsNotZeroThenHalt will be created
  + resource "null_resource" "IfExitCodeIsNotZeroThenHalt" {
      + id = (known after apply)
    }

  # null_resource.RunInitialPlaybook will be created
  + resource "null_resource" "RunInitialPlaybook" {
      + id = (known after apply)
    }

  # time_sleep.wait_10_seconds will be created
  + resource "time_sleep" "wait_10_seconds" {
      + create_duration = "10s"
      + id              = (known after apply)
    }

Plan: 4 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.CopyAnsibleWSAssetsToAnsibleServer: Creating...
null_resource.CopyAnsibleWSAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyAnsibleWSAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyAnsibleWSAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyAnsibleWSAssetsToAnsibleServer: Creation complete after 1s [id=8218302926752497323]
time_sleep.wait_10_seconds: Creating...
time_sleep.wait_10_seconds: Still creating... [00m10s elapsed]
time_sleep.wait_10_seconds: Creation complete after 10s [id=2025-11-18T07:52:23Z]
null_resource.RunInitialPlaybook: Creating...
null_resource.RunInitialPlaybook: Provisioning with 'local-exec'...
null_resource.RunInitialPlaybook (local-exec): Executing: ["/bin/sh" "-c" "ansible-playbook /etc/ansible/assets/InitialConfigurationForWS.ansible.yml -i /etc/ansible/assets/inventory.ini -v -c winrm; echo $? &gt; /etc/ansible/assets/ansible_exit_code.txt"]

null_resource.RunInitialPlaybook (local-exec): Using /etc/ansible/ansible.cfg as config file

null_resource.RunInitialPlaybook (local-exec): PLAY [Configure the WebStudio Server initially] ********************************

null_resource.RunInitialPlaybook (local-exec): TASK [Gathering Facts] *********************************************************
null_resource.RunInitialPlaybook (local-exec): ok: [tf-cvad-v-ws.the-austrian-citrix-guy.at]

null_resource.RunInitialPlaybook (local-exec): TASK [Create Installer directory] **********************************************
null_resource.RunInitialPlaybook (local-exec): ok: [tf-cvad-v-ws.the-austrian-citrix-guy.at] =&gt; {"changed": false}

null_resource.RunInitialPlaybook (local-exec): TASK [Create Log Folder] *******************************************************
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-v-ws.the-austrian-citrix-guy.at] =&gt; {"changed": true}

null_resource.RunInitialPlaybook (local-exec): TASK [Alter unwanted Registry Keys-UserIEESC] **********************************
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-v-ws.the-austrian-citrix-guy.at] =&gt; {"changed": true, "data_changed": false, "data_type_changed": true}

null_resource.RunInitialPlaybook (local-exec): TASK [Alter unwanted Registry Keys-AdminIEESC] *********************************
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-v-ws.the-austrian-citrix-guy.at] =&gt; {"changed": true, "data_changed": false, "data_type_changed": true}

null_resource.RunInitialPlaybook (local-exec): TASK [Copy Citrix iso to the host] *********************************************
null_resource.RunInitialPlaybook: Still creating... [00m10s elapsed]

...

null_resource.RunInitialPlaybook: Still creating... [09m20s elapsed]
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-v-ws.the-austrian-citrix-guy.at] =&gt; {"changed": true, "checksum": null, "dest": "C:\\InstallCVAD\\Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso", "operation": "file_copy", "original_basename": "Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso", "size": 4124442624, "src": "/etc/ansible/isos/Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso"}

null_resource.RunInitialPlaybook (local-exec): TASK [Copy the SSL certificate to the hosts] ***********************************
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-v-ws.the-austrian-citrix-guy.at] =&gt; {"changed": true, "checksum": null, "dest": "C:\\InstallCVAD\\2025-wildcard.the-austrian-citrix-guy.at.PFX", "operation": "file_copy", "original_basename": "2025-wildcard.the-austrian-citrix-guy.at.PFX", "size": 3593, "src": "/etc/ansible/assets/2025-wildcard.the-austrian-citrix-guy.at.PFX"}

null_resource.RunInitialPlaybook (local-exec): TASK [Install Required Server Roles for Citrix] ********************************
null_resource.RunInitialPlaybook: Still creating... [09m30s elapsed]

...

null_resource.RunInitialPlaybook: Still creating... [18m40s elapsed]
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-v-ws.the-austrian-citrix-guy.at] =&gt; {"changed": true, "exitcode": "Success", "feature_result": [{"display_name": "Message Queuing", "id": 49, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Message Queuing Server", "id": 191, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Message Queuing Services", "id": 190, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ASP.NET 4.8", "id": 429, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": ".NET Framework 3.5 (includes .NET 2.0 and 3.0)", "id": 220, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": ".NET Framework 3.5 Features", "id": 475, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "HTTP Activation", "id": 421, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Message Queuing (MSMQ) Activation", "id": 422, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Named Pipe Activation", "id": 423, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "TCP Activation", "id": 424, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Telnet Client", "id": 44, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Windows Process Activation Service", "id": 41, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Configuration APIs", "id": 217, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Process Model", "id": 219, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Application Development", "id": 147, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Application Initialization", "id": 445, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ASP", "id": 150, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ASP.NET 4.8", "id": 413, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Basic Authentication", "id": 163, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "CGI", "id": 151, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Common HTTP Features", "id": 141, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Default Document", "id": 143, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Directory Browsing", "id": 144, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Dynamic Content Compression", "id": 173, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Request Filtering", "id": 169, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Health and Diagnostics", "id": 155, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "HTTP Errors", "id": 145, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "HTTP Logging", "id": 156, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "HTTP Redirection", "id": 146, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Tracing", "id": 159, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Server Side Includes", "id": 154, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ISAPI Extensions", "id": 152, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ISAPI Filters", "id": 153, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Logging Tools", "id": 157, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "IIS 6 Metabase Compatibility", "id": 179, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "IIS 6 Management Compatibility", "id": 178, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "IIS Management Console", "id": 175, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Management Tools", "id": 174, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": ".NET Extensibility 4.8", "id": 414, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Performance", "id": 171, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "IIS Management Scripts and Tools", "id": 176, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Security", "id": 162, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Web Server (IIS)", "id": 2, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Static Content Compression", "id": 172, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Static Content", "id": 142, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Web Server", "id": 140, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Windows Authentication", "id": 164, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "IIS 6 WMI Compatibility", "id": 180, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}], "reboot_required": false, "success": true}

null_resource.RunInitialPlaybook (local-exec): TASK [Reboot when Required Roles need a restart] *******************************
null_resource.RunInitialPlaybook (local-exec): skipping: [tf-cvad-v-ws.the-austrian-citrix-guy.at] =&gt; {"changed": false, "false_condition": "requirements.reboot_required", "skip_reason": "Conditional result was False"}

null_resource.RunInitialPlaybook (local-exec): TASK [Install SSL-Certificate on Servers] **************************************
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-v-ws.the-austrian-citrix-guy.at] =&gt; {"changed": true, "thumbprints": ["0BB52BDDEB78E6E8E32FF3B1949CAA462B8EC386"]}

null_resource.RunInitialPlaybook (local-exec): TASK [Run installer from mounted ISO] ******************************************
null_resource.RunInitialPlaybook: Still creating... [18m50s elapsed]
...
null_resource.RunInitialPlaybook: Still creating... [22m20s elapsed]
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-v-ws.the-austrian-citrix-guy.at] =&gt; {"changed": true, "checksum": "EADE21C7B09DED5626F3BB51D4DF7A4C0E7AC607", "rc": 0, "reboot_required": false}

null_resource.RunInitialPlaybook (local-exec): TASK [Reboot after sf] *********************************************************
null_resource.RunInitialPlaybook: Still creating... [22m30s elapsed]
null_resource.RunInitialPlaybook: Still creating... [22m40s elapsed]
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-v-ws.the-austrian-citrix-guy.at] =&gt; {"changed": true, "elapsed": 23, "rebooted": true, "unreachable": false}

null_resource.RunInitialPlaybook (local-exec): PLAY RECAP *********************************************************************
null_resource.RunInitialPlaybook (local-exec): tf-cvad-v-ws.the-austrian-citrix-guy.at : ok=11   changed=9    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0

null_resource.RunInitialPlaybook: Creation complete after 22m45s [id=7191293509250199804]
data.external.CheckAnsibleReturnCode: Reading...
data.external.CheckAnsibleReturnCode: Read complete after 0s [id=-]
null_resource.IfExitCodeIsNotZeroThenHalt: Creating...
null_resource.IfExitCodeIsNotZeroThenHalt: Provisioning with 'local-exec'...
null_resource.IfExitCodeIsNotZeroThenHalt (local-exec): Executing: ["/bin/bash" "-c" "[ \"1\" = \"1\" ] &amp;&amp; echo \"Deploying WebStudio: Everything OK\" || { echo \"Deploying the WebStudio software and/or its prerequisites failed. Please review the settings. Halting Terraform.\"; exit 1; }"]
null_resource.IfExitCodeIsNotZeroThenHalt (local-exec): Deploying WebStudio: Everything OK
null_resource.IfExitCodeIsNotZeroThenHalt: Creation complete after 0s [id=416418525198442619]

Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
azadmin@tacg-cicd:/etc/terraform-tmm/CVADVS/_InstallWebStudio$After successful deployment, the Web Studio server is now installed and ready. We can continue with the next step.  Part 7: Using Terraform and Ansible to change the SSL BindingsWe want to configure all transport in our environment as securely as possible. Therefore, Terraform and Ansible take additional steps to configure all SSL bindings across all servers and listeners to enable complete HTTPS communication. Where applicable, the SSL certificate is changed to a publicly signed certificate to avoid certificate trust errors. The change is applied by a PowerShell script, which Ansible runs on each applicable host. To obtain all required elevated privileges, we use PsExec64.exe as the host to run the PowerShell scripts. Terraform monitors each Ansible Playbook run and stops further steps if a Playbook fails. Example Terraform snippet: # Terraform Deployment of Citrix CVAD on vSphere 8 - 25.11 
## Definition of vSphere Provider Variables - not all available variables are defined, only these we use
### Reference https://registry.terraform.io/providers/vmware/vsphere/latest/docs

#### Copy the required Ansible Assets to the Ansible Server
###### ***** IMPORTANT: You must set the permissions to the destination directory accordingly (sudo) chmod 777 otherwise the provisioning will fail *****
resource "null_resource" "CopyAnsibleSiteAssetsToAnsibleServer" {
  connection {
    type     = var.TACG-TMM-VSP-CL-WinRM-Connection-Type
    user     = var.TACG-TMM-VSP-CL-WinRM-Connection-User
    password = var.TACG-TMM-VSP-CL-WinRM-Connection-Password
    host     = var.TACG-TMM-VSP-CL-WinRM-Connection-Ansible
    port     = var.TACG-TMM-VSP-CL-WinRM-Connection-Port
    https    = var.TACG-TMM-VSP-CL-WinRM-Connection-HTTPS
    timeout  = var.TACG-TMM-VSP-CL-WinRM-Connection-Timeout
  }

  # Copy AdditionalSiteConfiguration Playbook file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-VSP-CL-Ansible-ChangeSSLOnServers-Playbook-Source
    destination = var.TACG-TMM-VSP-CL-Ansible-ChangeSSLOnServers-Playbook-Destination
  }
}

#### Copy the required PowerShell Scripts to the DDC1
resource "null_resource" "CopyPowerShellScriptsToDDC1" {
  connection {
    type     = var.TACG-TMM-VSP-CL-WinRM-Connection-Type
    user     = var.TACG-TMM-VSP-CL-WinRM-Connection-User
    password = var.TACG-TMM-VSP-CL-WinRM-Connection-Password
    host     = var.TACG-TMM-VSP-CL-WinRM-Connection-DDC1
    port     = var.TACG-TMM-VSP-CL-WinRM-Connection-Port
    https    = var.TACG-TMM-VSP-CL-WinRM-Connection-HTTPS
    timeout  = var.TACG-TMM-VSP-CL-WinRM-Connection-Timeout
  }

  # Copy ChangeSSLCOnServers.ps1 file To the DDC1
  provisioner "file" {
    source      = var.TACG-TMM-VSP-CL-Ansible-ChangeSSLOnServers-Source
    destination = var.TACG-TMM-VSP-CL-Ansible-ChangeSSLOnServers-Destination
  }

  # Copy PSExec64.exe file To the DDC1
  provisioner "file" {
    source      = var.TACG-TMM-VSP-CL-PsExec64-Source
    destination = var.TACG-TMM-VSP-CL-PsExec64-Destination
  }
}

#### Copy the required PowerShell Scripts to the DDC2
resource "null_resource" "CopyPowerShellScriptsToDDC2" {
  connection {
    type     = var.TACG-TMM-VSP-CL-WinRM-Connection-Type
    user     = var.TACG-TMM-VSP-CL-WinRM-Connection-User
    password = var.TACG-TMM-VSP-CL-WinRM-Connection-Password
    host     = var.TACG-TMM-VSP-CL-WinRM-Connection-DDC2
    port     = var.TACG-TMM-VSP-CL-WinRM-Connection-Port
    https    = var.TACG-TMM-VSP-CL-WinRM-Connection-HTTPS
    timeout  = var.TACG-TMM-VSP-CL-WinRM-Connection-Timeout
  }

  # Copy ChangeSSLCOnServers.ps1 file To the DDC2
  provisioner "file" {
    source      = var.TACG-TMM-VSP-CL-Ansible-ChangeSSLOnServers-Source
    destination = var.TACG-TMM-VSP-CL-Ansible-ChangeSSLOnServers-Destination
  }

  # Copy PSExec64.exe file To the DDC2
  provisioner "file" {
    source      = var.TACG-TMM-VSP-CL-PsExec64-Source
    destination = var.TACG-TMM-VSP-CL-PsExec64-Destination
  }
}

#### Copy the required PowerShell Scripts to the SF
resource "null_resource" "CopyPowerShellScriptsToSF" {
  connection {
    type     = var.TACG-TMM-VSP-CL-WinRM-Connection-Type
    user     = var.TACG-TMM-VSP-CL-WinRM-Connection-User
    password = var.TACG-TMM-VSP-CL-WinRM-Connection-Password
    host     = var.TACG-TMM-VSP-CL-WinRM-Connection-SF
    port     = var.TACG-TMM-VSP-CL-WinRM-Connection-Port
    https    = var.TACG-TMM-VSP-CL-WinRM-Connection-HTTPS
    timeout  = var.TACG-TMM-VSP-CL-WinRM-Connection-Timeout
  }

  # Copy ChangeSSLCOnServers.ps1 file To the SF
  provisioner "file" {
    source      = var.TACG-TMM-VSP-CL-Ansible-ChangeSSLOnServers-Source
    destination = var.TACG-TMM-VSP-CL-Ansible-ChangeSSLOnServers-Destination
  }

  # Copy PSExec64.exe file To the SF
  provisioner "file" {
    source      = var.TACG-TMM-VSP-CL-PsExec64-Source
    destination = var.TACG-TMM-VSP-CL-PsExec64-Destination
  }
}

#### Copy the required PowerShell Scripts to the WS
resource "null_resource" "CopyPowerShellScriptsToWS" {
  connection {
    type     = var.TACG-TMM-VSP-CL-WinRM-Connection-Type
    user     = var.TACG-TMM-VSP-CL-WinRM-Connection-User
    password = var.TACG-TMM-VSP-CL-WinRM-Connection-Password
    host     = var.TACG-TMM-VSP-CL-WinRM-Connection-WS
    port     = var.TACG-TMM-VSP-CL-WinRM-Connection-Port
    https    = var.TACG-TMM-VSP-CL-WinRM-Connection-HTTPS
    timeout  = var.TACG-TMM-VSP-CL-WinRM-Connection-Timeout
  }

  # Copy ChangeSSLCOnServers.ps1 file To the WS
  provisioner "file" {
    source      = var.TACG-TMM-VSP-CL-Ansible-ChangeSSLOnServers-Source
    destination = var.TACG-TMM-VSP-CL-Ansible-ChangeSSLOnServers-Destination
  }

  # Copy PSExec64.exe file To the WS
  provisioner "file" {
    source      = var.TACG-TMM-VSP-CL-PsExec64-Source
    destination = var.TACG-TMM-VSP-CL-PsExec64-Destination
  }
}

resource "time_sleep" "wait_10_seconds" {
  depends_on      = [null_resource.CopyAnsibleSiteAssetsToAnsibleServer, null_resource.CopyPowerShellScriptsToDDC1]
  create_duration = "10s"
}

##### Change the SSL Bindings Start
##### Connect to Ansible Interpreter and run ChangeSSLOnServers-Playbook
resource "null_resource" "RunChangeSSLPlaybook" {
  depends_on = [time_sleep.wait_10_seconds]

  provisioner "local-exec" {
    working_dir = "/etc/ansible"
    command     = var.TACG-TMM-VSP-CL-Ansible-ChangeSSLOnServers-CMD
  }
}

##### Check Ansible Return Code
data "external" "CheckAnsibleReturnCode" {
  depends_on = [null_resource.RunChangeSSLPlaybook]
  program    = ["bash", "/etc/ansible/assets/CheckAnsibleReturnCode.sh", "/etc/ansible/assets/ansible_exit_code.txt", "0"]
}

##### Terminate if Return Code is not 0
resource "null_resource" "IfExitCodeIsNotZeroThenHalt" {
  depends_on = [data.external.CheckAnsibleReturnCode]
  provisioner "local-exec" {
    interpreter = ["/bin/bash", "-c"]
    command     = "[ \"${data.external.CheckAnsibleReturnCode.result.found}\" = \"1\" ] &amp;&amp; echo \"Change SSL Bindings - Everything OK\" || { echo \"Changing the SSL bindings failed. Please review the settings. Halting Terraform.\"; exit 1; }"
  }
}

##### Change the SSL Bindings EndExample Ansible Playbook for changing the SSL bindings on the DDCs: # Terraform Deployment of Citrix CVAD on vSphere 8 - 25.11 
## For installation and configuration of the Delivery Controllers, StoreFront-Servers and WebStudio-Servers, we use Ansible Playbooks 
### Reference https://docs.ansible.com/ansible/latest/getting_started/index.html
#### We assume that the communication between the Ansible Server and all needed Hosts via WinRM is working as intended - check your Group Policies
##### All hosts and its corresponding communication-related Ansible variables are stored in assets/inventory.ini
###### Change SSL Certificate bindings to the previously uploaded public SSL Certificate by running a PowerShell script on all CVAD servers
- name: Change SSL Certificate bindings
  hosts: cvad-ddcs-v
  gather_facts: no
  collections:
    - community.windows
  vars:
    domain_user: "TACG\\XxXxXxXxX"
    domain_pass: "!XxXxXxXxXxXxXxXxX!"
    psexec_path: "C:\\InstallCVAD\\PsExec64.exe"
    remote_script: "C:\\InstallCVAD\\ChangeSSLOnServers.ps1"
    ansible_user: "svc_ansible"      
    ansible_password: „!XxXxXxXxXxXxXxXxX!“  
    ansible_connection: winrm
    ansible_winrm_transport: certificate

  tasks:
   - name: Run the ChangeSSLOnDDCs-PowerShell script as Domain Administrator
     community.windows.win_psexec:
       command: powershell.exe -NoProfile -ExecutionPolicy Bypass -File "{{ remote_script }}"
       #hostnames:
       #  - "{{ target_host }}"
       executable: "{{ psexec_path }}"
       username: "{{ domain_user }}"
       password: "{{ domain_pass }}"
       elevated: true
       noprofile: true
       interactive: true
       wait: trueExample PowerShell script to change the SSL bindings: Try {  
$friendlyName = "*.the-austrian-citrix-guy.at"
$Thumbprint = (Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object {$_.FriendlyName -eq $friendlyName}).Thumbprint -join ';'
$ThumbprintC = ($ThumbPrint.Replace(" ",""))

netsh http delete sslcert ipport=0.0.0.0:443

$NGuid = [guid]::NewGuid().ToString("B")   

netsh http add sslcert ipport=0.0.0.0:443 certhash=$ThumbprintC appid=$NGuid disablelegacytls=enable

netsh http show sslcert

    } catch {
    $logFile = "C:\Logs\MyLogFile.log"
    $errorMessage = $_.Exception.Message
    Add-Content -Path $logFile -Value "$(Get-Date) - ChangeSSLCertificateBindings: Error: $errorMessage"
    Write-Output "An error occurred trying to change the SSL-Certificate bindings (error: $($Error[0]))"
    Exit 1  

    }Running the snippet should fulfill all needed steps – example for running it on the DDCs: azadmin@tacg-cicd:/etc/terraform-tmm/CVADVS/_ChangeSSLOnServers$ terraform apply

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create
 &lt;= read (data resources)

Terraform will perform the following actions:

  # data.external.CheckAnsibleReturnCode will be read during apply
  # (depends on a resource or a module with changes pending)
 &lt;= data "external" "CheckAnsibleReturnCode" {
      + id      = (known after apply)
      + program = [
          + "bash",
          + "/etc/ansible/assets/CheckAnsibleReturnCode.sh",
          + "/etc/ansible/assets/ansible_exit_code.txt",
          + "0",
        ]
      + result  = (known after apply)
    }

  # null_resource.CopyAnsibleSiteAssetsToAnsibleServer will be created
  + resource "null_resource" "CopyAnsibleSiteAssetsToAnsibleServer" {
      + id = (known after apply)
    }

  # null_resource.CopyPowerShellScriptsToDDC1 will be created
  + resource "null_resource" "CopyPowerShellScriptsToDDC1" {
      + id = (known after apply)
    }

  # null_resource.CopyPowerShellScriptsToDDC2 will be created
  + resource "null_resource" "CopyPowerShellScriptsToDDC2" {
      + id = (known after apply)
    }

  # null_resource.CopyPowerShellScriptsToSF will be created
  + resource "null_resource" "CopyPowerShellScriptsToSF" {
      + id = (known after apply)
    }

  # null_resource.CopyPowerShellScriptsToWS will be created
  + resource "null_resource" "CopyPowerShellScriptsToWS" {
      + id = (known after apply)
    }

  # null_resource.IfExitCodeIsNotZeroThenHalt will be created
  + resource "null_resource" "IfExitCodeIsNotZeroThenHalt" {
      + id = (known after apply)
    }

  # null_resource.RunChangeSSLPlaybook will be created
  + resource "null_resource" "RunChangeSSLPlaybook" {
      + id = (known after apply)
    }

  # time_sleep.wait_10_seconds will be created
  + resource "time_sleep" "wait_10_seconds" {
      + create_duration = "10s"
      + 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

null_resource.CopyAnsibleSiteAssetsToAnsibleServer: Creating...
null_resource.CopyPowerShellScriptsToWS: Creating...
null_resource.CopyPowerShellScriptsToDDC1: Creating...
null_resource.CopyPowerShellScriptsToDDC1: Provisioning with 'file'...
null_resource.CopyPowerShellScriptsToWS: Provisioning with 'file'...
null_resource.CopyPowerShellScriptsToSF: Creating...
null_resource.CopyPowerShellScriptsToDDC2: Creating...
null_resource.CopyAnsibleSiteAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPowerShellScriptsToDDC2: Provisioning with 'file'...
null_resource.CopyPowerShellScriptsToSF: Provisioning with 'file'...
null_resource.CopyAnsibleSiteAssetsToAnsibleServer: Creation complete after 0s [id=8882922836300404953]
#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;
...
null_resource.CopyPowerShellScriptsToDDC1: Creation complete after 10s [id=4814951330333809760]
null_resource.CopyPowerShellScriptsToSF: Creation complete after 10s [id=6247443398511140586]
null_resource.CopyPowerShellScriptsToDDC2: Creation complete after 10s [id=4396737003381006003]

...
time_sleep.wait_10_seconds: Still creating... [00m10s elapsed]
time_sleep.wait_10_seconds: Creation complete after 10s [id=2025-11-13T12:24:08Z]
null_resource.RunChangeSSLPlaybook: Creating...
null_resource.RunChangeSSLPlaybook: Provisioning with 'local-exec'...
null_resource.RunChangeSSLPlaybook (local-exec): Executing: ["/bin/sh" "-c" "ansible-playbook /etc/ansible/assets/ChangeSSLOnServers.ansible.yml -i /etc/ansible/assets/inventory.ini -vvv -c winrm; echo $? &gt; /etc/ansible/assets/ansible_exit_code.txt"]
null_resource.RunChangeSSLPlaybook (local-exec): Using /etc/ansible/ansible.cfg as config file

null_resource.RunChangeSSLPlaybook (local-exec): PLAYBOOK: ChangeSSLOnServers.ansible.yml ***************************************
null_resource.RunChangeSSLPlaybook (local-exec): 1 plays in /etc/ansible/assets/ChangeSSLOnServers.ansible.yml

null_resource.RunChangeSSLPlaybook (local-exec): PLAY [Change SSL Certificate bindings] *****************************************

null_resource.RunChangeSSLPlaybook (local-exec): TASK [Run the ChangeSSLOnDDCs-PowerShell script as Domain Administrator] *******
null_resource.RunChangeSSLPlaybook (local-exec): task path: /etc/ansible/assets/ChangeSSLOnServers.ansible.yml:26
null_resource.RunChangeSSLPlaybook (local-exec): Using module file /usr/lib/python3/dist-packages/ansible_collections/community/windows/plugins/modules/win_psexec.ps1
null_resource.RunChangeSSLPlaybook (local-exec): Pipelining is enabled.
null_resource.RunChangeSSLPlaybook (local-exec): &lt;tf-cvad-v-ddc1.the-austrian-citrix-guy.at&gt; ESTABLISH WINRM CONNECTION FOR USER: svc_ansible on PORT 5986 TO tf-cvad-v-ddc1.the-austrian-citrix-guy.at
null_resource.RunChangeSSLPlaybook (local-exec): EXEC (via pipeline wrapper)
null_resource.RunChangeSSLPlaybook (local-exec): changed: [tf-cvad-v-ddc1.the-austrian-citrix-guy.at] =&gt; {
null_resource.RunChangeSSLPlaybook (local-exec):     "changed": true,
null_resource.RunChangeSSLPlaybook (local-exec):     "delta": "0:00:01.130213",
null_resource.RunChangeSSLPlaybook (local-exec):     "end": "2025-11-13 12:24:09.735282",
null_resource.RunChangeSSLPlaybook (local-exec):     "invocation": {
null_resource.RunChangeSSLPlaybook (local-exec):         "module_args": {
null_resource.RunChangeSSLPlaybook (local-exec):             "chdir": null,
null_resource.RunChangeSSLPlaybook (local-exec):             "command": "powershell.exe -NoProfile -ExecutionPolicy Bypass -File \"C:\\InstallCVAD\\ChangeSSLOnServers.ps1\"",
null_resource.RunChangeSSLPlaybook (local-exec):             "elevated": true,
null_resource.RunChangeSSLPlaybook (local-exec):             "executable": "C:\\InstallCVAD\\PsExec64.exe",
null_resource.RunChangeSSLPlaybook (local-exec):             "hostnames": null,
null_resource.RunChangeSSLPlaybook (local-exec):             "interactive": true,
null_resource.RunChangeSSLPlaybook (local-exec):             "limited": false,
null_resource.RunChangeSSLPlaybook (local-exec):             "nobanner": false,
null_resource.RunChangeSSLPlaybook (local-exec):             "noprofile": true,
null_resource.RunChangeSSLPlaybook (local-exec):             "password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
null_resource.RunChangeSSLPlaybook (local-exec):             "priority": null,
null_resource.RunChangeSSLPlaybook (local-exec):             "session": null,
null_resource.RunChangeSSLPlaybook (local-exec):             "system": false,
null_resource.RunChangeSSLPlaybook (local-exec):             "timeout": null,
null_resource.RunChangeSSLPlaybook (local-exec):             "username": "TACG\\administrator",
null_resource.RunChangeSSLPlaybook (local-exec):             "wait": true
null_resource.RunChangeSSLPlaybook (local-exec):         }
null_resource.RunChangeSSLPlaybook (local-exec):     },
null_resource.RunChangeSSLPlaybook (local-exec):     "psexec_command": "C:\\InstallCVAD\\PsExec64.exe -u TACG\\administrator -p *PASSWORD_REPLACED* -e -h -i -accepteula powershell.exe -NoProfile -ExecutionPolicy Bypass -File \"C:\\InstallCVAD\\ChangeSSLOnServers.ps1\"",
null_resource.RunChangeSSLPlaybook (local-exec):     "rc": 0,
null_resource.RunChangeSSLPlaybook (local-exec):     "start": "2025-11-13 12:24:08.605068",
null_resource.RunChangeSSLPlaybook (local-exec):     "stderr": "Connecting to local system...\r\r\rStarting PSEXESVC service on local system...\r\r\rCopying authentication key to TF-CVAD-V-DDC1...\r\r\rConnecting with PsExec service on TF-CVAD-V-DDC1...\r\r\rStarting powershell.exe on TF-CVAD-V-DDC1...\r\r\r\r\npowershell.exe exited on TF-CVAD-V-DDC1 with error code 0.\r\n",
null_resource.RunChangeSSLPlaybook (local-exec):     "stderr_lines": [
null_resource.RunChangeSSLPlaybook (local-exec):         "Connecting to local system...",
null_resource.RunChangeSSLPlaybook (local-exec):         "",
null_resource.RunChangeSSLPlaybook (local-exec):         "",
null_resource.RunChangeSSLPlaybook (local-exec):         "Starting PSEXESVC service on local system...",
null_resource.RunChangeSSLPlaybook (local-exec):         "",
null_resource.RunChangeSSLPlaybook (local-exec):         "",
null_resource.RunChangeSSLPlaybook (local-exec):         "Copying authentication key to TF-CVAD-V-DDC1...",
null_resource.RunChangeSSLPlaybook (local-exec):         "",
null_resource.RunChangeSSLPlaybook (local-exec):         "",
null_resource.RunChangeSSLPlaybook (local-exec):         "Connecting with PsExec service on TF-CVAD-V-DDC1...",
null_resource.RunChangeSSLPlaybook (local-exec):         "",
null_resource.RunChangeSSLPlaybook (local-exec):         "",
null_resource.RunChangeSSLPlaybook (local-exec):         "Starting powershell.exe on TF-CVAD-V-DDC1...",
null_resource.RunChangeSSLPlaybook (local-exec):         "",
null_resource.RunChangeSSLPlaybook (local-exec):         "",
null_resource.RunChangeSSLPlaybook (local-exec):         "",
null_resource.RunChangeSSLPlaybook (local-exec):         "powershell.exe exited on TF-CVAD-V-DDC1 with error code 0."
null_resource.RunChangeSSLPlaybook (local-exec):     ],
null_resource.RunChangeSSLPlaybook (local-exec):     "stdout": "\r\nPsExec v2.43 - Execute processes remotely\r\nCopyright (C) 2001-2023 Mark Russinovich\r\nSysinternals - www.sysinternals.com\r\n\r\n",
null_resource.RunChangeSSLPlaybook (local-exec):     "stdout_lines": [
null_resource.RunChangeSSLPlaybook (local-exec):         "",
null_resource.RunChangeSSLPlaybook (local-exec):         "PsExec v2.43 - Execute processes remotely",
null_resource.RunChangeSSLPlaybook (local-exec):         "Copyright (C) 2001-2023 Mark Russinovich",
null_resource.RunChangeSSLPlaybook (local-exec):         "Sysinternals - www.sysinternals.com",
null_resource.RunChangeSSLPlaybook (local-exec):         ""
null_resource.RunChangeSSLPlaybook (local-exec):     ]
null_resource.RunChangeSSLPlaybook (local-exec): }
null_resource.RunChangeSSLPlaybook (local-exec): PLAY RECAP *********************************************************************
null_resource.RunChangeSSLPlaybook (local-exec): tf-cvad-v-ddc1.the-austrian-citrix-guy.at : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
null_resource.RunChangeSSLPlaybook (local-exec): tf-cvad-v-ddc2.the-austrian-citrix-guy.at : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
...
null_resource.RunChangeSSLPlaybook: Creation complete after 3s [id=6734265951530601448]
data.external.CheckAnsibleReturnCode: Reading...
data.external.CheckAnsibleReturnCode: Read complete after 0s [id=-]
null_resource.IfExitCodeIsNotZeroThenHalt: Creating...
null_resource.IfExitCodeIsNotZeroThenHalt: Provisioning with 'local-exec'...
null_resource.IfExitCodeIsNotZeroThenHalt (local-exec): Executing: ["/bin/bash" "-c" "[ \"1\" = \"1\" ] &amp;&amp; echo \"Change SSL Bindings - Everything OK\" || { echo \"Changing the SSL bindings failed. Please review the settings. Halting Terraform.\"; exit 1; }"]
null_resource.IfExitCodeIsNotZeroThenHalt (local-exec): Change SSL Bindings - Everything OK
null_resource.IfExitCodeIsNotZeroThenHalt: Creation complete after 0s [id=5221894585109127965]

Apply complete! Resources: 8 added, 0 changed, 0 destroyed.
azadmin@tacg-cicd:/etc/terraform-tmm/CVADVS/_ChangeSSLOnServers$ Part 8: Using Terraform and Ansible to create the CVAD site and all its necessitiesCreating the CVAD site, including its requirements and dependencies, is a complex task that involves many steps. Terraform and Ansible together fulfill the following tasks: Create all databases Create the site Add the second DDC to the site Configure additional site configurations Configure a dedicated Administrators group Configure the licensing Before running these tasks, we see that no vSphere-related databases exist on SQL Server, and no site is configured on the primary Delivery Controller:  (Remark: the DB-CitrixTF-TACG-CVAD2507-Databases are for the XenServer-based CVAD deployment!)   Important: All Terraform configurations and Ansible Playbooks, as well as the corresponding PowerShell scripts, must be run in the Site Administrator´s context – it must be a Domain user account. To obtain all required elevated privileges, we use PsExec64.exe as the host to run the PowerShell scripts. If we do not use the correct user account and privileges, the deployment will fail. Terraform monitors each Ansible Playbook run and stops further steps if a Playbook fails. Example Terraform snippet: # Terraform Deployment of Citrix CVAD on vSphere 8 - 25.11 
## Definition of vSphere Provider Variables - not all available variables are defined, only these we use
### Reference https://registry.terraform.io/providers/vmware/vsphere/latest/docs

#### Copy the required Ansible Assets to the Ansible Server
###### ***** IMPORTANT: You must set the permissions to the destination directory accordingly (sudo) chmod 777 otherwise the provisioning will fail *****
resource "null_resource" "CopyAnsibleSiteAssetsToAnsibleServer" {
  connection {
    type     = var.TACG-TMM-VSP-CL-WinRM-Connection-Type
    user     = var.TACG-TMM-VSP-CL-WinRM-Connection-User
    password = var.TACG-TMM-VSP-CL-WinRM-Connection-Password
    host     = var.TACG-TMM-VSP-CL-WinRM-Connection-HostIP
    port     = var.TACG-TMM-VSP-CL-WinRM-Connection-Port
    https    = var.TACG-TMM-VSP-CL-WinRM-Connection-HTTPS
    timeout  = var.TACG-TMM-VSP-CL-WinRM-Connection-Timeout
  }

  # Copy AdditionalSiteConfiguration Playbook file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-VSP-CL-Ansible-AdditionalSiteConfiguration-Playbook-Source
    destination = var.TACG-TMM-VSP-CL-Ansible-AdditionalSiteConfiguration-Playbook-Destination
  }

  # Copy AddSecondDDCToSite Playbook file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-VSP-CL-Ansible-AddSecondDDCToSite-Playbook-Source
    destination = var.TACG-TMM-VSP-CL-Ansible-AddSecondDDCToSite-Playbook-Destination
  }

  # Copy ConfigureAdminGroup Playbook file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-VSP-CL-Ansible-ConfigureAdminGroup-Playbook-Source
    destination = var.TACG-TMM-VSP-CL-Ansible-ConfigureAdminGroup-Playbook-Destination
  }

  # Copy CreateDatabase Playbook file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-VSP-CL-Ansible-CreateDatabase-Playbook-Source
    destination = var.TACG-TMM-VSP-CL-Ansible-CreateDatabase-Playbook-Destination
  }

  # Copy CreateSite Playbook file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-VSP-CL-Ansible-CreateSite-Playbook-Source
    destination = var.TACG-TMM-VSP-CL-Ansible-CreateSite-Playbook-Destination
  }

  # Copy SetLicensing Playbook file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-VSP-CL-Ansible-SetLicensing-Playbook-Source
    destination = var.TACG-TMM-VSP-CL-Ansible-SetLicensing-Playbook-Destination
  }
}

#### Copy the required PowerShell Scripts to the DDC
resource "null_resource" "CopyPowerShellScriptsToDDC1" {
  connection {
    type     = var.TACG-TMM-VSP-CL-WinRM-Connection-Type
    user     = var.TACG-TMM-VSP-CL-WinRM-Connection-User
    password = var.TACG-TMM-VSP-CL-WinRM-Connection-Password
    host     = var.TACG-TMM-VSP-CL-WinRM-Connection-HostIP
    port     = var.TACG-TMM-VSP-CL-WinRM-Connection-Port
    https    = var.TACG-TMM-VSP-CL-WinRM-Connection-HTTPS
    timeout  = var.TACG-TMM-VSP-CL-WinRM-Connection-Timeout
  }

  # Copy AdditionalSiteConfiguration.ps1 file To the DDC
  provisioner "file" {
    source      = var.TACG-TMM-VSP-CL-DDC-AdditionalSiteConfiguration-Source
    destination = var.TACG-TMM-VSP-CL-DDC-AdditionalSiteConfiguration-Destination
  }

  # Copy AddSecondDDCToSite.ps1 file To the DDC
  provisioner "file" {
    source      = var.TACG-TMM-VSP-CL-DDC-AddSecondDDCToSite-Source
    destination = var.TACG-TMM-VSP-CL-DDC-AddSecondDDCToSite-Destination
  }

  # Copy ConfigureAdminGroup.ps1 file To the DDC
  provisioner "file" {
    source      = var.TACG-TMM-VSP-CL-DDC-ConfigureAdminGroup-Source
    destination = var.TACG-TMM-VSP-CL-DDC-ConfigureAdminGroup-Destination
  }

  # Copy CreateDatabase.ps1 file To the DDC
  provisioner "file" {
    source      = var.TACG-TMM-VSP-CL-DDC-CreateDatabase-Source
    destination = var.TACG-TMM-VSP-CL-DDC-CreateDatabase-Destination
  }

  # Copy CreateSite.ps1 file To the DDC
  provisioner "file" {
    source      = var.TACG-TMM-VSP-CL-DDC-CreateSite-Source
    destination = var.TACG-TMM-VSP-CL-DDC-CreateSite-Destination
  }

  # Copy SetLicensing.ps1 file To the DDC
  provisioner "file" {
    source      = var.TACG-TMM-VSP-CL-DDC-SetLicensing-Source
    destination = var.TACG-TMM-VSP-CL-DDC-SetLicensing-Destination
  }

  # Copy PsExec.exe file To the DDC
  provisioner "file" {
    source      = var.TACG-TMM-VSP-CL-DDC-PsExec-Source
    destination = var.TACG-TMM-VSP-CL-DDC-PsExec-Destination
  }
}

resource "time_sleep" "wait_10_seconds" {
  depends_on      = [null_resource.CopyAnsibleSiteAssetsToAnsibleServer, null_resource.CopyPowerShellScriptsToDDC1]
  create_duration = "10s"
}

##### Create Databases Start
##### Connect to Ansible Interpreter and run CreateDatabase-Playbook on DDCs
resource "null_resource" "RunCreateDatabasePlaybook" {
  depends_on = [time_sleep.wait_10_seconds]

  provisioner "local-exec" {
    working_dir = "/etc/ansible"
    command     = var.TACG-TMM-VSP-CL-Ansible-CreateDatabase-CMD
  }
}

##### Check Ansible Return Code
data "external" "CheckAnsibleReturnCodeCDB" {
  depends_on = [null_resource.RunCreateDatabasePlaybook]
  program    = ["bash", "/etc/ansible/assets/CheckAnsibleReturnCode.sh", "/etc/ansible/assets/ansible_exit_code.txt", "0"]
}

##### Terminate if Return Code is not 0
resource "null_resource" "IfExitCodeIsNotZeroThenHaltCDB" {
  depends_on = [data.external.CheckAnsibleReturnCodeCDB]
  provisioner "local-exec" {
    interpreter = ["/bin/bash", "-c"]
    command     = "[ \"${data.external.CheckAnsibleReturnCodeCDB.result.found}\" = \"1\" ] &amp;&amp; echo \"Create Databases - Everything OK\" || { echo \"Deploying the CVAD Databases failed. Please review the settings. Halting Terraform.\"; exit 1; }"
  }
}

#### Wait 10s for Background Processes to Settle
resource "time_sleep" "wait_300_secondsCDB" {
  depends_on      = [null_resource.IfExitCodeIsNotZeroThenHaltCDB]
  create_duration = "300s"
}

##### Create Databases End

##### Create Site Start
##### Connect to Ansible Interpreter and run CreateSite-Playbook on DDCs
resource "null_resource" "RunCreateSitePlaybook" {
  depends_on = [time_sleep.wait_300_secondsCDB]
  provisioner "local-exec" {
    working_dir = "/etc/ansible"
    command     = var.TACG-TMM-VSP-CL-Ansible-CreateSite-CMD
  }
}

##### Check Ansible Return Code
data "external" "CheckAnsibleReturnCodeCS" {
  depends_on = [null_resource.RunCreateSitePlaybook]
  program    = ["bash", "/etc/ansible/assets/CheckAnsibleReturnCode.sh", "/etc/ansible/assets/ansible_exit_code.txt", "0"]
}

##### Terminate if Return Code is not 0
resource "null_resource" "IfExitCodeIsNotZeroThenHaltCS" {
  depends_on = [data.external.CheckAnsibleReturnCodeCS]
  provisioner "local-exec" {
    interpreter = ["/bin/bash", "-c"]
    command     = "[ \"${data.external.CheckAnsibleReturnCodeCS.result.found}\" = \"1\" ] &amp;&amp; echo \"Create Site - Everything OK\" || { echo \"Deploying the CVAD Site failed. Please review the settings. Halting Terraform.\"; exit 1; }"
  }
}

#### Wait 10s for Background Processes to Settle
resource "time_sleep" "wait_60_secondsCS" {
  depends_on      = [null_resource.IfExitCodeIsNotZeroThenHaltCS]
  create_duration = "60s"
}

##### Create Site End

##### Create SiteAddSecondDDC Start
##### Connect to Ansible Interpreter and run AddSecondDDC-Playbook on DDCs
resource "null_resource" "RunAddSecondDDCPlaybook" {
  depends_on = [time_sleep.wait_60_secondsCS]
  provisioner "local-exec" {
    working_dir = "/etc/ansible"
    command     = var.TACG-TMM-VSP-CL-Ansible-AddSecondDDCToSite-CMD
  }
}

##### Check Ansible Return Code
data "external" "CheckAnsibleReturnCodeSecondDDC" {
  depends_on = [null_resource.RunAddSecondDDCPlaybook]
  program    = ["bash", "/etc/ansible/assets/CheckAnsibleReturnCode.sh", "/etc/ansible/assets/ansible_exit_code.txt", "0"]
}

##### Terminate if Return Code is not 0
resource "null_resource" "IfExitCodeIsNotZeroThenHaltSecondDDC" {
  depends_on = [data.external.CheckAnsibleReturnCodeSecondDDC]
  provisioner "local-exec" {
    interpreter = ["/bin/bash", "-c"]
    command     = "[ \"${data.external.CheckAnsibleReturnCodeSecondDDC.result.found}\" = \"1\" ] &amp;&amp; echo \"Add Second DDC - Everything OK\" || { echo \"Adding second DDC to the CVAD Site failed. Please review the settings. Halting Terraform.\"; exit 1; }"
  }
}

#### Wait 10s for Background Processes to Settle
resource "time_sleep" "wait_10_secondsSecondDDC" {
  depends_on      = [null_resource.IfExitCodeIsNotZeroThenHaltSecondDDC]
  create_duration = "10s"
}

##### Create SiteAddSecondDDC End

##### Create AdditionalSiteConfiguration Start
##### Connect to Ansible Interpreter and run AdditionalSiteConfiguration-Playbook on DDCs
resource "null_resource" "RunAdditionalSiteConfigurationPlaybook" {
  depends_on = [time_sleep.wait_10_secondsSecondDDC]

  provisioner "local-exec" {
    working_dir = "/etc/ansible"
    command     = var.TACG-TMM-VSP-CL-Ansible-AdditionalSiteConfiguration-CMD
  }
}

##### Check Ansible Return Code
data "external" "CheckAnsibleReturnCodeAdditionalSiteConfiguration" {
  depends_on = [null_resource.RunAdditionalSiteConfigurationPlaybook]
  program    = ["bash", "/etc/ansible/assets/CheckAnsibleReturnCode.sh", "/etc/ansible/assets/ansible_exit_code.txt", "0"]
}

##### Terminate if Return Code is not 0
resource "null_resource" "IfExitCodeIsNotZeroThenHaltAdditionalSiteConfiguration" {
  depends_on = [data.external.CheckAnsibleReturnCodeAdditionalSiteConfiguration]
  provisioner "local-exec" {
    interpreter = ["/bin/bash", "-c"]
    command     = "[ \"${data.external.CheckAnsibleReturnCodeAdditionalSiteConfiguration.result.found}\" = \"1\" ] &amp;&amp; echo \"Additional Site Configuration - Everything OK\" || { echo \"Running the additional Site Configurations failed. Please review the settings. Halting Terraform.\"; exit 1; }"
  }
}

#### Wait 10s for Background Processes to Settle
resource "time_sleep" "wait_10_secondsAdditionalSiteConfiguration" {
  depends_on      = [null_resource.IfExitCodeIsNotZeroThenHaltAdditionalSiteConfiguration]
  create_duration = "10s"
}

##### Create AdditionalSiteConfiguration End

##### Create ConfigureAdminGroup Start
##### Connect to Ansible Interpreter and run ConfigureAdminGroup-Playbook on DDCs
resource "null_resource" "RunConfigureAdminGroupPlaybook" {
  depends_on = [time_sleep.wait_10_secondsAdditionalSiteConfiguration]

  provisioner "local-exec" {
    working_dir = "/etc/ansible"
    command     = var.TACG-TMM-VSP-CL-Ansible-ConfigureAdminGroup-CMD
  }
}

##### Check Ansible Return Code
data "external" "CheckAnsibleReturnCodeConfigureAdminGroup" {
  depends_on = [null_resource.RunConfigureAdminGroupPlaybook]
  program    = ["bash", "/etc/ansible/assets/CheckAnsibleReturnCode.sh", "/etc/ansible/assets/ansible_exit_code.txt", "0"]
}

##### Terminate if Return Code is not 0
resource "null_resource" "IfExitCodeIsNotZeroThenHaltConfigureAdminGroup" {
  depends_on = [data.external.CheckAnsibleReturnCodeConfigureAdminGroup]
  provisioner "local-exec" {
    interpreter = ["/bin/bash", "-c"]
    command     = "[ \"${data.external.CheckAnsibleReturnCodeConfigureAdminGroup.result.found}\" = \"1\" ] &amp;&amp; echo \"Admin Group Configuration - Everything OK\" || { echo \"Running the Configuration of the AdminGroup failed. Please review the settings. Halting Terraform.\"; exit 1; }"
  }
}

#### Wait 10s for Background Processes to Settle
resource "time_sleep" "wait_10_secondsConfigureAdminGroup" {
  depends_on      = [null_resource.IfExitCodeIsNotZeroThenHaltConfigureAdminGroup]
  create_duration = "10s"
}

##### Create ConfigureAdminGroup End

##### Create SetLicensing Start
##### Connect to Ansible Interpreter and run SetLicensing-Playbook on DDCs
resource "null_resource" "RunConfigureSetLicensingPlaybook" {
  depends_on = [time_sleep.wait_10_secondsConfigureAdminGroup]

  provisioner "local-exec" {
    working_dir = "/etc/ansible"
    command     = var.TACG-TMM-VSP-CL-Ansible-SetLicensing-CMD
  }
}

##### Check Ansible Return Code
data "external" "CheckAnsibleReturnCodeSetLicensing" {
  depends_on = [null_resource.RunConfigureSetLicensingPlaybook]
  program    = ["bash", "/etc/ansible/assets/CheckAnsibleReturnCode.sh", "/etc/ansible/assets/ansible_exit_code.txt", "0"]
}

##### Terminate if Return Code is not 0
resource "null_resource" "IfExitCodeIsNotZeroThenHaltSetLicensing" {
  depends_on = [data.external.CheckAnsibleReturnCodeSetLicensing]
  provisioner "local-exec" {
    interpreter = ["/bin/bash", "-c"]
    command     = "[ \"${data.external.CheckAnsibleReturnCodeSetLicensing.result.found}\" = \"1\" ] &amp;&amp; echo \"Licensing Configuration - Everything OK\" || { echo \"Configuration of Licensing failed. Please review the settings. Halting Terraform.\"; exit 1; }"
  }
}

##### Create SetLicensing EndExample Ansible Playbook calling the corresponding PowerShell script in PsExec64.exe for creating the needed databases: # Terraform Deployment of Citrix CVAD on vSphere 8 - 25.11 
## For installation and configuration of the Delivery Controllers, StoreFront-Servers and WebStudio-Servers, we use Ansible Playbooks 
### Reference https://docs.ansible.com/ansible/latest/getting_started/index.html
#### We assume that the communication between the Ansible Server and all needed Hosts via WinRM is working as intended - check your Group Policies
##### All hosts and its corresponding communication-related Ansible variables are stored in assets/inventory.ini
###### Create CVAD Databases by running a PowerShell script on DDC1
- name: Create CVAD Databases
  hosts: cvad-ddc1-v
  gather_facts: no
  collections:
    - community.windows
  vars:
    domain_user: "TACG\\XxXxXxXxXxXxX"
    domain_pass: "!XxXxXxXxXxXxXxXxX!"
    psexec_path: "C:\\InstallCVAD\\PsExec64.exe"
    target_host: "tf-cvad-v-ddc1.the-austrian-citrix-guy.at"
    remote_script: "C:\\InstallCVAD\\CreateDatabases.ps1"
    ansible_user: "svc_ansible"      
    ansible_password: "!XxXxXxXxXxXxXxXxX!"
    ansible_connection: winrm
    ansible_winrm_transport: certificate

  tasks:
   - name: Run the Create Database-PowerShell script as Domain Administrator
     community.windows.win_psexec:
       command: powershell.exe -NoProfile -ExecutionPolicy Bypass -File "{{ remote_script }}"
       executable: "{{ psexec_path }}"
       username: "{{ domain_user }}"
       password: "{{ domain_pass }}"
       elevated: true
       noprofile: true
       interactive: true
       wait: trueExample PowerShell script for creating the required databases: Import-Module Citrix.XenDesktop®.Admin
asnp citrix.*
$SiteName = "TF-TACG-CVAD2507"
$DatabaseServer = "tacg-sql.the-austrian-citrix-guy.at"
$DDCName = "tf-cvad-v-ddc1.the-austrian-citrix-guy.at"
$DatabasePrefix = "DB-"

try {
        New-XDDatabase -AdminAddress $DDCName -SiteName $SiteName -AllDefaultDatabases -DatabaseServer $DatabaseServer -DatabaseNamePrefix $DatabasePrefix -Verbose

  } catch {
    $logFile = "C:\Logs\MyLogFile.log"
    $errorMessage = $_.Exception.Message
    Add-Content -Path $logFile -Value "$(Get-Date) - CreateDB: Error: $errorMessage"
    Write-Output "An error occurred trying to create the databases (error: $($Error[0]))"
    Exit 1     
} Note: The corresponding Ansible Playbooks for all the needed steps mentioned above are mostly the same – they differ only in running the feasible PowerShell script. Example PowerShell script for creating the CVAD site: Import-Module Citrix.XenDesktop.Admin
asnp citrix.*

$SiteName = "TF-TACG-CVAD2507-V"
$DatabaseServer = "tacg-sql.the-austrian-citrix-guy.at"
$DatabaseName_Site = "DB-CitrixTF-TACG-CVAD2507-VSite"
$DatabaseName_Logging = "DB-CitrixTF-TACG-CVAD2507-VLogging"
$DatabaseName_Monitoring = "DB-CitrixTF-TACG-CVAD2507-VMonitoring"
$DDCName = "tf-cvad-v-ddc1.the-austrian-citrix-guy.at"

try {
        New-XDSite -DatabaseServer $DatabaseServer -LoggingDatabaseName $DatabaseName_Logging -MonitorDatabaseName $DatabaseName_Monitoring -SiteDatabaseName $DatabaseName_Site -SiteName $SiteName -AdminAddress $DDCName -Verbose
  
  } catch {
    $logFile = "C:\Logs\MyLogFile.log"
    $errorMessage = $_.Exception.Message
    Add-Content -Path $logFile -Value "$(Get-Date) - CreateSite: Error: $errorMessage"
    Write-Output "An error occurred trying to create the CVAD site (error: $($Error[0]))"
    Exit 1  
  }
Example PowerShell script for adding the second DDC to the site: Import-Module Citrix.XenDesktop.Admin
asnp citrix.*

$DDCName = "tf-cvad-v-ddc1.the-austrian-citrix-guy.at"
$NewDDCName = "TF-CVAD-V-DDC2.the-austrian-citrix-guy.at"

try {
        Add-XDController -AdminAddress $NewDDCName -SiteControllerAddress $DDCName -Verbose
  
     } catch {
    $logFile = "C:\Logs\MyLogFile.log"
    $errorMessage = $_.Exception.Message
    Add-Content -Path $logFile -Value "$(Get-Date) - Add2ndDDC: Error: $errorMessage"
    Write-Output "An error occurred trying to add the 2nd DDC (error: $($Error[0]))"
    Exit 1  
    }Example PowerShell script for running additional site configurations: Import-Module Citrix.XenDesktop.Admin
asnp citrix.*

$GroomingDays = 90

try {  
      Set-MonitorConfiguration -GroomApplicationInstanceRetentionDays $GroomingDays -GroomDeletedRetentionDays $GroomingDays -GroomFailuresRetentionDays $GroomingDays -GroomLoadIndexesRetentionDays $GroomingDays -GroomMachineHotfixLogRetentionDays $GroomingDays -GroomNotificationLogRetentionDays $GroomingDays -GroomResourceUsageDayDataRetentionDays $GroomingDays -GroomSessionsRetentionDays $GroomingDays -GroomSummariesRetentionDays $GroomingDays 
      Set-BrokerSite -TrustRequestsSentToTheXmlServicePort $true
      Set-BrokerSite -ConnectionLeasingEnabled $false
      Set-BrokerSite -LocalHostCacheEnabled $true
      Set-AnalyticsSite -Enabled $false

    } catch {
    $logFile = "C:\Logs\MyLogFile.log"
    $errorMessage = $_.Exception.Message
    Add-Content -Path $logFile -Value "$(Get-Date) - AdditionalSiteConfiguration: Error: $errorMessage"
    Write-Output "An error occurred trying to run the additional Site configuration (error: $($Error[0]))"
    Exit 1  
    }Example PowerShell script for adding a dedicated Administrators group to the site: Import-Module Citrix.XenDesktop.Admin
asnp citrix.*

$DDCName = "tf-cvad-v-ddc1.the-austrian-citrix-guy.at"
$AdminGroup = "TACG-CTX-Admins"

try {  
      New-AdminAdministrator -AdminAddress $DDCName -Name $AdminGroup
      Add-AdminRight -AdminAddress $DDCName -Administrator $AdminGroup -Role 'Full Administrator' -Scope "All"

    } catch {
    $logFile = "C:\Logs\MyLogFile.log"
    $errorMessage = $_.Exception.Message
    Add-Content -Path $logFile -Value "$(Get-Date) - ConfigureCTXAdminGroup: Error: $errorMessage"
    Write-Output "An error occurred trying to run the CTXAdminGroup script (error: $($Error[0]))"
    Exit 1  
    }Example PowerShell script for configuring the Licensing: Import-Module Citrix.XenDesktop.Admin
asnp citrix.*

$DDCName = "tf-cvad-v-ddc1.the-austrian-citrix-guy.at"
$LicenseServer = "tacg-dc.the-austrian-citrix-guy.at"
$LicenseServerPort = 27000
$LicensingModel = "UserDevice"
$ProductCode = "XDT"
$ProductEdition = "PLT"

try {  
      Set-XDLicensing -AdminAddress $DDCName -LicenseServerAddress $LicenseServer -LicenseServerPort $LicenseServerPort -Force
      Set-ConfigSite  -AdminAddress $DDCName -LicensingModel $LicensingModel -ProductCode $ProductCode -ProductEdition $ProductEdition -UseLicenseActivationService $true
      Set-ConfigSiteMetadata -AdminAddress $DDCName -Name 'CertificateHash' -Value $(Get-LicCertificate -AdminAddress "https://$($LicenseServer):8083").CertHash

    } catch {
    $logFile = "C:\Logs\MyLogFile.log"
    $errorMessage = $_.Exception.Message
    Add-Content -Path $logFile -Value "$(Get-Date) - ConfigureLicensing: Error: $errorMessage"
    Write-Output "An error occurred trying to run the ConfigureLicensing script (error: $($Error[0]))"
    Exit 1 
	}Running the snippet should fulfill all the steps mentioned above and create the CVAD site: azadmin@tacg-cicd:/etc/terraform-tmm/CVADVS/_ConfigureSite$ terraform apply
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
  + create
 &lt;= read (data resources)

Terraform will perform the following actions:

  # data.external.CheckAnsibleReturnCodeAdditionalSiteConfiguration will be read during apply
  # (depends on a resource or a module with changes pending)
 &lt;= data "external" "CheckAnsibleReturnCodeAdditionalSiteConfiguration" {
      + id      = (known after apply)
      + program = [
          + "bash",
          + "/etc/ansible/assets/CheckAnsibleReturnCode.sh",
          + "/etc/ansible/assets/ansible_exit_code.txt",
          + "0",
        ]
      + result  = (known after apply)
    }

  # data.external.CheckAnsibleReturnCodeCDB will be read during apply
  # (depends on a resource or a module with changes pending)
 &lt;= data "external" "CheckAnsibleReturnCodeCDB" {
      + id      = (known after apply)
      + program = [
          + "bash",
          + "/etc/ansible/assets/CheckAnsibleReturnCode.sh",
          + "/etc/ansible/assets/ansible_exit_code.txt",
          + "0",
        ]
      + result  = (known after apply)
    }

  # data.external.CheckAnsibleReturnCodeCS will be read during apply
  # (depends on a resource or a module with changes pending)
 &lt;= data "external" "CheckAnsibleReturnCodeCS" {
      + id      = (known after apply)
      + program = [
          + "bash",
          + "/etc/ansible/assets/CheckAnsibleReturnCode.sh",
          + "/etc/ansible/assets/ansible_exit_code.txt",
          + "0",
        ]
      + result  = (known after apply)
    }

  # data.external.CheckAnsibleReturnCodeConfigureAdminGroup will be read during apply
  # (depends on a resource or a module with changes pending)
 &lt;= data "external" "CheckAnsibleReturnCodeConfigureAdminGroup" {
      + id      = (known after apply)
      + program = [
          + "bash",
          + "/etc/ansible/assets/CheckAnsibleReturnCode.sh",
          + "/etc/ansible/assets/ansible_exit_code.txt",
          + "0",
        ]
      + result  = (known after apply)
    }

  # data.external.CheckAnsibleReturnCodeSecondDDC will be read during apply
  # (depends on a resource or a module with changes pending)
 &lt;= data "external" "CheckAnsibleReturnCodeSecondDDC" {
      + id      = (known after apply)
      + program = [
          + "bash",
          + "/etc/ansible/assets/CheckAnsibleReturnCode.sh",
          + "/etc/ansible/assets/ansible_exit_code.txt",
          + "0",
        ]
      + result  = (known after apply)
    }

  # data.external.CheckAnsibleReturnCodeSetLicensing will be read during apply
  # (depends on a resource or a module with changes pending)
 &lt;= data "external" "CheckAnsibleReturnCodeSetLicensing" {
      + id      = (known after apply)
      + program = [
          + "bash",
          + "/etc/ansible/assets/CheckAnsibleReturnCode.sh",
          + "/etc/ansible/assets/ansible_exit_code.txt",
          + "0",
        ]
      + result  = (known after apply)
    }

  # null_resource.CopyAnsibleSiteAssetsToAnsibleServer will be created
  + resource "null_resource" "CopyAnsibleSiteAssetsToAnsibleServer" {
      + id = (known after apply)
    }

  # null_resource.CopyPowerShellScriptsToDDC1 will be created
  + resource "null_resource" "CopyPowerShellScriptsToDDC1" {
      + id = (known after apply)
    }

  # null_resource.IfExitCodeIsNotZeroThenHaltAdditionalSiteConfiguration will be created
  + resource "null_resource" "IfExitCodeIsNotZeroThenHaltAdditionalSiteConfiguration" {
      + id = (known after apply)
    }

  # null_resource.IfExitCodeIsNotZeroThenHaltCDB will be created
  + resource "null_resource" "IfExitCodeIsNotZeroThenHaltCDB" {
      + id = (known after apply)
    }

  # null_resource.IfExitCodeIsNotZeroThenHaltCS will be created
  + resource "null_resource" "IfExitCodeIsNotZeroThenHaltCS" {
      + id = (known after apply)
    }

  # null_resource.IfExitCodeIsNotZeroThenHaltConfigureAdminGroup will be created
  + resource "null_resource" "IfExitCodeIsNotZeroThenHaltConfigureAdminGroup" {
      + id = (known after apply)
    }

  # null_resource.IfExitCodeIsNotZeroThenHaltSecondDDC will be created
  + resource "null_resource" "IfExitCodeIsNotZeroThenHaltSecondDDC" {
      + id = (known after apply)
    }

  # null_resource.IfExitCodeIsNotZeroThenHaltSetLicensing will be created
  + resource "null_resource" "IfExitCodeIsNotZeroThenHaltSetLicensing" {
      + id = (known after apply)
    }

  # null_resource.RunAddSecondDDCPlaybook will be created
  + resource "null_resource" "RunAddSecondDDCPlaybook" {
      + id = (known after apply)
    }

  # null_resource.RunAdditionalSiteConfigurationPlaybook will be created
  + resource "null_resource" "RunAdditionalSiteConfigurationPlaybook" {
      + id = (known after apply)
    }

  # null_resource.RunConfigureAdminGroupPlaybook will be created
  + resource "null_resource" "RunConfigureAdminGroupPlaybook" {
      + id = (known after apply)
    }

  # null_resource.RunConfigureSetLicensingPlaybook will be created
  + resource "null_resource" "RunConfigureSetLicensingPlaybook" {
      + id = (known after apply)
    }

  # null_resource.RunCreateDatabasePlaybook will be created
  + resource "null_resource" "RunCreateDatabasePlaybook" {
      + id = (known after apply)
    }

  # null_resource.RunCreateSitePlaybook will be created
  + resource "null_resource" "RunCreateSitePlaybook" {
      + id = (known after apply)
    }

  # time_sleep.wait_10_seconds will be created
  + resource "time_sleep" "wait_10_seconds" {
      + create_duration = "10s"
      + id              = (known after apply)
    }

  # time_sleep.wait_10_secondsAdditionalSiteConfiguration will be created
  + resource "time_sleep" "wait_10_secondsAdditionalSiteConfiguration" {
      + create_duration = "10s"
      + id              = (known after apply)
    }

  # time_sleep.wait_10_secondsConfigureAdminGroup will be created
  + resource "time_sleep" "wait_10_secondsConfigureAdminGroup" {
      + create_duration = "10s"
      + id              = (known after apply)
    }

  # time_sleep.wait_10_secondsSecondDDC will be created
  + resource "time_sleep" "wait_10_secondsSecondDDC" {
      + create_duration = "10s"
      + id              = (known after apply)
    }

  # time_sleep.wait_300_secondsCDB will be created
  + resource "time_sleep" "wait_300_secondsCDB" {
      + create_duration = "300s"
      + id              = (known after apply)
    }

  # time_sleep.wait_60_secondsCS will be created
  + resource "time_sleep" "wait_60_secondsCS" {
      + create_duration = "60s"
      + id              = (known after apply)
    }

Plan: 20 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.CopyPowerShellScriptsToDDC1: Creating...
null_resource.CopyAnsibleSiteAssetsToAnsibleServer: Creating...
null_resource.CopyAnsibleSiteAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPowerShellScriptsToDDC1: Provisioning with 'file'...
null_resource.CopyAnsibleSiteAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyAnsibleSiteAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyAnsibleSiteAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyAnsibleSiteAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPowerShellScriptsToDDC1: Provisioning with 'file'...
null_resource.CopyAnsibleSiteAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyAnsibleSiteAssetsToAnsibleServer: Creation complete after 1s [id=3364084582988389224]
time_sleep.wait_10_seconds: Creating...
#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;
null_resource.CopyPowerShellScriptsToDDC1: Provisioning with 'file'...
#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;
...
null_resource.CopyPowerShellScriptsToDDC1: Creation complete after 10s [id=6861429796874371568]
time_sleep.wait_10_seconds: Still creating... [00m10s elapsed]
time_sleep.wait_10_seconds: Creation complete after 10s [id=2025-11-18T16:58:47Z]
null_resource.RunCreateDatabasePlaybook: Creating...
null_resource.RunCreateDatabasePlaybook: Provisioning with 'local-exec'...
null_resource.RunCreateDatabasePlaybook (local-exec): Executing: ["/bin/sh" "-c" "ansible-playbook /etc/ansible/assets/CreateDatabases.ansible.yml -i /etc/ansible/assets/inventory.ini -vvv -c winrm; echo $? &gt; /etc/ansible/assets/ansible_exit_code.txt"]

null_resource.RunCreateDatabasePlaybook (local-exec): PLAYBOOK: CreateDatabases.ansible.yml ******************************************
null_resource.RunCreateDatabasePlaybook (local-exec): 1 plays in /etc/ansible/assets/CreateDatabases.ansible.yml

null_resource.RunCreateDatabasePlaybook (local-exec): PLAY [Run PowerShell script on Windows Server as Domain Admin] *****************

null_resource.RunCreateDatabasePlaybook (local-exec): TASK [Run the StoreFront installer PowerShell script as domain user] ***********
null_resource.RunCreateDatabasePlaybook (local-exec): task path: /etc/ansible/assets/CreateDatabases.ansible.yml:20
null_resource.RunCreateDatabasePlaybook (local-exec): Using module file /usr/lib/python3/dist-packages/ansible_collections/community/windows/plugins/modules/win_psexec.ps1
null_resource.RunCreateDatabasePlaybook (local-exec): Pipelining is enabled.
null_resource.RunCreateDatabasePlaybook (local-exec): &lt;tf-cvad-v-ddc1.the-austrian-citrix-guy.at&gt; ESTABLISH WINRM CONNECTION FOR USER: svc_ansible on PORT 5986 TO tf-cvad-v-ddc1.the-austrian-citrix-guy.at
null_resource.RunCreateDatabasePlaybook (local-exec): EXEC (via pipeline wrapper)
null_resource.RunCreateDatabasePlaybook (local-exec): changed: [tf-cvad-v-ddc1.the-austrian-citrix-guy.at] =&gt; {
null_resource.RunCreateDatabasePlaybook (local-exec):     "changed": true,
null_resource.RunCreateDatabasePlaybook (local-exec):     "delta": "0:00:32.533635",
null_resource.RunCreateDatabasePlaybook (local-exec):     "end": "2025-11-12 04:59:20.201075",
null_resource.RunCreateDatabasePlaybook (local-exec):     "invocation": {
null_resource.RunCreateDatabasePlaybook (local-exec):         "module_args": {
null_resource.RunCreateDatabasePlaybook (local-exec):             "chdir": null,
null_resource.RunCreateDatabasePlaybook (local-exec):             "command": "powershell.exe -NoProfile -ExecutionPolicy Bypass -File \"C:\\InstallCVAD\\CreateDatabases.ps1\"",
null_resource.RunCreateDatabasePlaybook (local-exec):             "elevated": true,
null_resource.RunCreateDatabasePlaybook (local-exec):             "executable": "C:\\InstallCVAD\\PsExec64.exe",
null_resource.RunCreateDatabasePlaybook (local-exec):             "hostnames": null,
null_resource.RunCreateDatabasePlaybook (local-exec):             "interactive": true,
null_resource.RunCreateDatabasePlaybook (local-exec):             "limited": false,
null_resource.RunCreateDatabasePlaybook (local-exec):             "nobanner": false,
null_resource.RunCreateDatabasePlaybook (local-exec):             "noprofile": true,
null_resource.RunCreateDatabasePlaybook (local-exec):             "password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
null_resource.RunCreateDatabasePlaybook (local-exec):             "priority": null,
null_resource.RunCreateDatabasePlaybook (local-exec):             "session": null,
null_resource.RunCreateDatabasePlaybook (local-exec):             "system": false,
null_resource.RunCreateDatabasePlaybook (local-exec):             "timeout": null,
null_resource.RunCreateDatabasePlaybook (local-exec):             "username": "TACG\\administrator",
null_resource.RunCreateDatabasePlaybook (local-exec):             "wait": true
null_resource.RunCreateDatabasePlaybook (local-exec):         }
null_resource.RunCreateDatabasePlaybook (local-exec):     },
null_resource.RunCreateDatabasePlaybook (local-exec):     "psexec_command": "C:\\InstallCVAD\\PsExec64.exe -u TACG\\administrator -p *PASSWORD_REPLACED* -e -h -i -accepteula powershell.exe -NoProfile -ExecutionPolicy Bypass -File \"C:\\InstallCVAD\\CreateDatabases.ps1\"",
null_resource.RunCreateDatabasePlaybook (local-exec):     "rc": 0,
null_resource.RunCreateDatabasePlaybook (local-exec):     "start": "2025-11-12 04:58:47.667440",
null_resource.RunCreateDatabasePlaybook (local-exec):     "stderr": "Connecting to local system...\r\r\rStarting PSEXESVC service on local system...\r\r\rCopying authentication key to TF-CVAD-V-DDC1...\r\r\rConnecting with PsExec service on TF-CVAD-V-DDC1...\r\r\rStarting powershell.exe on TF-CVAD-V-DDC1...\r\r\r\r\npowershell.exe exited on TF-CVAD-V-DDC1 with error code 0.\r\n",
null_resource.RunCreateDatabasePlaybook (local-exec):     "stderr_lines": [
null_resource.RunCreateDatabasePlaybook (local-exec):         "Connecting to local system...",
null_resource.RunCreateDatabasePlaybook (local-exec):         "",
null_resource.RunCreateDatabasePlaybook (local-exec):         "",
null_resource.RunCreateDatabasePlaybook (local-exec):         "Starting PSEXESVC service on local system...",
null_resource.RunCreateDatabasePlaybook (local-exec):         "",
null_resource.RunCreateDatabasePlaybook (local-exec):         "",
null_resource.RunCreateDatabasePlaybook (local-exec):         "Copying authentication key to TF-CVAD-V-DDC1...",
null_resource.RunCreateDatabasePlaybook (local-exec):         "",
null_resource.RunCreateDatabasePlaybook (local-exec):         "",
null_resource.RunCreateDatabasePlaybook (local-exec):         "Connecting with PsExec service on TF-CVAD-V-DDC1...",
null_resource.RunCreateDatabasePlaybook (local-exec):         "",
null_resource.RunCreateDatabasePlaybook (local-exec):         "",
null_resource.RunCreateDatabasePlaybook (local-exec):         "Starting powershell.exe on TF-CVAD-V-DDC1...",
null_resource.RunCreateDatabasePlaybook (local-exec):         "",
null_resource.RunCreateDatabasePlaybook (local-exec):         "",
null_resource.RunCreateDatabasePlaybook (local-exec):         "",
null_resource.RunCreateDatabasePlaybook (local-exec):         "powershell.exe exited on TF-CVAD-V-DDC1 with error code 0."
null_resource.RunCreateDatabasePlaybook (local-exec):     ],
null_resource.RunCreateDatabasePlaybook (local-exec):     "stdout": "\r\nPsExec v2.43 - Execute processes remotely\r\nCopyright (C) 2001-2023 Mark Russinovich\r\nSysinternals - www.sysinternals.com\r\n\r\n",
null_resource.RunCreateDatabasePlaybook (local-exec):     "stdout_lines": [
null_resource.RunCreateDatabasePlaybook (local-exec):         "",
null_resource.RunCreateDatabasePlaybook (local-exec):         "PsExec v2.43 - Execute processes remotely",
null_resource.RunCreateDatabasePlaybook (local-exec):         "Copyright (C) 2001-2023 Mark Russinovich",
null_resource.RunCreateDatabasePlaybook (local-exec):         "Sysinternals - www.sysinternals.com",
null_resource.RunCreateDatabasePlaybook (local-exec):         ""
null_resource.RunCreateDatabasePlaybook (local-exec):     ]
null_resource.RunCreateDatabasePlaybook (local-exec): }

null_resource.RunCreateDatabasePlaybook (local-exec): PLAY RECAP *********************************************************************
null_resource.RunCreateDatabasePlaybook (local-exec): tf-cvad-v-ddc1.the-austrian-citrix-guy.at : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

null_resource.RunCreateDatabasePlaybook: Creation complete after 35s [id=5576848404034541586]
data.external.CheckAnsibleReturnCodeCDB: Reading...
data.external.CheckAnsibleReturnCodeCDB: Read complete after 0s [id=-]
null_resource.IfExitCodeIsNotZeroThenHaltCDB: Creating...
null_resource.IfExitCodeIsNotZeroThenHaltCDB: Provisioning with 'local-exec'...
null_resource.IfExitCodeIsNotZeroThenHaltCDB (local-exec): Executing: ["/bin/bash" "-c" "[ \"1\" = \"1\" ] &amp;&amp; echo \"Create Databases - Everything OK\" || { echo \"Deploying the CVAD Databases failed. Please review the settings. Halting Terraform.\"; exit 1; }"]
null_resource.IfExitCodeIsNotZeroThenHaltCDB (local-exec): Create Databases - Everything OK
null_resource.IfExitCodeIsNotZeroThenHaltCDB: Creation complete after 0s [id=449387280135483830]
...
Running all Playbooks looks similar, omitted due to the length of the output.
...
null_resource.RunConfigureSetLicensingPlaybook (local-exec): PLAY RECAP *********************************************************************
null_resource.RunConfigureSetLicensingPlaybook (local-exec): tf-cvad-v-ddc1.the-austrian-citrix-guy.at : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

null_resource.RunConfigureSetLicensingPlaybook: Creation complete after 13s [id=5106544801416722566]
data.external.CheckAnsibleReturnCodeSetLicensing: Reading...
data.external.CheckAnsibleReturnCodeSetLicensing: Read complete after 0s [id=-]
null_resource.IfExitCodeIsNotZeroThenHaltSetLicensing: Creating...
null_resource.IfExitCodeIsNotZeroThenHaltSetLicensing: Provisioning with 'local-exec'...
null_resource.IfExitCodeIsNotZeroThenHaltSetLicensing (local-exec): Executing: ["/bin/bash" "-c" "[ \"1\" = \"1\" ] &amp;&amp; echo \"Licensing Configuration - Everything OK\" || { echo \"Configuration of Licensing failed. Please review the settings. Halting Terraform.\"; exit 1; }"]
null_resource.IfExitCodeIsNotZeroThenHaltSetLicensing (local-exec): Licensing Configuration - Everything OK
null_resource.IfExitCodeIsNotZeroThenHaltSetLicensing: Creation complete after 0s [id=1454545395290283154]

Apply complete! Resources: 26 added, 0 changed, 0 destroyed.
azadmin@tacg-cicd:/etc/terraform-tmm/CVADVS/_ConfigureSite$The Terraform snippet has successfully created the site and all needed configurations: The CVAD databases for the vSphere-based deployment were created:  The CVAD site was successfully created:  All site tests are successful:  With the CVAD site ready, we can proceed to the next step.  Part 9: Using Terraform to create the StoreFront deployment and all its necessitiesCreating a StoreFront deployment is a simple, straightforward task. Terraform can create the Storefront site on its own – no Ansible interaction is needed. Caution: As already mentioned, this module, Module 9, must be run on a Windows-based, domain-joined VM due to limitations in StoreFront´s automation capabilities. Terraform will sequentially run the following tasks: Create the StoreFront deployment Create a StoreFront Authentication service Create a StoreFront Store service Create a StoreFront WebReceiver service  Warning: The current Automation snippets of StoreFront will only run on Windows Powershell &lt;=5.1. They will not run on Powershell &gt;=6. Be sure to start the Terraform code on the correct PowerShell version. You may encounter this error after starting the Terraform snippet: Error: Error fetching StoreFront version
│
│   with citrix_stf_webreceiver_service.CreateSFWebReceiverService,
│   on CVADOnXS-ConfigureStoreFront.tf line 48, in resource "citrix_stf_webreceiver_service" "CreateSFWebReceiverService":
│   48: resource "citrix_stf_webreceiver_service" "CreateSFWebReceiverService" {
│
│ Error message: error executing command: Get-STFVersion
│  Error Message:
│  ConvertTo-SecureString : "ConvertTo-SecureString" was found in module "Microsoft.PowerShell.Security",
│ we were unable to load the module. Run "Import-Module
│ Microsoft.PowerShell.Security".
│ In row:1 column:205
│ + ... n@the-austrian-citrix-guy.at',(ConvertTo-SecureString -Force  ...
│ +                                            ~~~~~~~~~~~~~~~~~~~~~~
│     + CategoryInfo          : ObjectNotFound: (ConvertTo-SecureString:String) [], CommandNotFoundException
│     + FullyQualifiedErrorId : CouldNotAutoloadMatchingModuleThat error means you have not loaded the module Microsoft.PowerShell.Security in Windows PowerShell. Be sure to load the module in Windows PowerShell, not PowerShell: PS C:\__PPMM\__TF\_CVADOnVSP-25.11\_ConfigureStoreFront&gt; Import-Module Microsoft.PowerShell.Security After loading the module in Windows PowerShell, the error should be gone and the Terraform snippet run through. Before running the snippet, the StoreFront server has no StoreFront site configured:  Example Terraform snippet to create a StoreFront site: # Terraform Deployment of Citrix CVAD on vSphere 8 - 25.11 
## Definition of vSphere Provider Variables - not all available variables are defined, only these we use
### Reference https://registry.terraform.io/providers/vmware/vsphere/latest/docs
#### Configuring StoreFront 
##### Reference https://github.com/citrix/terraform-provider-citrix/blob/main/StoreFront.md
###### Create an initial StoreFront Deployment
resource "citrix_stf_deployment" "CreateInitialSFDeployment" {
  site_id       = var.TACG-TMM-SF-SiteID
  host_base_url = var.TACG-TMM-SF-SiteURL
  roaming_gateway = [
    {
      name                         = var.TACG-TMM-SF-Gateway-Name
      logon_type                   = var.TACG-TMM-SF-Gateway-LogonType
      gateway_url                  = var.TACG-TMM-SF-Gateway-URL
      callback_url                 = var.TACG-TMM-SF-Gateway-CallbackURL
      subnet_ip_address            = var.TACG-TMM-SF-Gateway-SNIP
      secure_ticket_authority_urls = var.TACG-TMM-SF-Gateway-STAs
    }
  ]
  roaming_beacon = {
    internal_address   = var.TACG-TMM-SF-Beacon-Internal
    external_addresses = var.TACG-TMM-SF-Beacon-External
  }
}

##### Create an Authentication service
resource "citrix_stf_authentication_service" "CreateSFAuthenticationService" {
  depends_on    = [citrix_stf_deployment.CreateInitialSFDeployment]
  site_id       = citrix_stf_deployment.CreateInitialSFDeployment.site_id
  friendly_name = var.TACG-TMM-SF-AuthService-Name
  virtual_path  = var.TACG-TMM-SF-AuthService-Path
}

##### Create a Store service
resource "citrix_stf_store_service" "CreateSFStoreService" {
  depends_on                          = [citrix_stf_authentication_service.CreateSFAuthenticationService]
  site_id                             = citrix_stf_deployment.CreateInitialSFDeployment.site_id
  virtual_path                        = var.TACG-TMM-SF-StoreService-Path
  friendly_name                       = var.TACG-TMM-SF-StoreService-Name
  authentication_service_virtual_path = citrix_stf_authentication_service.CreateSFAuthenticationService.virtual_path
  pna = {
    enable = false
  }
  farms = var.TACG-TMM-SF-StoreService-Farms
}

##### Create a WebReceiver service
resource "citrix_stf_webreceiver_service" "CreateSFWebReceiverService" {
  depends_on         = [citrix_stf_deployment.CreateInitialSFDeployment, citrix_stf_store_service.CreateSFStoreService]
  site_id            = citrix_stf_deployment.CreateInitialSFDeployment.site_id
  friendly_name      = var.TACG-TMM-SF-WebService-Name
  virtual_path       = var.TACG-TMM-SF-WebService-Path
  store_virtual_path = citrix_stf_store_service.CreateSFStoreService.virtual_path
  authentication_methods = [
    "ExplicitForms",
    "CitrixAGBasic"
  ]
  plugin_assistant = {
    enabled                 = var.TACG-TMM-SF-WebService-PA-Enabled
    html5_single_tab_launch = var.TACG-TMM-SF-WebService-PA-HTML5SingleTab
    upgrade_at_login        = var.TACG-TMM-SF-WebService-PA-UpgradeAtLogin
    html5_enabled           = var.TACG-TMM-SF-WebService-PA-HTML5Enabled
  }
  application_shortcuts = {
    prompt_for_untrusted_shortcuts = var.TACG-TMM-SF-WebService-AppSC-Prompt
    trusted_urls                   = var.TACG-TMM-SF-WebService-AppSC-TrustedURLs
  }
  user_interface = {
    auto_launch_desktop     = var.TACG-TMM-SF-WebService-UI-AutoLaunchDesktop
    multi_click_timeout     = var.TACG-TMM-SF-WebService-UI-Timeout
    enable_apps_folder_view = var.TACG-TMM-SF-WebService-UI-AppFolderView
    workspace_control = {
      enabled                 = var.TACG-TMM-SF-WebService-UI-WC-Enabled
      auto_reconnect_at_logon = var.TACG-TMM-SF-WebService-UI-WC-AutoReconnect
      logoff_action           = var.TACG-TMM-SF-WebService-UI-LogoffAction
      show_reconnect_button   = var.TACG-TMM-SF-WebService-UI-WC-ShowReconnect
      show_disconnect_button  = var.TACG-TMM-SF-WebService-UI-WC-ShowDisconnect
    }
    receiver_configuration = {
      enabled = var.TACG-TMM-SF-WebService-RC-Enabled
    }
    app_shortcuts = {
      enabled               = var.TACG-TMM-SF-WebService-ASC-Enabled
      show_desktop_shortcut = var.TACG-TMM-SF-WebService-ASC-ShowDesktopShortcut
    }
    ui_views = {
      show_apps_view     = var.TACG-TMM-SF-WebService-UIViews-Apps
      show_desktops_view = var.TACG-TMM-SF-WebService-UIViews-Desktops
      default_view       = var.TACG-TMM-SF-WebService-UIViews-Default
    }
    category_view_collapsed   = var.TACG-TMM-SF-WebService-CategoryView
    move_app_to_uncategorized = var.TACG-TMM-SF-WebService-Uncategorized
    progressive_web_app = {
      enabled             = var.TACG-TMM-SF-WebService-PWA-Enabled
      show_install_prompt = var.TACG-TMM-SF-WebService-PWA-Install
    }
    show_activity_manager = var.TACG-TMM-SF-WebService-ActivityManager
    show_first_time_use   = var.TACG-TMM-SF-WebService-FirstTimeUse
    prevent_ica_downloads = var.TACG-TMM-SF-WebService-PreventICADownloads
  }
  resources_service = {
    ica_file_cache_expiry         = var.TACG-TMM-SF-WebService-RS-IcaFileExpiry
    persistent_icon_cache_enabled = var.TACG-TMM-SF-WebService-RS-IconCacheEnabled
  }
}

##### A PNA site is not installed due to no effective needs
#resource "citrix_stf_xenapp_default_store" "CreateSFXenAppPath" {
#  depends_on         = [citrix_stf_store_service.CreateSFStoreService]
#  store_virtual_path = citrix_stf_store_service.CreateSFStoreService.virtual_path
#  store_site_id      = citrix_stf_store_service.CreateSFStoreService.site_id
#}Running the snippet should fulfill all needed steps – during the run, the StoreFront server installs missing roles and features:  PS C:\__PPMM\__TF\_CVADOnVSP-25.11\_ConfigureStoreFront&gt; terraform apply

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_stf_authentication_service.CreateSFAuthenticationService will be created
  + resource "citrix_stf_authentication_service" "CreateSFAuthenticationService" {
      + claims_factory_name = "standardClaimsFactory"
      + friendly_name       = "TACG-SF-AuthService"
      + site_id             = "1"
      + virtual_path        = "/Citrix/Authentication"
    }

  # citrix_stf_deployment.CreateInitialSFDeployment will be created
  + resource "citrix_stf_deployment" "CreateInitialSFDeployment" {
      + host_base_url   = "https://storefront-v.the-austrian-citrix-guy.at"
      + roaming_beacon  = {
          + external_addresses = [
              + "https://www.orf.at/",
              + "https://www.microsoft.com/",
            ]
          + internal_address   = "http://10.10.100.1/"
        }
      + roaming_gateway = [
          + {
              + callback_url                   = "https://access.the-austrian-citrix-guy.at/CitrixAuthService/AuthService.asmx"
              + gateway_url                    = "https://access.the-austrian-citrix-guy.at/"
              + is_cloud_gateway               = false
              + logon_type                     = "Domain"
              + name                           = "TACG-NS"
              + request_ticket_from_two_stas   = false
              + secure_ticket_authority_urls   = [
                  + {
                      + sta_url                = "http://tf-cvad-v-ddc1.the-austrian-citrix-guy.at/scripts/ctxsta.dll"
                      + sta_validation_enabled = false
                    },
                  + {
                      + sta_url                = "http://tf-cvad-v-ddc2.the-austrian-citrix-guy.at/scripts/ctxsta.dll"
                      + sta_validation_enabled = false
                    },
                ]
              + session_reliability            = false
              + smart_card_fallback_logon_type = "None"
              + stas_bypass_duration           = "0.1:0:0"
              + stas_use_load_balancing        = false
              + subnet_ip_address              = "10.10.119.112"
              + version                        = "Version10_0_69_4"
            },
        ]
      + site_id         = "1"
    }

  # citrix_stf_store_service.CreateSFStoreService will be created
  + resource "citrix_stf_store_service" "CreateSFStoreService" {
      + authentication_service_virtual_path = "/Citrix/Authentication"
      + farms                               = [
          + {
              + all_failed_bypass_duration     = 0
              + bypass_duration                = 60
              + farm_name                      = "TACG443"
              + farm_type                      = "XenDesktop"
              + load_balance                   = true
              + max_failed_servers_per_request = 0
              + port                           = 443
              + rade_ticket_time_to_live       = 100
              + server_urls                    = []
              + servers                        = [
                  + "tf-cvad-v-ddc1.the-austrian-citrix-guy.at",
                  + "tf-cvad-v-ddc2.the-austrian-citrix-guy.at",
                ]
              + ssl_relay_port                 = 443
              + ticket_time_to_live            = 200
              + transport_type                 = "HTTPS"
              + xml_validation_enabled         = false
              + zones                          = []
                # (4 unchanged attributes hidden)
            },
        ]
      + friendly_name                       = "TACG-SF-StoreService"
      + pna                                 = {
          + enable = false
        }
      + site_id                             = "1"
      + virtual_path                        = "/Citrix/Store"
    }

  # citrix_stf_webreceiver_service.CreateSFWebReceiverService will be created
  + resource "citrix_stf_webreceiver_service" "CreateSFWebReceiverService" {
      + application_shortcuts  = {
          + gateway_urls                   = []
          + prompt_for_untrusted_shortcuts = true
          + trusted_urls                   = [
              + "https://storefront-v.the-austrian-citrix-guy.at/",
            ]
        }
      + authentication_methods = [
          + "CitrixAGBasic",
          + "ExplicitForms",
        ]
      + friendly_name          = "TACG-SF-WebReceiverService"
      + plugin_assistant       = {
          + enabled                 = true
          + html5_enabled           = "Fallback"
          + html5_single_tab_launch = true
          + show_after_login        = false
          + upgrade_at_login        = true
        }
      + resources_service      = {
          + ica_file_cache_expiry         = 80
          + icon_size                     = 128
          + persistent_icon_cache_enabled = true
          + show_desktop_viewer           = true
        }

      + site_id                = "1"
      + store_virtual_path     = "/Citrix/Store"
      + user_interface         = {
          + app_shortcuts             = {
              + allow_session_reconnect = false
            }
          + auto_launch_desktop       = true
          + category_view_collapsed   = false
          + enable_apps_folder_view   = true
          + move_app_to_uncategorized = true
          + multi_click_timeout       = 3
          + prevent_ica_downloads     = false
          + progressive_web_app       = {
              + enabled             = false
              + show_install_prompt = false
            }
          + receiver_configuration    = {
              + download_url = (known after apply)
              + enabled      = true
            }
          + show_activity_manager     = true
          + show_first_time_use       = true
          + ui_views                  = {
              + default_view       = "Auto"
              + show_apps_view     = true
              + show_desktops_view = true
            }
          + workspace_control         = {
              + auto_reconnect_at_logon = true
              + enabled                 = true
              + logoff_action           = "Disconnect"
              + show_disconnect_button  = false
              + show_reconnect_button   = false
            }
        }
      + virtual_path           = "/Citrix/StoreWeb"
    }

Plan: 4 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_stf_deployment.CreateInitialSFDeployment: Creating...
citrix_stf_deployment.CreateInitialSFDeployment: Still creating... [00m10s elapsed]
citrix_stf_deployment.CreateInitialSFDeployment: Still creating... [00m20s elapsed]
citrix_stf_deployment.CreateInitialSFDeployment: Creation complete after 25s
citrix_stf_authentication_service.CreateSFAuthenticationService: Creating...
citrix_stf_authentication_service.CreateSFAuthenticationService: Creation complete after 6s
citrix_stf_store_service.CreateSFStoreService: Creating...
citrix_stf_store_service.CreateSFStoreService: Still creating... [00m10s elapsed]
citrix_stf_store_service.CreateSFStoreService: Creation complete after 16s
citrix_stf_webreceiver_service.CreateSFWebReceiverService: Creating...
citrix_stf_webreceiver_service.CreateSFWebReceiverService: Still creating... [00m10s elapsed]
citrix_stf_webreceiver_service.CreateSFWebReceiverService: Creation complete after 15s

Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
PS C:\__PPMM\__TF\_CVADOnVSP-25.11\_ConfigureStoreFront&gt;The StoreFront server now has a valid StoreFront site deployed:  The StoreFront server now accepts logons:  With the StoreFront site ready, we can proceed to the final step: deploying all CVAD-related entities. Important: You can now switch back to the Ubuntu IaC machine or stay on the Windows machine to run the last Terraform snippet.  Part 10: Using Terraform to create all CVAD entities and all their necessitiesCreating the final steps is a simple, straightforward task. Terraform can create the CVAD entities site on its own – no Ansible interaction is needed. Create a StoreFront Server object Create a Hypervisor connection Create a Hypervisor connection resource pool Create a Machine Catalog Create a Delivery Group Create a Policy Set and its policies Before running the snippet, no CVAD-related entity is configured: No Hypervisor Connection and Hypervisor Connection pool exist:  No Machine Catalog exists:  No Delivery Group exists:  No dedicated Policy Set exists:  Example Terraform snippet to create all needed CVAD entities as mentioned above: # Terraform Deployment of Citrix CVAD on vSphere 8 - 25.11 
## Definition of CVAD Provider Variables - not all available variables are defined, only these we use
### Reference https://registry.terraform.io/providers/citrix/citrix/latest/docs
#### Definition of all required local variables or data
##### Get vSphere-related Data
data "vsphere_datacenter" "datacenter" {
  name          = var.vsphere_datacenter
}

data "vsphere_datastore" "datastore" {
  name          = var.vsphere_datastore
  datacenter_id = data.vsphere_datacenter.datacenter.id
}

data "vsphere_network" "network" {
  name          = var.vsphere_network
  datacenter_id = data.vsphere_datacenter.datacenter.id
}

data "vsphere_host" "host" {
  name          = var.vsphere_host
  datacenter_id = data.vsphere_datacenter.datacenter.id
}

### If a cluster is available, uncomment this block
data "vsphere_compute_cluster" "cluster" {
name            = "TACG"
datacenter_id   = data.vsphere_datacenter.datacenter.id
}

##### Get CVAD-related Data
###### Get the Zone in CVAD
data "citrix_zone" "TACG-TMM-CVAD-Zone" {
  name = var.TACG-TMM-CVAD-Zone-Name
}

#### Create a StoreFront Server object - we assume a load-balanced server
resource "citrix_storefront_server" "CreateSFServer" {
  name        = var.TACG-TMM-SF-Server-Name
  description = var.TACG-TMM-SF-Server-Description
  url         = var.TACG-TMM-SF-Server-URL
  enabled     = true
}

#### Create a Hypervisor Connection
resource "citrix_vsphere_hypervisor
" "TACG-TMM-CVAD-V-HypConn" {
  depends_on      = [data.citrix_zone.TACG-TMM-CVAD-Zone]
  name            = var.TACG-TMM-CVAD-HypConn-Name
  zone            = data.citrix_zone.TACG-TMM-CVAD-Zone.id
  username        = var.VSP-CL-UN
  password        = var.VSP-CL-PW
  password_format = "PlainText"
  addresses       = var.VSP-CL-IPs
}

resource "null_resource" "WriteProgress1" {
  depends_on = [citrix_xenserver_hypervisor.TACG-TMM-CVAD-HypConn]
  provisioner "local-exec" {
    command = "echo The Hypervisor Connection was successfully created..."
  }
}

#### Create a Hypervisor Connection Resource Pool
resource "citrix_vsphere_hypervisor_resource_pool" "TACG-TMM-CVAD-V-HypConnPool" {
  depends_on = [citrix_vsphere-hypervisor.TACG-TMM-CVAD-V-HypConn]
  name       = var.TACG-TMM-CVAD-HypConnPool-Name
  hypervisor = citrix_vsphere_hypervisor.TACG-TMM-CVAD-V-HypConn.id
  networks   = [data.vsphere_network.network.data_items[0].name_label]
  cluster                     = {
                  datacenter    = data.vsphere_datacenter.datacenter.name
                  cluster_name  = data.vsphere_compute_cluster.cluster.name
    }
    networks                    = [ data.vsphere_network.network.name, ]
    storage                     = [
                                    {
                                    storage_name = data.vsphere_datastore.datastore.name
                                    }
                                  ]
    temporary_storage           = [ 
                                    {
                                    storage_name = data.vsphere_datastore.datastore.name
                                    }
                                  ]    
    use_local_storage_caching = false
}

resource "null_resource" "WriteProgress2" {
  depends_on = [citrix_vsphere_hypervisor_resource_pool.TACG-TMM-CVAD-V-HypConnPool]
  provisioner "local-exec" {
    command = "echo The Hypervisor Connection Pool was successfully created..."
  }
}

#### Create the Machine Catalog
##### Important: You need to use standard Master Image VMs/Snapshots for creation of a Machine Catalog!
resource "citrix_machine_catalog" "TACG-TMM-CVAD-V-MC" {
  depends_on        = [citrix_vsphere_hypervisor_resource_pool.TACG-TMM-CVAD-V-HypConnPool]
  name              = var.TACG-TMM-CVAD-MC-Name
  description       = var.TACG-TMM-CVAD-MC-Description
  provisioning_type = var.TACG-TMM-CVAD-MC-ProvisioningType
  allocation_type   = var.TACG-TMM-CVAD-MC-AllocationType
  session_support   = var.TACG-TMM-CVAD-MC-SessionType
  zone              = data.citrix_zone.TACG-TMM-CVAD-Zone.id
  provisioning_scheme = {
    hypervisor               = citrix_vsphere_hypervisor.TACG-TMM-CVAD-V-HypConn.id
    hypervisor_resource_pool = citrix_vsphere_hypervisor_resource_pool.TACG-TMM-CVAD-V-HypConnPool.id
    identity_type            = var.TACG-TMM-CVAD-MC-IdentityType
    machine_domain_identity = {
      domain                   = var.TACG-TMM-CVAD-MC-VM-DomainFQDN
      domain_ou                = var.TACG-TMM-CVAD-MC-VM-DomainOU
      service_account          = var.TACG-TMM-CVAD-MC-VM-ServiceUserName
      service_account_password = var.TACG-TMM-CVAD-MC-VM-ServiceUserPassword
    }
    vsphere_machine_config            =  {
           master_image_vm                  = "${var.CVAD_MC-MasterImageName}"
           cpu_count                        = "${var.CVAD_MC-CpuCount}"
           memory_mb                        = "${var.CVAD_MC-MemorySize}"
           image_snapshot                   = "${var.CVAD_MC-MasterImageSnapshot}"
                                         }
    number_of_total_machines = var.TACG-TMM-CVAD-MC-VM-NumberOfVMs
    machine_account_creation_rules = {
      naming_scheme      = var.TACG-TMM-CVAD-MC-VM-NamingScheme
      naming_scheme_type = var.TACG-TMM-CVAD-MC-VM-NamingSchemeType
    }
  }
}

resource "null_resource" "WriteProgress3" {
  depends_on = [citrix_machine_catalog.TACG-TMM-CVAD-V-MC]
  provisioner "local-exec" {
    command = "echo The Machine Catalog was successfully created..."
  }
}

/* data "citrix_machine_catalog" "TACG-TMM-CVAD-MC" {
  name = var.TACG-TMM-CVAD-MC-Name
} */

#### Create the Delivery Group
resource "citrix_delivery_group" "TACG-TMM-CVAD-V-DG" {
  depends_on = [citrix_machine_catalog.TACG-TMM-CVAD-V-MC, citrix_storefront_server.CreateSFServer]
  #depends_on               = [data.citrix_machine_catalog.TACG-TMM-CVAD-MC]
  name                     = var.TACG-TMM-CVAD-DG-Name
  description              = var.TACG-TMM-CVAD-DG-Description
  minimum_functional_level = var.TACG-TMM-CVAD-DG-FunctionalLevel
  storefront_servers       = [citrix_storefront_server.CreateSFServer.id]
  associated_machine_catalogs = [
    {
      machine_catalog = citrix_machine_catalog.TACG-TMM-CVAD-V-MC.id
      #machine_catalog = data.citrix_machine_catalog.TACG-TMM-CVAD-MC.id
      machine_count = var.TACG-TMM-CVAD-MC-VM-NumberOfVMs
    }
  ]
  desktops = [
    {
      published_name = var.TACG-TMM-CVAD-DG-Desktops-Name
      description    = var.TACG-TMM-CVAD-DG-Desktops-Description
      restricted_access_users = {
        allow_list = var.TACG-TMM-CVAD-DG-Desktops-AllowList
      }
      enabled = var.TACG-TMM-CVAD-DG-Desktops-Enabled

      ###### Not needed due to Static assignment type-MC
      # enable_session_roaming = var.TACG-TMM-CVAD-DG-Desktops-SessionRoaming
    }

  ]
  autoscale_settings = {
    autoscale_enabled                   = var.TACG-TMM-CVAD-DG-AS-Enabled
    peak_disconnect_timeout_minutes     = var.TACG-TMM-CVAD-DG-AS-PeakDisconnectTime
    off_peak_disconnect_timeout_minutes = var.TACG-TMM-CVAD-DG-AS-OffPeakDisconnectTime
    peak_disconnect_action              = var.TACG-TMM-CVAD-DG-AS-PeakDisconnectAction
    off_peak_disconnect_action          = var.TACG-TMM-CVAD-DG-AS-OffPeakDisconnectAction
    off_peak_log_off_timeout_minutes    = var.TACG-TMM-CVAD-DG-AS-PeakLogoffTime
    peak_log_off_timeout_minutes        = var.TACG-TMM-CVAD-DG-AS-OffPeakLogoffTime
    peak_log_off_action                 = var.TACG-TMM-CVAD-DG-AS-PeakDisconnectAction
    off_peak_log_off_action             = var.TACG-TMM-CVAD-DG-AS-OffPeakDisconnectAction

    ###### Not needed due to Static assignment type-MC
    #log_off_off_peak_disconnected_session_after_seconds = var.TACG-TMM-CVAD-DG-AS-PeakLogoffTime
    #log_off_peak_disconnected_session_after_seconds     = var.TACG-TMM-CVAD-DG-AS-OffPeakLogoffTime
    #peak_log_off_action                                 = var.TACG-TMM-CVAD-DG-AS-PeakDisconnectAction
    #off_peak_log_off_action                             = var.TACG-TMM-CVAD-DG-AS-OffPeakDisconnectAction
    #log_off_reminder_enabled                            = var.TACG-TMM-CVAD-DG-AS-Enabled
    #log_off_reminder_message                            = var.TACG-TMM-CVAD-DG-RS-Notification-MessageBody
    #log_off_reminder_title                              = var.TACG-TMM-CVAD-DG-AS-Notification-MessageTitle
    #log_off_warning_message                             = var.TACG-TMM-CVAD-DG-RS-Notification-MessageBody
    #log_off_warning_title                               = var.TACG-TMM-CVAD-DG-AS-Notification-MessageTitle
    timezone = var.TACG-TMM-CVAD-DG-AS-TimeZone

    power_time_schemes = [
      {
        days_of_week          = var.TACG-TMM-CVAD-DG-AS-Days
        display_name          = var.TACG-TMM-CVAD-DG-AS-Name
        peak_time_ranges      = var.TACG-TMM-CVAD-DG-AS-PeakTime
        pool_using_percentage = false
      },
    ]
  }
  restricted_access_users = {
    allow_list = var.TACG-TMM-CVAD-DG-Desktops-AllowList
  }
  reboot_schedules = [
    {
      name                    = var.TACG-TMM-CVAD-DG-RS-Name
      reboot_schedule_enabled = var.TACG-TMM-CVAD-DG-RS-Enabled
      frequency               = var.TACG-TMM-CVAD-DG-RS-Frequency
      frequency_factor        = 1
      days_in_week            = var.TACG-TMM-CVAD-DG-RS-RebootDays
      start_time              = var.TACG-TMM-CVAD-DG-RS-RebootStartTime
      start_date              = var.TACG-TMM-CVAD-DG-RS-RebootStartDate
      reboot_duration_minutes = var.TACG-TMM-CVAD-DG-RS-RebootDuration
      ignore_maintenance_mode = var.TACG-TMM-CVAD-DG-RS-IgnoreMaintenanceMode
      natural_reboot_schedule = var.TACG-TMM-CVAD-DG-RS-NormalRebootSchedule

      ###### Not needed due to SingleSession type-MC
      #reboot_notification_to_users = {
      #notification_duration_minutes       = var.TACG-TMM-CVAD-DG-RS-Notification-Duration
      #notification_message                = var.TACG-TMM-CVAD-DG-RS-Notification-MessageBody
      #notification_title                  = var.TACG-TMM-CVAD-DG-RS-Notification-MessageTitle
      #notification_repeat_every_5_minutes = var.TACG-TMM-CVAD-DG-RS-Notification-MessageRepeat
      #} 
    }
  ]
}

resource "null_resource" "WriteProgress4" {
  depends_on = [citrix_delivery_group.TACG-TMM-CVAD-V-DG]
  provisioner "local-exec" {
    command = "echo The Delivery Group was successfully created..."
  }
}

#### Create a default Policy Set
resource "citrix_policy_set_v2" "TACG-TMM-CVAD-V-DefaultPolicySet" {
  depends_on      = [citrix_delivery_group.TACG-TMM-CVAD-V-DG]
  name            = var.TACG-TMM-CVAD-PS-Name
  description     = var.TACG-TMM-CVAD-PS-Description
  delivery_groups = [citrix_delivery_group.TACG-TMM-CVAD-V-DG.id]
}

#### Create a Policy in the Policy Set
resource "citrix_policy" "TACG-TMM-CVAD-DefaultPolicySet-Printing" {
  depends_on    = [citrix_policy_set_v2.TACG-TMM-CVAD-DefaultPolicySet]
  policy_set_id = citrix_policy_set_v2.TACG-TMM-CVAD-DefaultPolicySet.id
  name          = "Printing"
  description   = "Example of Printer-related policy in default Policy Set"
  enabled       = true
}

#### Create a Policy in the Policy Set
resource "citrix_policy" "TACG-TMM-CVAD-DefaultPolicySet-HDX" {
  depends_on    = [citrix_policy_set_v2.TACG-TMM-CVAD-DefaultPolicySet]
  policy_set_id = citrix_policy_set_v2.TACG-TMM-CVAD-DefaultPolicySet.id
  name          = "HDX Graphics"
  description   = "Example of HDX Graphics-related policy in default Policy Set"
  enabled       = true
}

#### Create a Policy in the Policy Set
resource "citrix_policy" "TACG-TMM-CVAD-DefaultPolicySet-ClientDrives" {
  depends_on    = [citrix_policy_set_v2.TACG-TMM-CVAD-DefaultPolicySet]
  policy_set_id = citrix_policy_set_v2.TACG-TMM-CVAD-DefaultPolicySet.id
  name          = "Client Drives"
  description   = "Example of Client Drive-related policy in default Policy Set"
  enabled       = true
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-Printing1" {
  policy_id   = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-Printing.id
  name        = "ClientPrinterAutoCreation"
  value       = "DefaultPrinterOnly"
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-Printing2" {
  policy_id   = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-Printing.id
  name        = "UniversalPrintDriverUsage"
  value       = "FallbackToSpecific"
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-HDX1" {
  policy_id   = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-HDX.id
  name        = "AllowVisuallyLosslessCompression"
  enabled     = true
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-HDX2" {
  policy_id   = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-HDX.id
  name        = "UseVideoCodecForCompression"
  value       = "UseVideoCodecIfPreferred"
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-HDX3" {
  policy_id   = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-HDX.id
  name        = "UseHardwareEncodingForVideoCodec"
  enabled     = true
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-CD1" {
  policy_id   = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-ClientDrives.id
  name        = "AutoConnectDrives"
  enabled     = true
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-CD2" {
  policy_id   = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-ClientDrives.id
  name        = "ClientDriveRedirection"
  enabled     = true
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-CD3" {
  policy_id   = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-ClientDrives.id
  name        = "ClientFixedDrives"
  enabled     = true
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-CD4" {
  policy_id   = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-ClientDrives.id
  name        = "ClientFloppyDrives"
  enabled     = false
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-CD5" {
  policy_id   = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-ClientDrives.id
  name        = "ClientOpticalDrives"
  enabled     = false
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-CD6" {
  policy_id   = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-ClientDrives.id
  name        = "ClientNetworkDrives"
  enabled     = false
  use_default = false
}

#### Create the corresponding Policy Filters
resource "citrix_delivery_group_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-DGFilter-1" {
  depends_on        = [citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-Printing]
  policy_id         = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-Printing.id
  enabled           = true
  allowed           = true
  delivery_group_id = citrix_delivery_group.TACG-TMM-CVAD-DG.id
}

#### Create the corresponding Policy Filters
resource "citrix_delivery_group_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-DGFilter-2" {
  depends_on        = [citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-HDX]
  policy_id         = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-HDX.id
  enabled           = true
  allowed           = true
  delivery_group_id = citrix_delivery_group.TACG-TMM-CVAD-DG.id
}

#### Create the corresponding Policy Filters
resource "citrix_delivery_group_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-DGFilter-3" {
  depends_on        = [citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-ClientDrives]
  policy_id         = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-ClientDrives.id
  enabled           = true
  allowed           = true
  delivery_group_id = citrix_delivery_group.TACG-TMM-CVAD-DG.id
}

#### Create the corresponding Policy Filters
resource "citrix_user_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-SIDFilter-1" {
  depends_on = [citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-Printing]
  policy_id  = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-Printing.id
  enabled    = true
  allowed    = true
  sid        = var.TACG-TMM-CVAD-PS-UserSID
}

#### Create the corresponding Policy Filters
resource "citrix_user_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-SIDFilter-2" {
  depends_on = [citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-HDX]
  policy_id  = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-HDX.id
  enabled    = true
  allowed    = true
  sid        = var.TACG-TMM-CVAD-PS-UserSID
}

#### Create the corresponding Policy Filters
resource "citrix_user_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-SIDFilter-3" {
  depends_on = [citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-ClientDrives]
  policy_id  = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-ClientDrives.id
  enabled    = true
  allowed    = true
  sid        = var.TACG-TMM-CVAD-PS-UserSID
}

#### Create the corresponding Policy Filters
resource "citrix_client_ip_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-ClientIPFilter-1" {
  depends_on = [citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-Printing]
  policy_id  = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-Printing.id
  enabled    = true
  allowed    = true
  ip_address = var.TACG-TMM-CVAD-PS-IPRange
}

#### Create the corresponding Policy Filters
resource "citrix_client_ip_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-ClientIPFilter-2" {
  depends_on = [citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-HDX]
  policy_id  = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-HDX.id
  enabled    = true
  allowed    = true
  ip_address = var.TACG-TMM-CVAD-PS-IPRange
}

#### Create the corresponding Policy Filters
resource "citrix_client_ip_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-ClientIPFilter-3" {
  depends_on = [citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-ClientDrives]
  policy_id  = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-ClientDrives.id
  enabled    = true
  allowed    = true
  ip_address = var.TACG-TMM-CVAD-PS-IPRange
}

resource "null_resource" "WriteProgress5" {
  depends_on = [citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD1, citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD2, citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD3, citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD4, citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD5, citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD6, citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-HDX1, citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-HDX2, citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-HDX3, citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-Printing1, citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-Printing2]
  provisioner "local-exec" {
    command = "echo The default Policy Set and its policies and filters were successfully created..."
  }
}Running the snippet should fulfill all needed steps: azadmin@tacg-cicd:/etc/terraform-tmm/CVADVS/_CreateCitrixEntities$ terraform apply
data.vsphere_datacenter.datacenter: Reading...
data.vsphere_datacenter.datacenter: Read complete after 0s [id=datacenter-3]
data.vsphere_host.host: Reading...
data.vsphere_datastore.datastore: Reading...
data.vsphere_compute_cluster.cluster: Reading...
data.vsphere_network.network: Reading...
data.vsphere_network.network: Read complete after 0s [id=network-12]
data.vsphere_datastore.datastore: Read complete after 0s [id=datastore-11]
data.vsphere_host.host: Read complete after 0s [id=host-8]
data.vsphere_compute_cluster.cluster: Read complete after 0s [id=domain-c17]
data.citrix_zone.TACG-TMM-CVAD-V-Zone: Reading...
data.citrix_zone.TACG-TMM-CVAD-V-Zone: Read complete after 0s [id=31fbc844-779f-4dc9-88f5-05f6de165187]

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_machine_catalog.TACG-TMM-CVAD-V-MC will be created
  + resource "citrix_machine_catalog" "TACG-TMM-CVAD-V-MC" {
      + allocation_type          = "Static"
      + built_in_scopes          = (known after apply)
      + delete_machine_accounts  = "None"
      + description              = "Terraform-created Machine Catalog for TACG running on XS8-Cluster"
      + id                       = (known after apply)
      + inherited_scopes         = (known after apply)
      + minimum_functional_level = "L7_20"
      + name                     = "TACG-TMM-CVAD-VSP-MC"
      + persist_user_changes     = (known after apply)
      + provisioning_scheme      = {
          + hypervisor                     = (known after apply)
          + hypervisor_resource_pool       = (known after apply)
          + identity_type                  = "ActiveDirectory"
          + machine_account_creation_rules = {
              + naming_scheme      = "tf-mcs-v-w11-#"
              + naming_scheme_type = "Numeric"
            }
          + machine_domain_identity        = {
              + domain                   = "the-austrian-citrix-guy.at"
              + domain_ou                = "OU=_COMPUTER,OU=TACG-CVAD,DC=the-austrian-citrix-guy,DC=at"
              + service_account          = (sensitive value)
              + service_account_password = (sensitive value)
            }
          + number_of_total_machines       = 2
          + vsphere_machine_config         = {
              + cpu_count                        = 2
              + image_snapshot                   = "V1"
              + master_image_note                = ""
              + master_image_vm                  = "win11-p01"
              + memory_mb                        = 4096
              + resource_pool_path               = ""
              + use_full_disk_clone_provisioning = false
            }
        }
      + provisioning_type        = "MCS"
      + scopes                   = []
      + session_support          = "SingleSession"
      + tenants                  = (known after apply)
      + zone                     = "31fbc844-779f-4dc9-88f5-05f6de165187"
    }

  # citrix_vsphere_hypervisor.TACG-TMM-CVAD-V-HypConn will be created
  + resource "citrix_vsphere_hypervisor" "TACG-TMM-CVAD-V-HypConn" {
      + addresses                                = [
          + (sensitive value),
        ]
      + id                                       = (known after apply)
      + max_absolute_active_actions              = 20
      + max_absolute_new_actions_per_minute      = 10
      + max_power_actions_percentage_of_machines = 20
      + name                                     = "TACG-TMM-CVAD-VSP-Cluster"
      + password                                 = (sensitive value)
      + password_format                          = "PlainText"
      + scopes                                   = []
      + tenants                                  = (known after apply)
      + username                                 = (sensitive value)
      + zone                                     = "31fbc844-779f-4dc9-88f5-05f6de165187"
    }

  # citrix_vsphere_hypervisor_resource_pool.TACG-TMM-CVAD-V-HypConnPool will be created
  + resource "citrix_vsphere_hypervisor_resource_pool" "TACG-TMM-CVAD-V-HypConnPool" {
      + cluster                   = {
          + cluster_name = "TACG"
          + datacenter   = (sensitive value)
        }
      + hypervisor                = (known after apply)
      + id                        = (known after apply)
      + name                      = "TACG-TMM-CVAD-VSP-Cluster"
      + networks                  = [
          + (sensitive value),
        ]
      + storage                   = [
          + {
              + storage_name = (sensitive value)
              + superseded   = false
            },
        ]
      + temporary_storage         = [
          + {
              + storage_name = (sensitive value)
              + superseded   = false
            },
        ]
      + use_local_storage_caching = false
      + vm_tagging                = true
    }

  # null_resource.WriteProgress2 will be created
  + resource "null_resource" "WriteProgress2" {
      + id = (known after apply)
    }

  # null_resource.WriteProgress3 will be created
  + resource "null_resource" "WriteProgress3" {
      + id = (known after apply)
    }
  # citrix_client_ip_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-ClientIPFilter-1 will be created
  + resource "citrix_client_ip_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-ClientIPFilter-1" {
      + allowed    = true
      + enabled    = true
      + id         = (known after apply)
      + ip_address = "10.10.0.0"
      + policy_id  = (known after apply)
    }

  # citrix_client_ip_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-ClientIPFilter-2 will be created
  + resource "citrix_client_ip_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-ClientIPFilter-2" {
      + allowed    = true
      + enabled    = true
      + id         = (known after apply)
      + ip_address = "10.10.0.0"
      + policy_id  = (known after apply)
    }

  # citrix_client_ip_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-ClientIPFilter-3 will be created
  + resource "citrix_client_ip_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-ClientIPFilter-3" {
      + allowed    = true
      + enabled    = true
      + id         = (known after apply)
      + ip_address = "10.10.0.0"
      + policy_id  = (known after apply)
    }

  # citrix_delivery_group.TACG-TMM-CVAD-V-DG will be created
  + resource "citrix_delivery_group" "TACG-TMM-CVAD-V-DG" {
      + associated_machine_catalogs = [
          + {
              + machine_catalog = "bf005a81-2235-4f6a-af65-bccf431a0b31"
              + machine_count   = 2
            },
        ]
      + autoscale_settings          = {
          + autoscale_enabled                                     = true
          + disconnect_off_peak_idle_session_after_seconds        = 0
          + disconnect_peak_idle_session_after_seconds            = 0
          + log_off_off_peak_disconnected_session_after_seconds   = 0
          + log_off_peak_disconnected_session_after_seconds       = 0
          + log_off_reminder_enabled                              = false
          + log_off_reminder_message                              = ""
          + log_off_reminder_title                                = ""
          + log_off_warning_message                               = ""
          + log_off_warning_title                                 = ""
          + off_peak_buffer_size_percent                          = 0
          + off_peak_disconnect_action                            = "Suspend"
          + off_peak_disconnect_timeout_minutes                   = 5
          + off_peak_extended_disconnect_action                   = "Nothing"
          + off_peak_extended_disconnect_timeout_minutes          = 0
          + off_peak_limit_seconds_to_force_log_off_user          = 0
          + off_peak_log_off_action                               = "Suspend"
          + off_peak_log_off_reminder_interval                    = 0
          + off_peak_log_off_timeout_minutes                      = 5
          + peak_autoscale_assigned_power_on_idle_action          = "Nothing"
          + peak_autoscale_assigned_power_on_idle_timeout_minutes = 0
          + peak_buffer_size_percent                              = 0
          + peak_disconnect_action                                = "Suspend"
          + peak_disconnect_timeout_minutes                       = 5
          + peak_extended_disconnect_action                       = "Nothing"
          + peak_extended_disconnect_timeout_minutes              = 0
          + peak_limit_seconds_to_force_log_off_user              = 0
          + peak_log_off_action                                   = "Suspend"
          + peak_log_off_reminder_interval                        = 0
          + peak_log_off_timeout_minutes                          = 5
          + power_off_delay_minutes                               = 30
          + power_time_schemes                                    = [
              + {
                  + days_of_week          = [
                      + "Friday",
                      + "Monday",
                      + "Thursday",
                      + "Tuesday",
                      + "Wednesday",
                    ]
                  + display_name          = "TACG-TMM-CVAD-VSP-AS"
                  + peak_time_ranges      = [
                      + "09:00-17:00",
                    ]
                  + pool_using_percentage = false
                },
            ]
          + timezone                                              = "UTC"
        }
      + built_in_scopes             = (known after apply)
      + color_depth                 = "TwentyFourBit"
      + default_desktop_icon        = "1"
      + description                 = "Terraform-created Delivery Group for TACG running on XS8-Cluster"
      + desktops                    = [
          + {
              + description             = "Terraform-created W11 Desktops running on XS8-Cluster"
              + enabled                 = true
              + id                      = (known after apply)
              + published_name          = "TACG-VSP-W11"
              + restricted_access_users = {
                  + allow_list = [
                      + "TACG\\vdaallowed",
                    ]
                  + block_list = []
                }
            },
        ]
      + enabled                     = true
      + force_delete                = false
      + id                          = (known after apply)
      + in_maintenance_mode         = false
      + inherited_scopes            = (known after apply)
      + load_balancing_type         = "None"
      + minimum_functional_level    = "L7_20"
      + name                        = "TACG-TMM-CVAD-VSP-DG"
      + reboot_schedules            = [
          + {
              + days_in_week            = [
                  + "Sunday",
                ]
              + frequency               = "Weekly"
              + frequency_factor        = 1
              + ignore_maintenance_mode = true
              + name                    = "TACG-TMM-CVAD-VSP-RS"
              + natural_reboot_schedule = false
              + reboot_duration_minutes = 0
              + reboot_schedule_enabled = true
              + start_date              = "2025-01-01"
              + start_time              = "02:00"
                # (1 unchanged attribute hidden)
            },
        ]
      + restricted_access_users     = {
          + allow_list = [
              + "TACG\\vdaallowed",
            ]
          + block_list = []
        }
      + scopes                      = []
      + secure_ica_required         = false
      + storefront_servers          = [
          + (known after apply),
        ]
      + tenants                     = (known after apply)
      + total_machines              = (known after apply)
    }

  # citrix_delivery_group_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-DGFilter-1 will be created
  + resource "citrix_delivery_group_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-DGFilter-1" {
      + allowed           = true
      + delivery_group_id = (known after apply)
      + enabled           = true
      + id                = (known after apply)
      + policy_id         = (known after apply)
    }

  # citrix_delivery_group_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-DGFilter-2 will be created
  + resource "citrix_delivery_group_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-DGFilter-2" {
      + allowed           = true
      + delivery_group_id = (known after apply)
      + enabled           = true
      + id                = (known after apply)
      + policy_id         = (known after apply)
    }

  # citrix_delivery_group_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-DGFilter-3 will be created
  + resource "citrix_delivery_group_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-DGFilter-3" {
      + allowed           = true
      + delivery_group_id = (known after apply)
      + enabled           = true
      + id                = (known after apply)
      + policy_id         = (known after apply)
    }

  # citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-ClientDrives will be created
  + resource "citrix_policy" "TACG-TMM-CVAD-DefaultPolicySet-ClientDrives" {
      + description   = "Example of Client Drive-related policy in default Policy Set"
      + enabled       = true
      + id            = (known after apply)
      + name          = "Client Drives"
      + policy_set_id = (known after apply)
    }

  # citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-HDX will be created
  + resource "citrix_policy" "TACG-TMM-CVAD-DefaultPolicySet-HDX" {
      + description   = "Example of HDX Graphics-related policy in default Policy Set"
      + enabled       = true
      + id            = (known after apply)
      + name          = "HDX Graphics"
      + policy_set_id = (known after apply)
    }

  # citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-Printing will be created
  + resource "citrix_policy" "TACG-TMM-CVAD-DefaultPolicySet-Printing" {
      + description   = "Example of Printer-related policy in default Policy Set"
      + enabled       = true
      + id            = (known after apply)
      + name          = "Printing"
      + policy_set_id = (known after apply)
    }

  # citrix_policy_set_v2.TACG-TMM-CVAD-DefaultPolicySet will be created
  + resource "citrix_policy_set_v2" "TACG-TMM-CVAD-DefaultPolicySet" {
      + assigned        = (known after apply)
      + delivery_groups = [
          + (known after apply),
        ]
      + description     = "Terraform-created default Policy Set for TACG running on VSP-Cluster"
      + id              = (known after apply)
      + name            = "TACG-TMM-CVAD-VSP-PS-Default"
      + scopes          = []
    }

  # citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD1 will be created
  + resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-CD1" {
      + enabled     = true
      + id          = (known after apply)
      + name        = "AutoConnectDrives"
      + policy_id   = (known after apply)
      + use_default = false
    }

  # citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD2 will be created
  + resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-CD2" {
      + enabled     = true
      + id          = (known after apply)
      + name        = "ClientDriveRedirection"
      + policy_id   = (known after apply)
      + use_default = false
    }

  # citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD3 will be created
  + resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-CD3" {
      + enabled     = true
      + id          = (known after apply)
      + name        = "ClientFixedDrives"
      + policy_id   = (known after apply)
      + use_default = false
    }

  # citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD4 will be created
  + resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-CD4" {
      + enabled     = false
      + id          = (known after apply)
      + name        = "ClientFloppyDrives"
      + policy_id   = (known after apply)
      + use_default = false
    }

  # citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD5 will be created
  + resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-CD5" {
      + enabled     = false
      + id          = (known after apply)
      + name        = "ClientOpticalDrives"
      + policy_id   = (known after apply)
      + use_default = false
    }

  # citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD6 will be created
  + resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-CD6" {
      + enabled     = false
      + id          = (known after apply)
      + name        = "ClientNetworkDrives"
      + policy_id   = (known after apply)
      + use_default = false
    }

  # citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-HDX1 will be created
  + resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-HDX1" {
      + enabled     = true
      + id          = (known after apply)
      + name        = "AllowVisuallyLosslessCompression"
      + policy_id   = (known after apply)
      + use_default = false
    }

  # citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-HDX2 will be created
  + resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-HDX2" {
      + id          = (known after apply)
      + name        = "UseVideoCodecForCompression"
      + policy_id   = (known after apply)
      + use_default = false
      + value       = "UseVideoCodecIfPreferred"
    }

  # citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-HDX3 will be created
  + resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-HDX3" {
      + enabled     = true
      + id          = (known after apply)
      + name        = "UseHardwareEncodingForVideoCodec"
      + policy_id   = (known after apply)
      + use_default = false
    }

  # citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-Printing1 will be created
  + resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-Printing1" {
      + id          = (known after apply)
      + name        = "ClientPrinterAutoCreation"
      + policy_id   = (known after apply)
      + use_default = false
      + value       = "DefaultPrinterOnly"
    }

  # citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-Printing2 will be created
  + resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-Printing2" {
      + id          = (known after apply)
      + name        = "UniversalPrintDriverUsage"
      + policy_id   = (known after apply)
      + use_default = false
      + value       = "FallbackToSpecific"
    }

  # citrix_storefront_server.CreateSFServer will be created
  + resource "citrix_storefront_server" "CreateSFServer" {
      + description = "Terraform-created StoreFront Server for TACG-CVAD"
      + enabled     = true
      + id          = (known after apply)
      + name        = "TACG-SF-Server1"
      + url         = "https://storefront-v.the-austrian-citrix-guy.at/Citrix/Store"
    }

  # citrix_user_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-SIDFilter-1 will be created
  + resource "citrix_user_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-SIDFilter-1" {
      + allowed   = true
      + enabled   = true
      + id        = (known after apply)
      + policy_id = (known after apply)
      + sid       = "S-1-5-21-3919795815-764310115-1491410927-2116"
    }

  # citrix_user_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-SIDFilter-2 will be created
  + resource "citrix_user_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-SIDFilter-2" {
      + allowed   = true
      + enabled   = true
      + id        = (known after apply)
      + policy_id = (known after apply)
      + sid       = "S-1-5-21-3919795815-764310115-1491410927-2116"
    }

  # citrix_user_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-SIDFilter-3 will be created
  + resource "citrix_user_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-SIDFilter-3" {
      + allowed   = true
      + enabled   = true
      + id        = (known after apply)
      + policy_id = (known after apply)
      + sid       = "S-1-5-21-3919795815-764310115-1491410927-2116"
    }

  # null_resource.WriteProgress4 will be created
  + resource "null_resource" "WriteProgress4" {
      + id = (known after apply)
    }

  # null_resource.WriteProgress5 will be created
  + resource "null_resource" "WriteProgress5" {
      + id = (known after apply)
    }

Plan: 33 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_vsphere_hypervisor.TACG-TMM-CVAD-V-HypConn: Creating...
citrix_vsphere_hypervisor.TACG-TMM-CVAD-V-HypConn: Still creating... [00m10s elapsed]
citrix_vsphere_hypervisor.TACG-TMM-CVAD-V-HypConn: Still creating... [00m20s elapsed]
citrix_vsphere_hypervisor.TACG-TMM-CVAD-V-HypConn: Still creating... [00m30s elapsed]
citrix_vsphere_hypervisor.TACG-TMM-CVAD-V-HypConn: Still creating... [00m40s elapsed]
citrix_vsphere_hypervisor.TACG-TMM-CVAD-V-HypConn: Creation complete after 40s [id=7743fb34-1e31-4cb0-b7e7-6a0d65ca871a]
citrix_vsphere_hypervisor_resource_pool.TACG-TMM-CVAD-V-HypConnPool: Creating...
citrix_vsphere_hypervisor_resource_pool.TACG-TMM-CVAD-V-HypConnPool: Still creating... [00m10s elapsed]
citrix_vsphere_hypervisor_resource_pool.TACG-TMM-CVAD-V-HypConnPool: Still creating... [00m20s elapsed]
citrix_vsphere_hypervisor_resource_pool.TACG-TMM-CVAD-V-HypConnPool: Still creating... [00m30s elapsed]
citrix_vsphere_hypervisor_resource_pool.TACG-TMM-CVAD-V-HypConnPool: Still creating... [00m40s elapsed]
citrix_vsphere_hypervisor_resource_pool.TACG-TMM-CVAD-V-HypConnPool: Still creating... [00m50s elapsed]
citrix_vsphere_hypervisor_resource_pool.TACG-TMM-CVAD-V-HypConnPool: Still creating... [01m00s elapsed]
citrix_vsphere_hypervisor_resource_pool.TACG-TMM-CVAD-V-HypConnPool: Creation complete after 1m0s [id=7fe362b3-6f9f-4880-ba6e-8d7af5dc1b48]
null_resource.WriteProgress2: Creating...
null_resource.WriteProgress2: Provisioning with 'local-exec'...
null_resource.WriteProgress2 (local-exec): Executing: ["cmd" "/C" "echo The Hypervisor Connection Pool was successfully created..."]
citrix_machine_catalog.TACG-TMM-CVAD-V-MC: Creating...
null_resource.WriteProgress2 (local-exec): The Hypervisor Connection Pool was successfully created...
null_resource.WriteProgress2: Creation complete after 0s [id=2523874641761484813]
citrix_machine_catalog.TACG-TMM-CVAD-V-MC: Still creating... [00m10s elapsed]
citrix_machine_catalog.TACG-TMM-CVAD-V-MC: Still creating... [00m20s elapsed]
citrix_machine_catalog.TACG-TMM-CVAD-V-MC: Still creating... [00m30s elapsed]
citrix_machine_catalog.TACG-TMM-CVAD-V-MC: Still creating... [00m40s elapsed]
citrix_machine_catalog.TACG-TMM-CVAD-V-MC: Still creating... [00m50s elapsed]
citrix_machine_catalog.TACG-TMM-CVAD-V-MC: Still creating... [01m00s elapsed]
citrix_machine_catalog.TACG-TMM-CVAD-V-MC: Still creating... [01m10s elapsed]
citrix_machine_catalog.TACG-TMM-CVAD-V-MC: Still creating... [01m20s elapsed]
citrix_machine_catalog.TACG-TMM-CVAD-V-MC: Still creating... [01m30s elapsed]
citrix_machine_catalog.TACG-TMM-CVAD-V-MC: Still creating... [01m40s elapsed]
citrix_machine_catalog.TACG-TMM-CVAD-V-MC: Still creating... [01m50s elapsed]
citrix_machine_catalog.TACG-TMM-CVAD-V-MC: Still creating... [02m00s elapsed]
citrix_machine_catalog.TACG-TMM-CVAD-V-MC: Still creating... [02m10s elapsed]
citrix_machine_catalog.TACG-TMM-CVAD-V-MC: Still creating... [02m20s elapsed]
citrix_machine_catalog.TACG-TMM-CVAD-V-MC: Still creating... [02m30s elapsed]
citrix_machine_catalog.TACG-TMM-CVAD-V-MC: Still creating... [02m40s elapsed]
citrix_machine_catalog.TACG-TMM-CVAD-V-MC: Still creating... [02m50s elapsed]
citrix_machine_catalog.TACG-TMM-CVAD-V-MC: Creation complete after 2m51s [id=bf005a81-2235-4f6a-af65-bccf431a0b31]
null_resource.WriteProgress3: Creating...
null_resource.WriteProgress3: Provisioning with 'local-exec'...
null_resource.WriteProgress3 (local-exec): Executing: ["cmd" "/C" "echo The Machine Catalog was successfully created..."]
null_resource.WriteProgress3 (local-exec): The Machine Catalog was successfully created...
null_resource.WriteProgress3: Creation complete after 1s [id=1626259821095883122]
citrix_storefront_server.CreateSFServer: Creating...
citrix_storefront_server.CreateSFServer: Still creating... [00m10s elapsed]
citrix_storefront_server.CreateSFServer: Creation complete after 10s [id=7b1a5810-57f8-4381-9d1b-8d12232bfd1d1]
citrix_delivery_group.TACG-TMM-CVAD-V-DG: Creating...
citrix_delivery_group.TACG-TMM-CVAD-V-DG: Still creating... [00m10s elapsed]
citrix_delivery_group.TACG-TMM-CVAD-V-DG: Still creating... [00m20s elapsed]
citrix_delivery_group.TACG-TMM-CVAD-V-DG: Creation complete after 21s [id=3f259eed-b038-4811-acb5-8b605e243454]
null_resource.WriteProgress4: Creating...
citrix_policy_set_v2.TACG-TMM-CVAD-DefaultPolicySet: Creating...
null_resource.WriteProgress4: Provisioning with 'local-exec'...
null_resource.WriteProgress4 (local-exec): Executing: ["cmd" "/C" "echo The Delivery Group was successfully created..."]
null_resource.WriteProgress4 (local-exec): The Delivery Group was successfully created...
null_resource.WriteProgress4: Creation complete after 0s [id=8623307076369312790]
citrix_policy_set_v2.TACG-TMM-CVAD-DefaultPolicySet: Still creating... [00m10s elapsed]
citrix_policy_set_v2.TACG-TMM-CVAD-DefaultPolicySet: Still creating... [00m20s elapsed]
citrix_policy_set_v2.TACG-TMM-CVAD-DefaultPolicySet: Still creating... [00m30s elapsed]
citrix_policy_set_v2.TACG-TMM-CVAD-DefaultPolicySet: Creation complete after 31s [id=e59ec7dd-548d-4ee5-9f12-265a72e57eab]
citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-Printing: Creating...
citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-HDX: Creating...
citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-ClientDrives: Creating...
citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-ClientDrives: Creation complete after 0s [id=6857750b-be93-4496-bcf3-a158be09a2f4]
citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-HDX: Creation complete after 0s [id=d89d8c8b-643d-4b4d-b18d-a113db441377]
citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-Printing: Creation complete after 0s [id=a2a37d82-84cd-40ad-a8d3-b2a40e6cd9cf]
citrix_client_ip_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-ClientIPFilter-3: Creating...
citrix_user_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-SIDFilter-2: Creating...
citrix_delivery_group_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-DGFilter-3: Creating...
citrix_user_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-SIDFilter-3: Creating...
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD4: Creating...
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD2: Creating...
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD5: Creating...
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD3: Creating...
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD6: Creating...
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD1: Creating...
citrix_user_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-SIDFilter-2: Creation complete after 0s [id=1655477d-ae9f-41b1-b8a8-dfa3946b03ca]
citrix_client_ip_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-ClientIPFilter-3: Creation complete after 0s [id=ee6b4eec-603d-4100-9645-deae77fda9c1]
citrix_delivery_group_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-DGFilter-3: Creation complete after 0s [id=a0c3ea85-bb37-4951-9452-00ae3fa0989a]
citrix_delivery_group_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-DGFilter-2: Creating...
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-HDX3: Creating...
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD4: Creation complete after 0s [id=192657fb-3d4e-4c33-af52-5592d27f2850]
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-HDX2: Creating...
citrix_user_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-SIDFilter-3: Creation complete after 0s [id=be88bf99-dea0-4b92-97a7-98aaeb93582c]
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD2: Creation complete after 0s [id=5575e198-b0f7-4153-9111-578d34687f02]
citrix_delivery_group_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-DGFilter-1: Creating...
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD3: Creation complete after 0s [id=70adc8c4-3d7a-4b0f-a1c9-5e15ed46d660]
citrix_client_ip_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-ClientIPFilter-2: Creating...
citrix_user_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-SIDFilter-1: Creating...
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-HDX1: Creating...
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD5: Creation complete after 0s [id=3e91fc16-69f8-4300-a76c-0dfe3b98c2bb]
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD1: Creation complete after 0s [id=d936a75a-001c-492d-9a33-ae386a484077]
citrix_client_ip_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-ClientIPFilter-1: Creating...
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD6: Creation complete after 1s [id=da97b2b0-6b99-471f-8814-9c7cdd1d81e9]
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-Printing1: Creating...
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-Printing2: Creating...
citrix_delivery_group_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-DGFilter-2: Creation complete after 1s [id=f07abd42-1719-4a35-989f-67b43d5e57da]
citrix_client_ip_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-ClientIPFilter-2: Creation complete after 1s [id=d7c336fd-1da3-490b-9c25-4f339960610f]
citrix_user_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-SIDFilter-1: Creation complete after 1s [id=23ba2ee7-e3cf-4f98-8ab3-0f54ae4c901e]
citrix_delivery_group_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-DGFilter-1: Creation complete after 1s [id=7da46401-84dd-4770-b3d3-7a83cc156a72]
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-HDX3: Creation complete after 1s [id=d99c2aca-eadf-40f5-b3b2-850bbc451149]
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-HDX2: Creation complete after 1s [id=df8c4b99-4c40-4d63-b72d-935a79fa53db]
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-HDX1: Creation complete after 1s [id=4767a94b-fb63-4196-a6e5-f62cfc371e8a]
citrix_client_ip_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-ClientIPFilter-1: Creation complete after 1s [id=55b82b00-518f-4f65-b6a5-b695a64f7357]
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-Printing1: Creation complete after 0s [id=d547e435-d3fb-4742-aacc-c31d2d3c9309]
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-Printing2: Creation complete after 0s [id=48e05a9f-faaa-4454-a688-991a05985db1]
null_resource.WriteProgress5: Creating...
null_resource.WriteProgress5: Provisioning with 'local-exec'...
null_resource.WriteProgress5 (local-exec): Executing: ["cmd" "/C" "echo The default Policy Set and its policies and filters were successfully created..."]
null_resource.WriteProgress5 (local-exec): The default Policy Set and its policies and filters were successfully created...
null_resource.WriteProgress5: Creation complete after 0s [id=3744815402207074589]

Apply complete! Resources: 33 added, 0 changed, 0 destroyed.
azadmin@tacg-cicd:/etc/terraform-tmm/CVADVS/_CreateCitrixEntities$Terraform has successfully installed all needed Citrix entities: A Hypervisor Connection and Hypervisor Connection Pool were created:  All related tests are OK:  A Machine Catalog was created:  All related tests are OK:  A Delivery Group was created:  All related tests are OK:  A dedicated Policy Set and its policies were created:  Web Studio shows a complete, error-free deployment of Citrix Virtual Apps and Desktops 2507 LTSR:  As the deployment is complete, we can now log on to StoreFront and start our assigned desktop. Using the DeploymentAs all prerequisites are met, we can now log on to our CVAD site and start our assigned VDI desktop. Logging on through Citrix Workspace App or Storefront is possible:   Let´s choose our desktop and start it:    That completes our guide “Using Infrastructure-as-Code for deploying Citrix Virtual Apps and Desktops 2507 LTSR on vSphere 8”. As the importance of Infrastructure-as-Code and Automation steadily increases, we will continue to focus on these topics. Stay tuned for new guides and our soon-to-be-published Automation Handbook on our Automation landing page in Citrix TechZone (https://community.citrix.com/tech-zone/automation), where we cover all relevant aspects of IaC and Automation in a Citrix Environment. DisclaimerDisclaimer: EXCEPT WHERE EXPRESSLY PROVIDED OTHERWISE BY CLOUD SOFTWARE GROUP, THE CONTENT IN THIS DOCUMENT IS PROVIDED “AS IS” AND CLOUD SOFTWARE GROUP HEREBY DISCLAIMS ALL EXPRESS OR IMPLIED REPRESENTATIONS, WARRANTIES, GUARANTIES, AND CONDITIONS, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES OR CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD TO BE LEGALLY INVALID. CLOUD SOFTWARE GROUP MAKES NO REPRESENTATIONS, WARRANTIES, GUARANTIES, OR CONDITIONS AS TO THE QUALITY, SUITABILITY, TRUTH, ACCURACY, OR COMPLETENESS OF ANY OF THE CONTENT CONTAINED ON THE WEBSITE. The above disclaimer applies to any damages, liability, or injuries caused by any failure of performance, error, omission, interruption, deletion, defect, delay in operation or transmission, computer virus, communication line failure, theft or destruction of or unauthorized access to, alteration of, or use, whether for breach of contract, tort, negligence or any other cause of action.]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2025_11/overview.png.d2846ba227e815ad3cca634463f1f486.png" length="152602" type="image/png"/><pubDate>Wed, 19 Nov 2025 15:54:00 +0000</pubDate></item><item><title>Deployment Guide: Using Infrastructure-as-Code for deploying Citrix&#xAE; Virtual Apps and Desktops&#x2122; 2507 LTSR on XenServer&#x2122; 8.4</title><link>https://community.stage.citrix.com/tech-zone/automation/iac-cvad2507-xenserver8/</link><description><![CDATA[Using Infrastructure-as-Code for deploying Citrix® Virtual Apps and Desktops™ 2507 LTSR on XenServer™ 8.4 Citrix’s goal is to help customers automate their environments with minimal friction. We support multiple automation interfaces, REST APIs, PowerShell SDKs, Terraform providers, Ansible playbooks, and all current CI/CD frameworks, so customers can choose the tools that fit their workflow. Automation enhances Citrix’s ability to deliver repeatable, scalable, and secure deployments across hybrid and multi-cloud environments. It supports Citrix’s broader goals of reducing time-to-value, improving customer satisfaction, and deeply integrating into customer ecosystems.  IntroductionIn this guide, we cover the usage of three distinct Infrastructure-as-Code (IaC) frameworks to deploy a Citrix Virtual Desktops™ 2507 LTSR-based deployment on a XenServer® 8.4 cluster. The instructions are divided into blocks that must be started in a specific, predetermined order. However, it is not necessary to start all blocks in one go—the dependencies on previous blocks are recorded during runtime, so that essential time intervals or even execution stops between the individual blocks are not a problem.   Components and frameworks used in this guideLet´s have a short overview of all the components and frameworks used in this guide. We use Hashicorp Packer as the framework for generating the Master Images and templates for deploying all needed Windows-based virtual machines on XenServer 8.4, Hashicorp Terraform as the backbone and “coordinating” framework for creating all needed Infrastructure components, and Ansible as the configuration framework for all configuration needs of the components that Terraform cannot do during the initial creation of the respective element. XenServer 8.4For customers who want to virtualize Citrix workloads, XenServer delivers an affordable, straightforward hypervisor optimized for Citrix Virtual Apps™ and Desktops™ deployments. XenServer 8.4 is the most optimized hypervisor for running Citrix workloads, with features only available with Citrix and XenServer – relevant for this deployment guide are: IntelliCache™ MCS Read Cache You can find more information about XenServer 8 in its respective product documentation. Caution: XenServer currently lacks an official Packer plugin. In this guide, we use an open-source Tech Preview plugin maintained by my fellow Citrite colleagues Rody Kossen and Marshall Wu. You can find more about this TP on GitHub: https://github.com/xenserver/packer-plugin-xenserver/tree/feature-rodyk-revamp Please read the disclaimer at the end of the document for further information. Hashicorp TerraformTerraform is one of the most widely adopted Infrastructure-as-Code (IaC) platforms for provisioning and managing cloud and on-prem infrastructure at scale. Its declarative configuration model, strong ecosystem support, and provider-agnostic architecture make it a foundational tool for organizations seeking consistency, compliance, and operational maturity in their infrastructure lifecycle.  Terraform relies on declarative code to define infrastructure in a predictable, reproducible manner. By representing compute, networking, storage, IAM, and platform services as version-controlled code artifacts, Terraform: Eliminates configuration drift Ensures consistent provisioning across development, staging, and production Produces deployments that can be rebuilt deterministically This consistency is particularly valuable in regulated environments where documentation, repeatability, and environmental fidelity are required for audits and certification processes. For highly regulated verticals—such as finance, healthcare, government, and critical infrastructure—Terraform provides the governance, repeatability, and auditability required to meet stringent compliance obligations. These regulated verticals often operate under strict frameworks, such as: Finance: PCI-DSS, FFIEC, SOX Healthcare: HIPAA, HITECH Government/Public Sector: FedRAMP, NIST 800-53, DoD SRG Energy/Critical Infrastructure: NERC CIP Global Privacy Requirements: GDPR, regional data sovereignty mandates Terraform helps organizations meet these obligations by providing: Complete Traceability and Auditability: All infrastructure changes can be captured in version control systems. This enables traceable change histories, approvals, and rollbacks—supporting SOX, NIST, and ISO 27001 requirements around change management and configuration governance.  Standardized, Repeatable Environments: Terraform modules enable organizations to define compliant “building blocks” that development teams cannot inadvertently modify, ensuring every environment meets baseline controls for security, networking, identity, and data protection.  Operational Efficiency and Reduced Risk: Terraform’s execution plan and state management capabilities allow organizations to preview changes before applying them, reducing misconfigurations—a key risk driver in regulated environments. Additional operational benefits include: Automated provisioning at scale Predictable change outcomes through planning and applying workflows Reduced human error, a significant source of compliance violations Consistent rollback and recovery patterns  Integration With Secure DevOps and Platform Engineering Practices Terraform fits seamlessly into CI/CD, GitOps, and automated compliance frameworks, enabling regulated organizations to adopt modern DevOps practices without compromising governance.  It perfectly integrates with frameworks such as: Terraform Cloud/Enterprise for controlled workflows Vault for secure secrets injection Packer for master image pipelines GitHub Actions, GitLab, Azure DevOps for automated deployment Hashicorp PackerHashiCorp Packer is a foundational tool for organizations seeking to standardize, automate, and scale the creation of master images across multi-cloud and hybrid environments. As organizations increasingly adopt Infrastructure-as-Code (IaC) approaches, Packer provides a consistent, version-controlled, and repeatable way to build golden images aligned with modern DevOps and platform engineering practices. Standardization and Consistency Across Environments: Packer enables the creation of identically configured images for AWS, Azure, GCP, VMware, and other platforms from a single source template. This eliminates configuration drift and ensures that compute instances, virtual desktops, containers, or on-prem VMs are consistently built from the same baseline. For enterprises managing multiple environments, Packer’s multi-builder architecture ensures that every deployment starts from a deterministic, verified image.  Full Infrastructure-as-Code Alignment: Packer templates are declarative and integrate seamlessly into IaC workflows using tools such as Terraform, Ansible, and even cloud-native configuration tools. This yields several architectural advantages: Versioned images: Each new image build is immutable and tied to a version-controlled configuration. Reproducibility: Builds can be recreated deterministically at any time, supporting compliance and audit requirements. Automation-ready: Image creation fits naturally into CI/CD pipelines, enabling continuous delivery of operating system and middleware baselines.  Efficient, Scalable Deployments: Master images generated by Packer accelerate workload provisioning by drastically reducing runtime bootstrap configuration. This leads to: Faster autoscaling responsiveness Reduced risk of configuration failures during provisioning Decreased cloud compute costs due to shorter initialization times Better operational consistency across development, staging, and production These benefits scale exceptionally well for enterprise cloud deployments, VDI environments, and large-scale microservice platforms.  Multi-Cloud Flexibility and Future-Proofing Packer’s provider-agnostic architecture helps organizations avoid lock-in and stay adaptable. A single template can create AMIs, Azure Managed Images, GCP images, Docker images, and VMware templates. This uniformity supports long-term cloud strategy, modernization efforts, and disaster recovery architectures that depend on identical images across regions and providers.  Integration with Modern Platform Engineering Practices Packer integrates tightly with modern platform engineering toolchains: Terraform for downstream deployment Vault for secure secrets handling GitHub Actions, GitLab CI, Azure DevOps for automated pipelines This positions Packer as a first-class component in automated golden image pipelines, enabling platform teams to provide self-service, pre-validated, compliant images to developers and operations teams. Packer is a mature, efficient, and cloud-agnostic solution that brings the rigor of Infrastructure-as-Code to master image creation. It ensures repeatability, security, compliance, and operational speed while integrating seamlessly into modern CI/CD and platform engineering ecosystems. For organizations pursuing scalable, automated, and consistent image-based deployments, Packer is not only valid but strategically advantageous. Packer is an open-source tool that automates the creation of machine images for multiple platforms from a single source configuration. It enables developers and DevOps teams to define image-creation workflows in code, resulting in consistent, repeatable, and version-controlled image builds. RedHat AnsibleMany organizations that have matured their Infrastructure-as-Code (IaC) practices increasingly require tooling that not only provisions infrastructure but also configures systems, applications, and operating environments in a controlled, repeatable manner. Ansible is a leading configuration management and automation platform that aligns naturally with IaC principles and complements provisioning tools such as Terraform, or cloud-native IaC engines. Its agentless architecture, declarative playbook model, and extensive ecosystem make it a strategic choice for enterprises seeking predictable, scalable, and compliant post-provision configuration. Ansible provides a declarative, idempotent configuration model that ensures systems converge to a desired state regardless of their current configuration. This makes it ideally suited for environments provisioned using IaC tools, allowing organizations to: Configure compute resources, operating systems, middleware, and applications immediately after provisioning Apply consistent baseline templates across development, staging, and production Avoid configuration drift and reduce manual intervention Enforce standardization across diverse platforms and environments Because Ansible uses standard protocols (SSH, WinRM, API calls) and does not require agents, it can manage virtually any resource created by IaC tooling with minimal setup. Ansible excels by enabling: OS configuration (packages, services, permissions, patching) Deployment and maintenance of middleware or applications Secure configuration of agents (monitoring, logging, EDR, secrets managers) Post-provision checks and compliance enforcement Continuous updates and lifecycle operations long after initial provisioning Ansible delivers operational benefits such as: Idempotent reruns that safely correct drift Automated configuration updates Consistent rollout of application versions or patches Reduced human error and faster recovery Centralized configuration logic rather than ad-hoc scripts These capabilities support long-term environment stability, which is often a weak point in IaC deployments if configuration is not managed separately. Better togetherTerraform and Ansible serve complementary roles in modern Infrastructure-as-Code (IaC) architectures. Terraform excels at provisioning infrastructure resources—compute, networks, storage, identity, and cloud services—while Ansible specializes in configuring and managing the software, applications, and operating system state on those resources once they are created. When combined, they form a robust, scalable, and fully automated delivery pipeline that allows organizations to provision secure, consistent environments and configure them in a modular, policy-driven manner. Terraform is primarily designed for infrastructure lifecycle management, while Ansible is engineered for post-provisioning configuration. This clean separation ensures: Terraform builds and destroys infrastructure predictably Ansible configures servers, middleware, application stacks, and OS settings Changes to infrastructure and configuration are maintained in distinct codebases, improving clarity and reducing risk IT can version, audit, and test both layers independently This division of responsibilities aligns with architectural patterns used in platform engineering and regulated environments, where infrastructure and system configuration often have separate compliance requirements. Schematic workflow in this guide:  PrerequisitesLet´s have a short overview of all the prerequisites needed: IaC Deployment MachineWe assume you have a dedicated machine or a cluster running all Automation components. If all IaC frameworks reside on the same machine/cluster, communication between them is less error-prone. We recommend an Ubuntu-based machine for various reasons: An Ubuntu VM is one of the most reliable, flexible, and well-supported environments for running IaC tools like Terraform, Ansible, Jenkins, and Packer, thanks to its stability, compatibility, and strong community support. Most IaC tools (Terraform, Ansible, Packer) are developed and tested primarily on Linux.  Note: You can find a guide to create such an all-in-one deployment machine on Citrix TechZone. If you use the Ubuntu-based IaC machine, all Terraform, Packer, and Ansible snippets are running on Ubuntu. All local-exec provisioners also run on the Ubuntu machine: Example of a local-exec provisioner: ##### Connect to Ansible Interpreter and run CreateDatabase-Playbook on DDCs
resource "null_resource" "RunCreateDatabasePlaybook" {
  depends_on = [time_sleep.wait_10_seconds]

  provisioner "local-exec" {
    working_dir = "/etc/ansible"
    command     = var.TACG-TMM-XS-CL-Ansible-CreateDatabase-CMD
  }
} Caution: Due to current limitations in Citrix StoreFront´s automation capabilities, you must run the automated deployment and configuration of Citrix StoreFront™ from a domain-joined Windows machine with Terraform installed.  Important: Suppose you decide to switch to running all Terraform snippets on a Windows machine. In that case, you need to change the provisioner types and the code from local-exec provisioners to remote-exec provisioners, as Ansible cannot run on a Windows machine! Example of the local-exec provisioner mentioned above - converted to a remote-exec provisioner: ##### Connect to Ansible Interpreter and run CreateDatabase-Playbook on DDCs
resource "null_resource" "RunCreateDatabasePlaybook" {
  depends_on = [time_sleep.wait_10_seconds]
  
 connection {
    type            = var.Provisioner_Type
    user            = var.Provisioner_SecureAdmin-Username
    password        = var.Provisioner_SecureAdmin-Password
    host            = var.Provisioner_IP1
    port            = var.Provisioner_Port
    https           = var.Provisioner_HTTPS
    timeout         = var.Provisioner_Timeout
  }

  provisioner "remote-exec" {
    inline     = [ var.TACG-TMM-XS-CL-Ansible-CreateDatabase-CMD ]
  }
}You can find instructions for setting up the necessary WinRM/SSH configuration later in this guide. XenServer 8.4We assume that you already have a XenServer 8.4 cluster in place, which will serve as the Hypervisor host for all required virtual machines. Important: There is no need to have template VMs or template snapshots configured if you want to follow our IaC-based approach to create and configure all needed Master images using Packer. If you do not want to use Packer but have already configured Master images, you can use them and reference them in the corresponding Terraform variables later.  Communication Flow between Terraform, Ansible, and the Target MachinesAll communication between Terraform, Packer, and Ansible on the IaC-VM and the target VMs on the XenServer cluster uses SSH and WinRM. WinRM (Windows Remote Management) is a Microsoft protocol that enables remote management of Windows systems by allowing access to remote computers to perform management tasks. It is Microsoft's implementation of the WS-Management (Web Services Management Protocol) standard. It often uses SOAP (Simple Object Access Protocol) for communication over HTTP or HTTPS, using ports 5985 (HTTP) and 5986 (HTTPS) by default. WinRM provides the basis for remote management with PowerShell and is used for tasks such as running remote scripts, automating, configuring, and performing system inventory. Therefore, WinRM and SSH must be configured correctly. Important: We emphasize ensuring the communication flow works as intended from the outset of the pre-domain-joined stage; otherwise, Terraform and Ansible will fail to configure the VMs.  During the pre-domain-join stage, you can configure these settings using the local Group Policy Editor on the VM or in the autounattend.xml file as we do in our Packer scripts. After a successful domain join, you can set all relevant settings using GPOs. To provide maximum flexibility and a fallback mechanism, you can use both listeners - HTTP and HTTPS - but we recommend using the secure transport whenever and wherever possible. You can check all available listeners using PowerShell: PS &gt; winrm e winrm/config/listener
Listener [Source="GPO"]
    Address = *
    Transport = HTTP
    Port = 5985
    Hostname
    Enabled = true
    URLPrefix = wsman
    CertificateThumbprint
    ListeningOn = 10.10.119.11, 127.0.0.1, ::1, fe80::287c:216e:9bd9:18e5%7

Listener [Source="GPO"]
    Address = *
    Transport = HTTPS
    Port = 5986
    Hostname = tf-cvad-ddc1.the-austrian-citrix-guy.at
    Enabled = true
    URLPrefix = wsman
    CertificateThumbprint = e3bf89dd77c95dd6c0f1b94480ea63782df4279e
    ListeningOn = 10.10.119.11, 127.0.0.1, ::1, fe80::287c:216e:9bd9:18e5%7To check the communication, you can use a Terraform snippet: Unsecure Communication - connecting to the HTTP listener: resource "null_resource" "UploadTestFileToIP1" {
 connection {
    type            = var.Provisioner_Type
    user            = var.Provisioner_Admin-Username
    password        = var.Provisioner_Admin-Password
    host            = var.Provisioner_IP1
    timeout         = var.Provisioner_Timeout
  }

###### Upload Test script to IP1
  provisioner "file" {
    source      = "${path.module}/DATA/Test-Script.ps1"
    destination = "c:/temp/Test-Script.ps1"
  } 
}

resource "null_resource" "InstallonIP1" {
depends_on = [ null_resource.UploadTestFileToIP1 ]
connection {
    type            = var.Provisioner_Type
    user            = var.Provisioner_Admin-Username
    password        = var.Provisioner_Admin-Password
    host            = var.Provisioner_IP1
    timeout         = var.Provisioner_Timeout
  }
 provisioner "remote-exec" {
    inline = [
      "powershell -File c:/temp/Test-Script.ps1"
    ]
  }
}Output: ...
null_resource.InstallonIP1: Creating...
null_resource.InstallonIP1: Provisioning with 'remote-exec'...
null_resource.InstallonIP1 (remote-exec): Connecting to remote host via WinRM...
null_resource.InstallonIP1 (remote-exec):   Host: tf-cvad-ddc1
null_resource.InstallonIP1 (remote-exec):   Port: 5985
null_resource.InstallonIP1 (remote-exec):   User: administrator
null_resource.InstallonIP1 (remote-exec):   Password: true
null_resource.InstallonIP1 (remote-exec):   HTTPS: false
null_resource.InstallonIP1 (remote-exec):   Insecure: false
null_resource.InstallonIP1 (remote-exec):   NTLM: false
null_resource.InstallonIP1 (remote-exec):   CACert: false
null_resource.InstallonIP1 (remote-exec): Connected!
... Secure Communication - connecting to the HTTPS listener: resource "null_resource" "UploadTestFileToIP1" {
 connection {
    type            = var.Provisioner_Type
    user            = var.Provisioner_SecureAdmin-Username
    password        = var.Provisioner_SecureAdmin-Password
    host            = var.Provisioner_IP1
    port            = var.Provisioner_Port
    https           = var.Provisioner_HTTPS
    timeout         = var.Provisioner_Timeout
  }

###### Upload Test script to IP1
  provisioner "file" {
    source      = "${path.module}/DATA/Test-Script.ps1"
    destination = "c:/temp/Test-Script.ps1"
    
  } 
}

resource "null_resource" "InstallonIP1" {
depends_on = [ null_resource.UploadTestFileToIP1 ]
connection {
    type            = var.Provisioner_Type
    user            = var.Provisioner_SecureAdmin-Username
    password        = var.Provisioner_SecureAdmin-Password
    host            = var.Provisioner_IP1
    port            = var.Provisioner_Port
    https           = var.Provisioner_HTTPS
    timeout         = var.Provisioner_Timeout

  }
 provisioner "remote-exec" {
    inline = [
      "powershell -File c:/temp/Test-Script.ps1"
    ]
  }
}Output: ...
null_resource.InstallonIP1: Creating...
null_resource.InstallonIP1: Provisioning with 'remote-exec'...
null_resource.InstallonIP1 (remote-exec): Connecting to remote host via WinRM...
null_resource.InstallonIP1 (remote-exec):   Host: tf-cvad-ddc1
null_resource.InstallonIP1 (remote-exec):   Port: 5986
null_resource.InstallonIP1 (remote-exec):   User: svc_ansible
null_resource.InstallonIP1 (remote-exec):   Password: true
null_resource.InstallonIP1 (remote-exec):   HTTPS: true
null_resource.InstallonIP1 (remote-exec):   Insecure: false
null_resource.InstallonIP1 (remote-exec):   NTLM: false
null_resource.InstallonIP1 (remote-exec):   CACert: false
null_resource.InstallonIP1 (remote-exec): Connected!
...Important: Configuring secure communication, especially for Ansible on the Ubuntu machine, is a complex task that involves certificates and user mappings – you can find many comprehensive configuration guides online. A good start can be found in Ansible´s own documentation.  Active Directory DomainAn existing AD domain is imperative for a successful CVAD deployment. We assume you have a domain with all the needed configurations in place.  Creation of the CVAD Environment using IaCAs all prerequisites are met, we can now start with the workflow to create our Citrix Virtual Apps and Desktops deployment. As already stated, the workflow is divided into blocks that must be started in a specific, predetermined order: Caution: As already mentioned above, Module 9 - Using Terraform to create the StoreFront deployment and all its necessities, must be run on a Windows-based, domain-joined VM to be supported. This is due to limitations in StoreFront´s automation capabilities.  Using Packer to create the Master Images  In this IaC block, we use Packer to create the Master Images for the VDI machines and the Windows Servers.  Using Terraform to create the needed VMs based on the Master Images Terraform will create these VMs: a.       Two Windows Server 2025-based VMs for the Delivery Controllers b.       One Windows Server 2025-based VM for the StoreFront™ server c.       One Windows Server 2025-based VM for the WebStudio™ server d.       One Windows 11-based VM as a VDI Worker  Using Terraform and Ansible to configure the VMs Terraform and Ansible take further configuration steps: a.       Change the IP addresses to known IPs b.       Change the Ansible configuration file to reflect the IP changes c.       Put the VMs into the AD domain  Using Terraform and Ansible to deploy the Delivery Controllers Terraform and Ansible take further configuration steps: a.       Mount the CVAD ISO installer b.       Copy the publicly-signed SSL certificate to the DDCs c.       Install all required server roles and features before installing the DDCs d.       Change some necessary registry settings  Using Terraform and Ansible to deploy the StoreFront server Terraform and Ansible take further configuration steps: a.       Mount the CVAD ISO installer b.       Copy the publicly-signed SSL certificate to the StoreFront server c.       Install all required server roles and features before installing the StoreFront d.       Change some necessary registry settings  Using Terraform and Ansible to deploy the WebStudio server Terraform and Ansible take further configuration steps: a.       Mount the CVAD ISO installer b.       Copy the publicly-signed SSL certificate to the WebStudio server c.       Install all required server roles and features before installing the WebStudio server d.       Change some necessary registry settings e.       Configure WebStudio server as proxy for the DDCs  Using Terraform and Ansible to change the SSL Bindings Terraform and Ansible take further configuration steps: a.       Change the SSL bindings on all servers and listeners to achieve complete HTTPS communication  Using Terraform and Ansible to create the CVAD site and all its necessities Terraform and Ansible take further configuration steps: a.       Create all databases b.       Create the site c.       Add the second DDC to the site d.       Configure additional site configurations e.       Configure a dedicated Administrators group f.        Configure the licensing  Using Terraform to create the StoreFront deployment and all its necessities Terraform takes further configuration steps: a.       Create the StoreFront deployment b.       Create a StoreFront Authentication service c.       Create a StoreFront Store service d.       Create a StoreFront WebReceiver service  Using Terraform to create all CVAD entities and all their necessities Terraform takes further configuration steps: a.       Create a StoreFront Server object b.       Create a Hypervisor connection c.       Create a Hypervisor connection resource pool d.       Create a Machine Catalog e.       Create a Delivery Group f.        Create a Policy Set and its policies   Note: All Ansible playbooks are directly called from Terraform. Terraform stops the workflow if an error occurs by checking Ansible's return code.  Important: All shown Terraform codes, Ansible Playbooks, and PowerShell scripts are tailored exclusively for the specific environment used for this guide. You need to adjust all settings, values, snippets, etc., in the most suitable way tailored to your requirements, regulations, and existing environments. All settings, scripts, and code examples listed in this document are intended only to illustrate the possibilities and serve as a basis for your own deployment. Using all the provided code snippets is at your own risk – please read the disclaimer at the end of the document for further information. Terraform verifies the successful execution of each Ansible playbook. If an error occurs, Terraform will halt the deployment and write out the failed Playbook: Example snippet: ##### Create Databases Start
##### Connect to Ansible Interpreter and run CreateDatabase-Playbook on DDCs
resource "null_resource" "RunCreateDatabasePlaybook" {
  depends_on = [time_sleep.wait_10_seconds]

  provisioner "local-exec" {
    working_dir = "/etc/ansible"
    command     = var.TACG-TMM-XS-CL-Ansible-CreateDatabase-CMD
  }
}

##### Check Ansible Return Code
data "external" "CheckAnsibleReturnCodeCDB" {
  depends_on = [null_resource.RunCreateDatabasePlaybook]
  program    = ["bash", "/etc/ansible/assets/CheckAnsibleReturnCode.sh", "/etc/ansible/assets/ansible_exit_code.txt", "0"]
}

##### Terminate if Return Code is not 0
resource "null_resource" "IfExitCodeIsNotZeroThenHaltCDB" {
  depends_on = [data.external.CheckAnsibleReturnCodeCDB]
  provisioner "local-exec" {
    interpreter = ["/bin/bash", "-c"]
    command     = "[ \"${data.external.CheckAnsibleReturnCodeCDB.result.found}\" = \"1\" ] &amp;&amp; echo \"Create Databases - Everything OK\" || { echo \"Deploying the CVAD Databases failed. Please review the settings. Halting Terraform.\"; exit 1; }"
  }
}
In this example, Terraform runs the Ansible playbook to create the CVAD databases. If an error occurs, Terraform writes an error message indicating that deploying the CVAD databases failed. You would find more information in the LogFile directory on the corresponding server. Terraform then stops the further execution of the snippets.  Part 1: Using Packer to create the Master ImagesHashicorp Packer is our standard framework for creating, maintaining, and deploying Master Images using IaC.  Our XenServer cluster has no VMs before running the snippet:  UPDATE: We changed the used Packer plugin: Caution: XenServer currently lacks an official Packer plugin. In this guide, we use an open-source Tech Preview plugin maintained by my Citrite colleagues Rody Kossen and Marshall Wu to showcase the feasibility of end-to-end IaC. You can find more about this TP on GitHub: https://github.com/xenserver/packer-plugin-xenserver/tree/feature-rodyk-revamp Use the plugin at your own risk – please read the disclaimer at the end of the document for further information. There is NO warranty that it will work as intended – we cannot provide any support. You can find a more thorough example of using a supported Packer deployment in our TechZone guide "Using Infrastructure-as-Code for deploying Citrix® Virtual Apps and Desktops™ 2507 LTSR on vSphere™ 8". Before you can use the mentioned plugin, you need to compile it: Here´s a guide for compiling it – we use our Ubuntu IaC machine for compilation. Before compilation, we need to ensure that these prerequisites are installed: Packer – is installed. Golang 1.24.1 – is not installed, need to install Step 1: Install Golang 1.24.1 azadmin@tacg-cicd:~$ wget https://go.dev/dl/go1.24.1.linux-amd64.tar.gz
--2025-11-21 09:32:35--  https://go.dev/dl/go1.24.1.linux-amd64.tar.gz
Resolving go.dev (go.dev)... 216.239.36.21, 216.239.32.21, 216.239.34.21, ...
Connecting to go.dev (go.dev)|216.239.36.21|:443... connected.
HTTP request sent, awaiting response... 302 Found
Location: https://dl.google.com/go/go1.24.1.linux-amd64.tar.gz [following]
--2025-11-21 09:32:36--  https://dl.google.com/go/go1.24.1.linux-amd64.tar.gz
Resolving dl.google.com (dl.google.com)... 142.251.39.78, 2a00:1450:400d:80e::200e
Connecting to dl.google.com (dl.google.com)|142.251.39.78|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 78503123 (75M) [application/x-gzip]
Saving to: ‘go1.24.1.linux-amd64.tar.gz’

go1.24.1.linux-amd64.tar.gz   100%[=================================================&gt;]  74.87M  29.5MB/s    in 2.5s

2025-11-21 09:32:38 (29.5 MB/s) - ‘go1.24.1.linux-amd64.tar.gz’ saved [78503123/78503123]

azadmin@tacg-cicd:~$ 
azadmin@tacg-cicd:~$ sudo tar -C /usr/local -xzf go1.24.1.linux-amd64.tar.gz
azadmin@tacg-cicd:~$ 
azadmin@tacg-cicd:~$ ls -l /usr/local/go*
total 76
drwxr-xr-x  2 root root  4096 Feb 27  2025 api
drwxr-xr-x  2 root root  4096 Feb 27  2025 bin
-rw-r--r--  1 root root    52 Feb 27  2025 codereview.cfg
-rw-r--r--  1 root root  1337 Feb 27  2025 CONTRIBUTING.md
drwxr-xr-x  3 root root  4096 Feb 27  2025 doc
-rw-r--r--  1 root root   505 Feb 27  2025 go.env
drwxr-xr-x  5 root root  4096 Feb 27  2025 lib
-rw-r--r--  1 root root  1453 Feb 27  2025 LICENSE
drwxr-xr-x  8 root root  4096 Feb 27  2025 misc
-rw-r--r--  1 root root  1303 Feb 27  2025 PATENTS
drwxr-xr-x  4 root root  4096 Feb 27  2025 pkg
-rw-r--r--  1 root root  1454 Feb 27  2025 README.md
-rw-r--r--  1 root root   426 Feb 27  2025 SECURITY.md
drwxr-xr-x 56 root root  4096 Feb 27  2025 src
drwxr-xr-x 28 root root 16384 Feb 27  2025 test
-rw-r--r--  1 root root    35 Feb 27  2025 VERSION
azadmin@tacg-cicd:~$
azadmin@tacg-cicd:~$ sudo nano ~/.profile
azadmin@tacg-cicd:~$
azadmin@tacg-cicd:~$ source ~/.profile
azadmin@tacg-cicd:~$
azadmin@tacg-cicd:~$ go version
go version go1.24.1 linux/amd64
azadmin@tacg-cicd:~$
Step 2: Download the plugin source from GitHub and place it into a feasible directory We downloaded the source and put it into “packer-plugin-xenserver-feature-rodyk-revamp” azadmin@tacg-cicd:~$ ls
cert_key.pem  Documents                    Music                                         packer-templates  snap
cert.pem      Downloads                    packer-examples-for-vsphere                   Pictures          Templates
Desktop       go1.24.1.linux-amd64.tar.gz  packer-plugin-xenserver-feature-rodyk-revamp  Public            Videos
azadmin@tacg-cicd:~$
Step 3: Compile the plugin azadmin@tacg-cicd:~/packer-plugin-xenserver-feature-rodyk-revamp$ make
builder/xenserver/common/client.go:9:2: xenapi@v0.0.0-00010101000000-000000000000: replacement directory ./goSDK does not exist
make: *** [GNUmakefile:11: build] Error 1

azadmin@tacg-cicd:~/packer-plugin-xenserver-feature-rodyk-revamp$
This error indicates that the XenServer SDK-Go files are missing. We need to download them into a subdirectory to make them accessible for compilation: azadmin@tacg-cicd:~/packer-plugin-xenserver-feature-rodyk-revamp$ wget -P /home/azadmin/Downloads https://downloads.xenserver.com/sdk/25.30.0/XenServer-SDK-25.30.0.zip
--2025-11-21 10:39:31--  https://downloads.xenserver.com/sdk/25.30.0/XenServer-SDK-25.30.0.zip
Resolving downloads.xenserver.com (downloads.xenserver.com)... 13.107.226.44, 13.107.253.44, 2620:1ec:48:1::44, ...
Connecting to downloads.xenserver.com (downloads.xenserver.com)|13.107.226.44|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 19568538 (19M) [application/zip]
Saving to: ‘/home/azadmin/Downloads/XenServer-SDK-25.30.0.zip’

XenServer-SDK-25.30.0.zip                          100%[================================================================================================================&gt;]  18.66M  21.7MB/s    in 0.9s

2025-11-21 10:39:32 (21.7 MB/s) - ‘/home/azadmin/Downloads/XenServer-SDK-25.30.0.zip’ saved [19568538/19568538]

azadmin@tacg-cicd:~/packer-plugin-xenserver-feature-rodyk-revamp$
azadmin@tacg-cicd:~/packer-plugin-xenserver-feature-rodyk-revamp$ mkdir goSDK
azadmin@tacg-cicd:~/packer-plugin-xenserver-feature-rodyk-revamp$ 
azadmin@tacg-cicd:~/packer-plugin-xenserver-feature-rodyk-revamp/goSDK$ unzip /home/azadmin/Downloads/XenServer-SDK-25.30.0.zip -d /home/azadmin/Downloads/XenServer-SDK-25.30.0
...
azadmin@tacg-cicd:~/packer-plugin-xenserver-feature-rodyk-revamp$ 
azadmin@tacg-cicd:~/packer-plugin-xenserver-feature-rodyk-revamp$ cp -r /home/azadmin/Downloads/XenServer-SDK-25.30.0/XenServer-SDK/XenServerGo/src/. /home/azadmin/packer-plugin-xenserver-feature-rodyk-re
vamp/goSDK
azadmin@tacg-cicd:~/packer-plugin-xenserver-feature-rodyk-revamp$
azadmin@tacg-cicd:~/packer-plugin-xenserver-feature-rodyk-revamp$ cd goSDK
azadmin@tacg-cicd:~/packer-plugin-xenserver-feature-rodyk-revamp/goSDK$ ls
api_errors.go    cluster_host.go    enums.go           host_driver.go     network_sriov.go  pool_patch.go         repository.go      subject.go      vdi_nbd_server_info.go  vm_group.go
api_messages.go  console.go         event.go           host.go            observer.go       pool_update.go        role.go            task.go         vgpu.go                 vm_guest_metrics.go
api_versions.go  convert.go         export_test.go     host_metrics.go    pbd.go            probe_result.go       sdn_controller.go  tunnel.go       vgpu_type.go            vm_metrics.go
auth.go          convert_test.go    feature.go         host_patch.go      pci.go            pusb.go               secret.go          usb_group.go    vif.go                  vmpp.go
blob.go          crashdump.go       go.mod             jsonrpc_client.go  pgpu.go           pvs_cache_storage.go  session.go         user.go         vif_metrics.go          vmss.go
bond.go          data_source.go     gpu_group.go       lvhd.go            pif.go            pvs_proxy.go          sm.go              vbd.go          vlan.go                 vtpm.go
certificate.go   driver_variant.go  host_cpu.go        message.go         pif_metrics.go    pvs_server.go         sr.go              vbd_metrics.go  vm_appliance.go         vusb.go
cluster.go       dr_task.go         host_crashdump.go  network.go         pool.go           pvs_site.go           sr_stat.go         vdi.go          vm.go
azadmin@tacg-cicd:~/packer-plugin-xenserver-feature-rodyk-revamp/goSDK$
As the required SDK files are now in place, we can start the compilation again: azadmin@tacg-cicd:~/packer-plugin-xenserver-feature-rodyk-revamp$ make
go: downloading github.com/hashicorp/packer-plugin-sdk v0.6.2
go: downloading github.com/hashicorp/hcl/v2 v2.24.0
go: downloading github.com/hashicorp/hcl v1.0.0
...
go: downloading github.com/golang/protobuf v1.5.3
go: downloading github.com/mattn/go-colorable v0.1.13
go: downloading github.com/hashicorp/go-immutable-radix v1.3.1
go: downloading golang.org/x/sync v0.14.0
go: downloading github.com/hashicorp/golang-lru v0.5.4
azadmin@tacg-cicd:~/packer-plugin-xenserver-feature-rodyk-revamp$
azadmin@tacg-cicd:~/packer-plugin-xenserver-feature-rodyk-revamp$ ls
builder  docs  examples  GNUmakefile  go.mod  goSDK  go.sum  LICENSE  main.go  packer-plugin-xenserver  README.md  version
azadmin@tacg-cicd:~/packer-plugin-xenserver-feature-rodyk-revamp$The compilation was successful, now we need to install the plugin in Packer. Step 4: Install the plugin azadmin@tacg-cicd:~/packer-plugin-xenserver-feature-rodyk-revamp$ sudo packer plugins install --path packer-plugin-xenserver github.com/xenserver/xenserver
Successfully installed plugin github.com/xenserver/xenserver from /home/azadmin/packer-plugin-xenserver-feature-rodyk-revamp/packer-plugin-xenserver to /root/.config/packer/plugins/github.com/xenserver/xenserver/packer-plugin-xenserver_v0.9.1_x5.0_linux_amd64
azadmin@tacg-cicd:~/packer-plugin-xenserver-feature-rodyk-revamp$ 
azadmin@tacg-cicd:~/packer-plugin-xenserver-feature-rodyk-revamp$ sudo packer plugins installed
/root/.config/packer/plugins/github.com/xenserver/xenserver/packer-plugin-xenserver_v0.9.1_x5.0_linux_amd64
azadmin@tacg-cicd:~/packer-plugin-xenserver-feature-rodyk-revamp$
Installation is complete, let´s create some VMs now. For a detailed overview of the builder, please look at the plugin´s documentation. Example Packer HCL file for deploying a Windows Server 2025-VM on XenServer 8: packer {
  required_plugins {
   xenserver= {
      version = "&gt;= v0.1.0"
      source = "github.com/xenserver/xenserver"
    }
  }
}

source "xenserver-iso" "XSTEST" {

    iso_checksum_type           = "none"
	iso_name					= var.os_iso_path
    sr_iso_name                 = var.sr_iso_name
    sr_name                     = var.sr_name
    tools_iso_name              = var.tools_iso_name

    remote_host                 = var.remote_host
    remote_password             = var.remote_password
    remote_username             = var.remote_username

    vm_name                     = var.vm_name
    vm_description              = var.vm_description
    vm_memory                   = var.memory
    disk_size                   = var.disksize
	vcpus				        = var.cpu
	cores_per_socket			= var.cpu
	network_names				= ["${var.virtual_network_name}"]
	firmware                    = var.firmware
    secure_boot 				= var.secure_boot
    vTPM                        = var.vtpm
    vgpu_profile                = var.vgpu_profile

	clone_template				= var.clone_template

    cd_files                	= ["W2K25AutoUnattendInclWinRM.xml"]

	ssh_username            	= var.ssh_username
	ssh_password            	= var.ssh_password
	ssh_wait_timeout        	= "60000s"
	
    boot_command = [
        "&lt;spacebar&gt;&lt;wait2&gt;&lt;spacebar&gt;"
    ]
    boot_wait                   = "1s"
	
    #Export Options
	format 						= "xva"
    keep_vm 					= "on_success"

    communicator                = "winrm"
    winrm_insecure              = true
    winrm_password              = var.winrm_password
    winrm_timeout               = "5m"
    winrm_use_ssl               = false
    winrm_username              = var.winrm_username
}

build {
   sources = ["xenserver-iso.windows"]
  
   provisioner "powershell" {
    inline = ["Write-Host 'Installing updates and XenServer Tools...'", "Start-Process msiexec.exe -ArgumentList '/i D:\\xensetup\\managementagentx64.msi /qn' -Wait”]
  }
}
In this example, we use a modified autounattend.xml file to configure the Windows VM during creation: &lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;unattend xmlns="urn:schemas-microsoft-com:unattend"&gt;
  &lt;settings pass="windowsPE"&gt;
    &lt;component name="Microsoft-Windows-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"&gt;
      &lt;DiskConfiguration&gt;
        &lt;Disk wcm:action="add"&gt;
          &lt;DiskID&gt;0&lt;/DiskID&gt;
          &lt;WillWipeDisk&gt;true&lt;/WillWipeDisk&gt;
          &lt;CreatePartitions&gt;
            &lt;CreatePartition wcm:action="add"&gt;
              &lt;Order&gt;1&lt;/Order&gt;
              &lt;Type&gt;Primary&lt;/Type&gt;
              &lt;Size&gt;100&lt;/Size&gt;
            &lt;/CreatePartition&gt;
            &lt;CreatePartition wcm:action="add"&gt;
              &lt;Order&gt;2&lt;/Order&gt;
              &lt;Type&gt;Primary&lt;/Type&gt;
              &lt;Extend&gt;true&lt;/Extend&gt;
            &lt;/CreatePartition&gt;
          &lt;/CreatePartitions&gt;
          &lt;ModifyPartitions&gt;
            &lt;ModifyPartition wcm:action="add"&gt;
              &lt;Order&gt;1&lt;/Order&gt;
              &lt;PartitionID&gt;1&lt;/PartitionID&gt;
              &lt;Active&gt;true&lt;/Active&gt;
              &lt;Label&gt;System&lt;/Label&gt;
              &lt;Format&gt;NTFS&lt;/Format&gt;
            &lt;/ModifyPartition&gt;
            &lt;ModifyPartition wcm:action="add"&gt;
              &lt;Order&gt;2&lt;/Order&gt;
              &lt;PartitionID&gt;2&lt;/PartitionID&gt;
              &lt;Label&gt;Windows&lt;/Label&gt;
              &lt;Format&gt;NTFS&lt;/Format&gt;
              &lt;Letter&gt;C&lt;/Letter&gt;
            &lt;/ModifyPartition&gt;
          &lt;/ModifyPartitions&gt;
        &lt;/Disk&gt;
        &lt;WillShowUI&gt;OnError&lt;/WillShowUI&gt;
      &lt;/DiskConfiguration&gt;

      &lt;ImageInstall&gt;
        &lt;OSImage&gt;
          &lt;InstallFrom&gt;
            &lt;MetaData wcm:action="add"&gt;
              &lt;Key&gt;/IMAGE/INDEX&lt;/Key&gt;
              &lt;Value&gt;1&lt;/Value&gt;
            &lt;/MetaData&gt;
          &lt;/InstallFrom&gt;
          &lt;InstallTo&gt;
            &lt;DiskID&gt;0&lt;/DiskID&gt;
            &lt;PartitionID&gt;2&lt;/PartitionID&gt;
          &lt;/InstallTo&gt;
        &lt;/OSImage&gt;
      &lt;/ImageInstall&gt;

      &lt;UserData&gt;
        &lt;ProductKey&gt;
          &lt;Key&gt;&lt;/Key&gt;
          &lt;WillShowUI&gt;Never&lt;/WillShowUI&gt;
        &lt;/ProductKey&gt;
        &lt;AcceptEula&gt;true&lt;/AcceptEula&gt;
        &lt;FullName&gt;Packer Builder&lt;/FullName&gt;
        &lt;Organization&gt;ExampleCorp&lt;/Organization&gt;
      &lt;/UserData&gt;
    &lt;/component&gt;
  &lt;/settings&gt;

  &lt;settings pass="specialize"&gt;
    &lt;component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"&gt;
      &lt;ComputerName&gt;WIN2025BASE&lt;/ComputerName&gt;
      &lt;TimeZone&gt;UTC&lt;/TimeZone&gt;
    &lt;/component&gt;

    &lt;component name="Microsoft-Windows-Deployment" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"&gt;
      &lt;RunSynchronous&gt;
        &lt;RunSynchronousCommand&gt;
          &lt;Order&gt;1&lt;/Order&gt;
          &lt;Path&gt;cmd /c winrm quickconfig -quiet&lt;/Path&gt;
          &lt;Description&gt;Enable WinRM&lt;/Description&gt;
        &lt;/RunSynchronousCommand&gt;
        &lt;RunSynchronousCommand&gt;
          &lt;Order&gt;2&lt;/Order&gt;
          &lt;Path&gt;cmd /c winrm set winrm/config/service/auth @{Basic="true"}&lt;/Path&gt;
          &lt;Description&gt;Enable Basic Auth&lt;/Description&gt;
        &lt;/RunSynchronousCommand&gt;
        &lt;RunSynchronousCommand&gt;
          &lt;Order&gt;3&lt;/Order&gt;
          &lt;Path&gt;cmd /c winrm set winrm/config/service @{AllowUnencrypted="true"}&lt;/Path&gt;
          &lt;Description&gt;Allow Unencrypted&lt;/Description&gt;
        &lt;/RunSynchronousCommand&gt;
        &lt;RunSynchronousCommand&gt;
          &lt;Order&gt;4&lt;/Order&gt;
          &lt;Path&gt;cmd /c netsh advfirewall set allprofiles state off&lt;/Path&gt;
          &lt;Description&gt;Disable Firewall&lt;/Description&gt;
        &lt;/RunSynchronousCommand&gt;
        &lt;RunSynchronousCommand&gt;
          &lt;Order&gt;5&lt;/Order&gt;
          &lt;Path&gt;cmd /c winrm set winrm/config/client @{TrustedHosts="*"}&lt;/Path&gt;
          &lt;Description&gt;Disable Firewall&lt;/Description&gt;
        &lt;/RunSynchronousCommand&gt;
      &lt;/RunSynchronous&gt;
    &lt;/component&gt;
  &lt;/settings&gt;

   &lt;settings pass="oobeSystem"&gt;
    &lt;component name="Microsoft-Windows-International-Core" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"&gt;
      &lt;InputLocale&gt;de-AT&lt;/InputLocale&gt;
      &lt;SystemLocale&gt;en-US&lt;/SystemLocale&gt;
      &lt;UILanguage&gt;en-US&lt;/UILanguage&gt;
      &lt;UserLocale&gt;de-AT&lt;/UserLocale&gt;
    &lt;/component&gt;
    &lt;component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"&gt;
      &lt;OOBE&gt;
        &lt;HideEULAPage&gt;true&lt;/HideEULAPage&gt;
        &lt;NetworkLocation&gt;Work&lt;/NetworkLocation&gt;
        &lt;ProtectYourPC&gt;3&lt;/ProtectYourPC&gt;
      &lt;/OOBE&gt;
      &lt;UserAccounts&gt;
        &lt;AdministratorPassword&gt;
          &lt;Value&gt;!XxXxXxXxXxXxXxXxX!&lt;/Value&gt;
          &lt;PlainText&gt;true&lt;/PlainText&gt;
        &lt;/AdministratorPassword&gt;
      &lt;/UserAccounts&gt;
      &lt;AutoLogon&gt;
        &lt;Username&gt;Administrator&lt;/Username&gt;
        &lt;Password&gt;
          &lt;Value&gt;!XxXxXxXxXxXxXxXxX!&lt;/Value&gt;
          &lt;PlainText&gt;true&lt;/PlainText&gt;
        &lt;/Password&gt;
        &lt;Enabled&gt;true&lt;/Enabled&gt;
        &lt;LogonCount&gt;1&lt;/LogonCount&gt;
      &lt;/AutoLogon&gt;

      &lt;FirstLogonCommands&gt;
      &lt;/FirstLogonCommands&gt;
      
    &lt;/component&gt;
  &lt;/settings&gt;

  &lt;cpi:offlineImage cpi:source="wim://wimfile/install.wim#Windows Server 2025 SERVERSTANDARD" xmlns:cpi="urn:schemas-microsoft-com:cpi" /&gt;
&lt;/unattend&gt;
As the syntax of the autounattend.xml is very sensitive to errors, we use a self-written configuration tool to create the autounattend.xml more comfortably:  Running the Packer IaC snippet should create a XenServer XVA that can then be used as a Master Image. azadmin@tacg-cicd:~$ packer build packer-tmpl-w2k25-xs.pkr.hcl
xenserver-iso.XSTEST: output will be in this color.

==&gt; xenserver-iso.XSTEST: XAPI client session established
==&gt; xenserver-iso.XSTEST: Retrieving ISO
==&gt; xenserver-iso.XSTEST: Trying http://10.10.100.1/iso/windows_server_2025.iso
==&gt; xenserver-iso.XSTEST: Trying http://10.10.100.1/iso/windows_server_2025.iso
==&gt; xenserver-iso.XSTEST: http://10.10.100.1/iso/windows_server_2025.iso =&gt; C:\__PPMM\_PACKER\_XENSERVER\packer_cache\4282f11098333441b4a8c39028041a36490797cb
==&gt; xenserver-iso.XSTEST: Creating CD disk...
    xenserver-iso.XSTEST: OSCDIMG 2.56 CD-ROM and DVD-ROM Premastering Utility
    xenserver-iso.XSTEST: Copyright (C) Microsoft, 1993-2012. All rights reserved.
    xenserver-iso.XSTEST: Licensed only for producing Microsoft authorized content.
    xenserver-iso.XSTEST: Scanning source tree
    xenserver-iso.XSTEST: Scanning source tree complete (1 files in 1 directories)
    xenserver-iso.XSTEST: Computing directory information complete
    xenserver-iso.XSTEST: Image file is 57344 bytes (before optimization)
    xenserver-iso.XSTEST: Writing 1 files in 1 directories to tmp/packer1625305315.iso
    xenserver-iso.XSTEST: Storage optimization saved 0 files, 0 bytes (0% of image)
    xenserver-iso.XSTEST: After optimization, image file is 57344 bytes
    xenserver-iso.XSTEST: Done.
    xenserver-iso.XSTEST: 100% complete
    xenserver-iso.XSTEST: Done copying paths from CD_dirs
==&gt; xenserver-iso.XSTEST: Step: Upload VDI 'Packer-CD'
==&gt; xenserver-iso.XSTEST: Step: Found SR for upload 'OpaqueRef:ed24af24-cd3e-4ed0-2303-d885e72a691a'
==&gt; xenserver-iso.XSTEST: PUT 'https://10.10.119.1/import_raw_vdi?session_id=OpaqueRef%3A93b232ae-a9bf-8b83-bb54-9aaf7412b6ba&amp;task_id=OpaqueRef%3A99d66110-5044-37b2-7b21-baa23c54d2e1&amp;vdi=OpaqueRef%3A65a7da41-3aba-e5b1-d1f1-0e5f006286f4'
==&gt; xenserver-iso.XSTEST: Attemping to find VDI 'windows_server_2025.iso'
==&gt; xenserver-iso.XSTEST: Step: Upload VDI 'windows_server_2025.iso'
==&gt; xenserver-iso.XSTEST: Step: Found SR for upload 'OpaqueRef:ed24af24-cd3e-4ed0-2303-d885e72a691a'
==&gt; xenserver-iso.XSTEST: PUT 'https://10.10.119.1/import_raw_vdi?session_id=OpaqueRef%3A93b232ae-a9bf-8b83-bb54-9aaf7412b6ba&amp;task_id=OpaqueRef%3Ac8d26e51-2289-49b5-5363-220848bb4b6c&amp;vdi=OpaqueRef%3A4893a784-2514-12b3-20bf-9880fc5bf3be'
==&gt; xenserver-iso.XSTEST: Step: Create Instance
==&gt; xenserver-iso.XSTEST: Using the following SR for the VM: OpaqueRef:67690a5f-2856-f785-7cac-fb59728d7ef2
==&gt; xenserver-iso.XSTEST: Created instance '2d4f4efc-5a3d-d915-380c-f86b0108eabe'
==&gt; xenserver-iso.XSTEST: Step: Start VM Paused
==&gt; xenserver-iso.XSTEST: Step: Set SSH address to VM host IP
==&gt; xenserver-iso.XSTEST: Set host SSH address to '10.10.119.1'.
==&gt; xenserver-iso.XSTEST: Set host SSH port to 22.
==&gt; xenserver-iso.XSTEST: Unpausing VM 2d4f4efc-5a3d-d915-380c-f86b0108eabe
==&gt; xenserver-iso.XSTEST: Waiting 10s for boot...
==&gt; xenserver-iso.XSTEST: Connecting to VNC over XAPI...
    xenserver-iso.XSTEST: Making HTTP request to initiate VNC connection: CONNECT /console?uuid=6e861319-fb00-a8c8-d607-615b6c4561fe HTTP/1.0
    xenserver-iso.XSTEST: Host: 10.10.119.1
    xenserver-iso.XSTEST: Cookie: session_id=OpaqueRef:93b232ae-a9bf-8b83-bb54-9aaf7412b6ba
    xenserver-iso.XSTEST:
    xenserver-iso.XSTEST:
    xenserver-iso.XSTEST: Received response: HTTP/1.1 200 OK
    xenserver-iso.XSTEST: Connection: keep-alive
    xenserver-iso.XSTEST: Cache-Control: no-cache, no-store
    xenserver-iso.XSTEST:
    xenserver-iso.XSTEST:
    xenserver-iso.XSTEST: Found local IP: 10.10.11.99
==&gt; xenserver-iso.XSTEST: Typing boot commands over VNC...
==&gt; xenserver-iso.XSTEST: Step: Wait for VMs IP to become known to us.
==&gt; xenserver-iso.XSTEST: Successfully installed
==&gt; xenserver-iso.XSTEST: Destroying VDI
==&gt; xenserver-iso.XSTEST: Destroyed VDI 'windows_server_2025.iso'
==&gt; xenserver-iso.XSTEST: Destroyed VDI 'Packer-CD'
==&gt; xenserver-iso.XSTEST: Deleting output directory...
==&gt; xenserver-iso.XSTEST: Closing sessions ....
Build 'xenserver-iso.XSTEST' finished after 43 minutes 52 seconds.

==&gt; Wait completed after 43 minutes 52 seconds

==&gt; Builds finished. The artifacts of successful builds are:
--&gt; ==&gt; xenserver-iso.XSTEST: W2K25.2511

azadmin@tacg-cicd:~$ After a successful run, XenServer should reflect the created VM, and after conversion, the corresponding template:  The exact same process is done to create the Windows 11 Master Image and Template:  Since the Master Images have been created, we can proceed to the next step.  Part 2: Using Terraform to create the needed VMs based on the Master ImagesTerraform now creates all the required Infrastructure VMs for deploying CVAD: Two Windows Server 2025-based VMs for the Delivery Controllers One Windows Server 2025-based VM for the StoreFront server One Windows Server 2025-based VM for the WebStudio server. All needed settings are defined in a dedicated JSON file, where all variables and their values are stored. This allows maximum code flexibility and reusability, as it does not need to be changed when other VM configurations arise. Note: Those are the key principles of IaC – repeatability, reusability, and flexibility. Example of the VM settings in Terraform: {
    "TACG-TMM-XS-CL-VMSR":"local storage",
    "TACG-TMM-XS-CL-ISOSR":"ENTERPRISE ISO",
    "TACG-TMM-XS-CL-NW":"Network 0",
    "TACG-TMM-XS-CL-W2K25-MI":"W2K25.2511",
    "TACG-TMM-XS-CL-VM-DDC1-Name":"VM-TF-TACG-DDC1",
    "TACG-TMM-XS-CL-VM-DDC2-Name":"VM-TF-TACG-DDC2",
    "TACG-TMM-XS-CL-VM-StoreFront-Name":"VM-TF-TACG-SF",
    "TACG-TMM-XS-CL-VM-WebStudio-Name":"VM-TF-TACG-WS",
    "TACG-TMM-XS-CL-VM-DDC1-DiskName":"vdi-tacg-ddc1",
    "TACG-TMM-XS-CL-VM-DDC2-DiskName":"vdi-tacg-ddc2",
    "TACG-TMM-XS-CL-VM-StoreFront-DiskName":"vdi-tacg-sf",
    "TACG-TMM-XS-CL-VM-WebStudio-DiskName":"vdi-tacg-ws",
    "TACG-TMM-XS-CL-VM-DDC1-DiskSize":64424509440,
    "TACG-TMM-XS-CL-VM-DDC2-DiskSize":64424509440,
    "TACG-TMM-XS-CL-VM-StoreFront-DiskSize":64424509440,
    "TACG-TMM-XS-CL-VM-WebStudio-DiskSize":64424509440,
    "TACG-TMM-XS-CL-VM-DDC1-MemSize":8589934592,
    "TACG-TMM-XS-CL-VM-DDC2-MemSize":8589934592,
    "TACG-TMM-XS-CL-VM-StoreFront-MemSize":8589934592,
    "TACG-TMM-XS-CL-VM-WebStudio-MemSize":8589934592,
    "TACG-TMM-XS-CL-VM-DDC1-CPUs":2,
    "TACG-TMM-XS-CL-VM-DDC2-CPUs":2,
    "TACG-TMM-XS-CL-VM-StoreFront-CPUs":2,
    "TACG-TMM-XS-CL-VM-DDC1-Cores":2,
    "TACG-TMM-XS-CL-VM-DDC2-Cores":2,
    "TACG-TMM-XS-CL-VM-StoreFront-Cores":2
}For example, if you want to change the main disk size, you only need to change the corresponding value: From  to  would double the disk size to 128GB. Important: As the current Terraform provider for XenServer does not support joining a newly created VM to an AD domain, this step will be performed in the next block using an Ansible playbook.  Caution: The current version of the Terraform provider (1.0.30) at the time of writing this guide has a bug: The storage name is processed internally only in lowercase; however, if it contains an uppercase letter, an error message is returned.  That is why the code snippets always refer to “local storage” rather than its original name, “Local storage”.  Please rename the storage name you want to use in XenCenter™ to lowercase characters. Example Terraform snippet for VM creation: # Terraform Deployment of Citrix CVAD on XenServer 8 - 25.11 
## Definition of XenServer Provider Variables - not all available variables are defined, only these we use
### Reference https://registry.terraform.io/providers/xenserver/xenserver/latest/docs

## Definition of all required local variables
### Get XenServer-related Data
#### Get the VM SR object
data "xenserver_sr" "vmsr" {
  name_label = var.TACG-TMM-XS-CL-VMSR
}

#### Get the ISO SR object
data "xenserver_sr" "isosr" {
  name_label = var.TACG-TMM-XS-CL-ISOSR
}

#### Get the Network object
data "xenserver_network" "network" {
}

# Create a Windows Server 2025 VM for DDC1 from the custom template
resource "xenserver_vm" "VM-DDC1" {
  name_label            = var.TACG-TMM-XS-CL-VM-DDC1-Name
  template_name         = var.TACG-TMM-XS-CL-W2K25-MI
  static_mem_max        = var.TACG-TMM-XS-CL-VM-DDC1-MemSize
  vcpus                 = var.TACG-TMM-XS-CL-VM-DDC1-CPUs
  cores_per_socket      = var.TACG-TMM-XS-CL-VM-DDC1-Cores
  sr_for_full_disk_copy = data.xenserver_sr.vmsr.data_items[0].uuid

  network_interface = [
    {
      device       = "0"
      network_uuid = data.xenserver_network.network.data_items[0].uuid,
    },
  ]
}

# Create a Windows Server 2025 VM for DDC2 from the custom template
resource "xenserver_vm" "VM-DDC2" {
  name_label            = var.TACG-TMM-XS-CL-VM-DDC2-Name
  template_name         = var.TACG-TMM-XS-CL-W2K25-MI
  static_mem_max        = var.TACG-TMM-XS-CL-VM-DDC2-MemSize
  vcpus                 = var.TACG-TMM-XS-CL-VM-DDC2-CPUs
  cores_per_socket      = var.TACG-TMM-XS-CL-VM-DDC2-Cores
  sr_for_full_disk_copy = data.xenserver_sr.vmsr.data_items[0].uuid

  network_interface = [
    {
      device       = "0"
      network_uuid = data.xenserver_network.network.data_items[0].uuid,
    },
  ]
}

# Create a Windows Server 2025 VM for SF from the custom template
resource "xenserver_vm" "VM-StoreFront" {
  name_label            = var.TACG-TMM-XS-CL-VM-StoreFront-Name
  template_name         = var.TACG-TMM-XS-CL-W2K25-MI
  static_mem_max        = var.TACG-TMM-XS-CL-VM-StoreFront-MemSize
  vcpus                 = var.TACG-TMM-XS-CL-VM-StoreFront-CPUs
  cores_per_socket      = var.TACG-TMM-XS-CL-VM-StoreFront-Cores
  sr_for_full_disk_copy = data.xenserver_sr.vmsr.data_items[0].uuid

  network_interface = [
    {
      device       = "0"
      network_uuid = data.xenserver_network.network.data_items[0].uuid,
    },
  ]
}

# Create a Windows Server 2025 VM for WebStudio from the custom template
resource "xenserver_vm" "VM-WebStudio" {
  name_label            = var.TACG-TMM-XS-CL-VM-WebStudio-Name
  template_name         = var.TACG-TMM-XS-CL-W2K25-MI
  static_mem_max        = var.TACG-TMM-XS-CL-VM-WebStudio-MemSize
  vcpus                 = var.TACG-TMM-XS-CL-VM-StoreFront-CPUs
  cores_per_socket      = var.TACG-TMM-XS-CL-VM-StoreFront-Cores
  sr_for_full_disk_copy = data.xenserver_sr.vmsr.data_items[0].uuid

  network_interface = [
    {
      device       = "0"
      network_uuid = data.xenserver_network.network.data_items[0].uuid,
    },
  ]
}Running this snippet should create all four VMs on XenServer: azadmin@tacg-cicd:/etc/terraform-tmm/CVADXS/_Creation$ terraform apply
data.xenserver_sr.vmsr: Reading...
data.xenserver_sr.isosr: Reading...
data.xenserver_network.network: Reading...
data.xenserver_sr.vmsr: Read complete after 0s
data.xenserver_network.network: Read complete after 0s
data.xenserver_sr.isosr: Read complete after 0s

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:

  # xenserver_vm.VM-DDC1 will be created
  + resource "xenserver_vm" "VM-DDC1" {
      + boot_mode             = (known after apply)
      + boot_order            = (known after apply)
      + cdrom                 = "Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso"
      + check_ip_timeout      = 0
      + cores_per_socket      = 2
      + default_ip            = (known after apply)
      + dynamic_mem_max       = (known after apply)
      + dynamic_mem_min       = (known after apply)
      + hard_drive            = (known after apply)
      + id                    = (known after apply)
      + name_description      = ""
      + name_label            = "VM-TF-TACG-DDC1"
      + network_interface     = [
          + {
              + device       = "0"
              + mac          = (known after apply)
              + network_uuid = "48045591-9653-12a9-edd9-62b345080a75"
              + other_config = (known after apply)
              + vif_ref      = (known after apply)
            },
        ]
      + other_config          = {}
      + sr_for_full_disk_copy = "ab9a9821-a664-8727-788e-0bb4bbd692b8"
      + static_mem_max        = 8589934592
      + static_mem_min        = (known after apply)
      + template_name         = "W2K25.1025"
      + uuid                  = (known after apply)
      + vcpus                 = 2
    }

  # xenserver_vm.VM-DDC2 will be created
  + resource "xenserver_vm" "VM-DDC2" {
      + boot_mode             = (known after apply)
      + boot_order            = (known after apply)
      + cdrom                 = "Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso"
      + check_ip_timeout      = 0
      + cores_per_socket      = 2
      + default_ip            = (known after apply)
      + dynamic_mem_max       = (known after apply)
      + dynamic_mem_min       = (known after apply)
      + hard_drive            = (known after apply)
      + id                    = (known after apply)
      + name_description      = ""
      + name_label            = "VM-TF-TACG-DDC2"
      + network_interface     = [
          + {
              + device       = "0"
              + mac          = (known after apply)
              + network_uuid = "48045591-9653-12a9-edd9-62b345080a75"
              + other_config = (known after apply)
              + vif_ref      = (known after apply)
            },
        ]
      + other_config          = {}
      + sr_for_full_disk_copy = "ab9a9821-a664-8727-788e-0bb4bbd692b8"
      + static_mem_max        = 8589934592
      + static_mem_min        = (known after apply)
      + template_name         = "W2K25.1025"
      + uuid                  = (known after apply)
      + vcpus                 = 2
    }

  # xenserver_vm.VM-StoreFront will be created
  + resource "xenserver_vm" "VM-StoreFront" {
      + boot_mode             = (known after apply)
      + boot_order            = (known after apply)
      + cdrom                 = "Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso"
      + check_ip_timeout      = 0
      + cores_per_socket      = 2
      + default_ip            = (known after apply)
      + dynamic_mem_max       = (known after apply)
      + dynamic_mem_min       = (known after apply)
      + hard_drive            = (known after apply)
      + id                    = (known after apply)
      + name_description      = ""
      + name_label            = "VM-TF-TACG-SF"
      + network_interface     = [
          + {
              + device       = "0"
              + mac          = (known after apply)
              + network_uuid = "48045591-9653-12a9-edd9-62b345080a75"
              + other_config = (known after apply)
              + vif_ref      = (known after apply)
            },
        ]
      + other_config          = {}
      + sr_for_full_disk_copy = "ab9a9821-a664-8727-788e-0bb4bbd692b8"
      + static_mem_max        = 8589934592
      + static_mem_min        = (known after apply)
      + template_name         = "W2K25.1025"
      + uuid                  = (known after apply)
      + vcpus                 = 2
    }
 # xenserver_vm.VM-WebStudio will be created
  + resource "xenserver_vm" "VM-WebStudio" {
      + boot_mode             = (known after apply)
      + boot_order            = (known after apply)
      + cdrom                 = "Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso"
      + check_ip_timeout      = 0
      + cores_per_socket      = 2
      + default_ip            = (known after apply)
      + dynamic_mem_max       = (known after apply)
      + dynamic_mem_min       = (known after apply)
      + hard_drive            = (known after apply)
      + id                    = (known after apply)
      + name_description      = ""
      + name_label            = "VM-TF-TACG-WS"
      + network_interface     = [
          + {
              + device       = "0"
              + mac          = (known after apply)
              + network_uuid = "48045591-9653-12a9-edd9-62b345080a75"
              + other_config = (known after apply)
              + vif_ref      = (known after apply)
            },
        ]
      + other_config          = {}
      + sr_for_full_disk_copy = "ab9a9821-a664-8727-788e-0bb4bbd692b8"
      + static_mem_max        = 8589934592
      + static_mem_min        = (known after apply)
      + template_name         = "W2K25.1025"
      + uuid                  = (known after apply)
      + vcpus                 = 2
    }

Plan: 4 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

xenserver_vm.VM-DDC2: Creating...
xenserver_vm.VM-DDC1: Creating...
xenserver_vm.VM-StoreFront: Creating...
xenserver_vm.VM-WebStudio: Creating...
xenserver_vm.VM-DDC2: Still creating... [00m10s elapsed]
xenserver_vm.VM-StoreFront: Still creating... [00m10s elapsed]
xenserver_vm.VM-DDC1: Still creating... [00m10s elapsed]
xenserver_vm.VM-WebStudio: Still creating... [00m10s elapsed]
...
xenserver_vm.VM-DDC2: Still creating... [01m50s elapsed]
xenserver_vm.VM-StoreFront: Still creating... [01m50s elapsed]
xenserver_vm.VM-DDC1: Still creating... [01m50s elapsed]
xenserver_vm.VM-WebStudio: Still creating... [01m50s elapsed]
xenserver_vm.VM-DDC2: Creation complete after 1m59s [id=5e08ba70-f038-f926-9650-99148985018e]
xenserver_vm.VM-DDC1: Creation complete after 1m59s [id=16330ad2-4c9a-e2db-cdc5-9c9b765fa879]
xenserver_vm.VM-StoreFront: Creation complete after 1m59s [id=db28575f-34f1-7624-184a-9f9c69bebc0b]
xenserver_vm.VM-WebStudio: Creation complete after 1m59s [id=88e132dd-45ed-1121-9eab-1e7fda2361aa]

Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
azadmin@tacg-cicd:/etc/terraform-tmm/CVADXS/_Creation$XenServer reflects the successful run:  Since the VMs have been created, we can proceed to the next step.  Part 3: Using Terraform and Ansible to configure the VMsThis configuration block now uses the combination of Terraform and Ansible for all needed configuration steps. As mentioned, Terraform excels at creating components; Ansible excels at configuring them. Together, we have everything we need for a successful deployment. Note: In all our guides, we try to be as Hypervisor- and Hyperscaler-agnostic as possible. For example, the vSphere provider could add a VM to an AD domain, but not all providers support this, and each provider uses a different syntax. Using the same Ansible playbook across all Hypervisors/Hyperscalers, regardless of built-in capabilities, also improves code reuse, as no specific code needs to be changed for different Hypervisor-/Hyperscaler deployments. This block also shows the main flow: Terraform decides which steps are necessary and starts the corresponding Ansible playbooks to achieve the needed outcome.  The following configuration steps are sequentially done: Change the IP addresses to known IPs. Change the Ansible configuration file to reflect the IP changes. Put the VMs into the AD domain. As the VMs are created, they will get an IP address from a DHCP server. This is expected behavior, but it will break any communication flow between Ansible and the VMs, as Ansible needs to know how to contact the VM. It can also lead to interruptions if the VMs are still configured to use DHCP when the Delivery Controllers and StoreFront are installed, so it is imperative to update the Ansible hosts file to reflect the assigned, static IP addresses after IP assignment. In our example, the VMs will get DHCP-based IP addresses in the 10.10.11.x/24 range. This is reflected in Ansible´s etc/ansible/HOSTS file: ...
[ddc1_ipaddr]
10.10.11.178

[ddc2_ipaddr]
10.10.11.179

[sf_ipaddr]
10.10.11.180

[ws_ipaddr]
10.10.11.181
...After the VMs are created, Ansible can contact them using the originally assigned DHCP-based IPs. Terraform now takes the three steps mentioned above to correct that. Therefore, it changes the IPs to fixed IPs defined in the Ansible playbook's corresponding variables. The etc/ansible/HOSTS file is updated with the correct IPs, and the VMs are added to the domain. Terraform monitors each Ansible Playbook run and stops further steps if a Playbook fails. # Terraform Deployment of Citrix CVAD on XenServer 8 - 25.11 
## Definition of XenServer Provider Variables - not all available variables are defined, only these we use
### Reference https://registry.terraform.io/providers/xenserver/xenserver/latest/docs

## Definition of all required local variables
### Get Needed Data
#### Call Ansible to Join VM to Domain
##### Copy Ansible Playbooks to Ansible Server

###### ***** IMPORTANT: You must set the permissions to the destination directory accordingly (sudo) chmod 777 otherwise the provisioning will fail *****
resource "null_resource" "CopyPlaybooksForCC1ToAnsibleServer" {
  connection {
    type     = var.TACG-TMM-XS-CL-Ansible-Connection-Type
    user     = var.TACG-TMM-XS-CL-Ansible-Connection-User
    password = var.TACG-TMM-XS-CL-Ansible-Connection-Password
    host     = var.TACG-TMM-XS-CL-Ansible-Connection-HostIP
    port     = var.TACG-TMM-XS-CL-Ansible-Connection-Port
    https    = var.TACG-TMM-XS-CL-Ansible-Connection-HTTPS
    timeout  = var.TACG-TMM-XS-CL-Ansible-Connection-Timeout
  }

  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-DomainJoinPlaybook-CC1-Source
    destination = var.TACG-TMM-XS-CL-Ansible-DomainJoinPlaybook-CC1-Destination
  }

  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-IPChangePlaybook-CC1-Source
    destination = var.TACG-TMM-XS-CL-Ansible-IPChangePlaybook-CC1-Destination
  }

  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-HostChangePlaybook-CC1-Source
    destination = var.TACG-TMM-XS-CL-Ansible-HostChangePlaybook-CC1-Destination
  }
}

resource "null_resource" "CopyPlaybooksForCC2ToAnsibleServer" {
  connection {
    type     = var.TACG-TMM-XS-CL-Ansible-Connection-Type
    user     = var.TACG-TMM-XS-CL-Ansible-Connection-User
    password = var.TACG-TMM-XS-CL-Ansible-Connection-Password
    host     = var.TACG-TMM-XS-CL-Ansible-Connection-HostIP
    port     = var.TACG-TMM-XS-CL-Ansible-Connection-Port
    https    = var.TACG-TMM-XS-CL-Ansible-Connection-HTTPS
    timeout  = var.TACG-TMM-XS-CL-Ansible-Connection-Timeout
  }

  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-DomainJoinPlaybook-CC2-Source
    destination = var.TACG-TMM-XS-CL-Ansible-DomainJoinPlaybook-CC2-Destination
  }

  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-IPChangePlaybook-CC2-Source
    destination = var.TACG-TMM-XS-CL-Ansible-IPChangePlaybook-CC2-Destination
  }

  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-HostChangePlaybook-CC2-Source
    destination = var.TACG-TMM-XS-CL-Ansible-HostChangePlaybook-CC2-Destination
  }
}

resource "null_resource" "CopyPlaybooksForSFToAnsibleServer" {
  connection {
    type     = var.TACG-TMM-XS-CL-Ansible-Connection-Type
    user     = var.TACG-TMM-XS-CL-Ansible-Connection-User
    password = var.TACG-TMM-XS-CL-Ansible-Connection-Password
    host     = var.TACG-TMM-XS-CL-Ansible-Connection-HostIP
    port     = var.TACG-TMM-XS-CL-Ansible-Connection-Port
    https    = var.TACG-TMM-XS-CL-Ansible-Connection-HTTPS
    timeout  = var.TACG-TMM-XS-CL-Ansible-Connection-Timeout
  }

  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-DomainJoinPlaybook-SF-Source
    destination = var.TACG-TMM-XS-CL-Ansible-DomainJoinPlaybook-SF-Destination
  }

  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-IPChangePlaybook-SF-Source
    destination = var.TACG-TMM-XS-CL-Ansible-IPChangePlaybook-SF-Destination
  }

  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-HostChangePlaybook-SF-Source
    destination = var.TACG-TMM-XS-CL-Ansible-HostChangePlaybook-SF-Destination
  }
}

resource "null_resource" "CopyPlaybooksForWebStudioToAnsibleServer" {
  connection {
    type     = var.TACG-TMM-XS-CL-Ansible-Connection-Type
    user     = var.TACG-TMM-XS-CL-Ansible-Connection-User
    password = var.TACG-TMM-XS-CL-Ansible-Connection-Password
    host     = var.TACG-TMM-XS-CL-Ansible-Connection-HostIP
    port     = var.TACG-TMM-XS-CL-Ansible-Connection-Port
    https    = var.TACG-TMM-XS-CL-Ansible-Connection-HTTPS
    timeout  = var.TACG-TMM-XS-CL-Ansible-Connection-Timeout
  }

  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-DomainJoinPlaybook-WS-Source
    destination = var.TACG-TMM-XS-CL-Ansible-DomainJoinPlaybook-WS-Destination
  }

  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-IPChangePlaybook-WS-Source
    destination = var.TACG-TMM-XS-CL-Ansible-IPChangePlaybook-WS-Destination
  }

  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-HostChangePlaybook-WS-Source
    destination = var.TACG-TMM-XS-CL-Ansible-HostChangePlaybook-WS-Destination
  }
}

#### Wait 10s Minute for Backgroud Processes to Settle
resource "time_sleep" "wait_10_seconds" {
  depends_on = [null_resource.CopyPlaybooksForCC1ToAnsibleServer, null_resource.CopyPlaybooksForCC2ToAnsibleServer, null_resource.CopyPlaybooksForSFToAnsibleServer, null_resource.CopyPlaybooksForWebStudioToAnsibleServer]

  create_duration = "10s"
}

#### Use Ansible to change all relevant settings on DDC1 
##### Connect to Ansible Interpreter and change IP of DDC1
resource "null_resource" "ChangeIPOfDDC1" {
  depends_on = [time_sleep.wait_10_seconds]
  provisioner "local-exec" {
    working_dir = "/etc/ansible"
    command     = var.TACG-TMM-XS-CL-Ansible-IPChangeCMDForCC1
  }
}

##### Connect to Ansible Interpreter and change IP of DDC1 in Ansible´s HOSTS file
resource "null_resource" "ChangeHostIPOfDDC1" {
  depends_on = [null_resource.ChangeIPOfDDC1]
  provisioner "local-exec" {
    working_dir = "/etc/ansible"
    command     = var.TACG-TMM-XS-CL-Ansible-HostChangeCMDForCC1
  }
}

##### Check Ansible Return Code
data "external" "CheckAnsibleReturnCode1" {
  depends_on = [null_resource.ChangeHostIPOfDDC1]
  program    = ["bash", "/etc/ansible/assets/CheckAnsibleReturnCode.sh", "/etc/ansible/assets/ansible_exit_code.txt", "0"]
}

##### Terminate if Return Code is not 0
resource "null_resource" "IfExitCodeIsNotZeroThenHalt1" {
  depends_on = [data.external.CheckAnsibleReturnCode1]
  provisioner "local-exec" {
    interpreter = ["/bin/bash", "-c"]
    command     = "[ \"${data.external.CheckAnsibleReturnCode1.result.found}\" = \"1\" ] &amp;&amp; echo \"Changing IP/HOSTS: Everything OK\" || { echo \"Changing the IP of DDC1 and/or changing the Ansible HOSTS file failed. Please review the settings. Halting Terraform.\"; exit 1; }"
  }
}

resource "time_sleep" "wait_10_seconds_1" {
  depends_on      = [null_resource.IfExitCodeIsNotZeroThenHalt1]
  create_duration = "10s"
}

##### Connect to Ansible Interpreter and Add DDC1 To Domain
resource "null_resource" "AddCC1ToDomain" {
  depends_on = [time_sleep.wait_10_seconds_1]

  provisioner "local-exec" {
    working_dir = "/etc/ansible"
    command     = var.TACG-TMM-XS-CL-Ansible-DomainJoinCMDForCC1
  }
}

##### Check Ansible Return Code
data "external" "CheckAnsibleReturnCodeD1" {
  depends_on = [null_resource.AddCC1ToDomain]
  program    = ["bash", "/etc/ansible/assets/CheckAnsibleReturnCode.sh", "/etc/ansible/assets/ansible_exit_code.txt", "0"]
}

##### Terminate if Return Code is not 0
resource "null_resource" "IfExitCodeIsNotZeroThenHaltD1" {
  depends_on = [data.external.CheckAnsibleReturnCodeD1]
  provisioner "local-exec" {
    interpreter = ["/bin/bash", "-c"]
    command     = "[ \"${data.external.CheckAnsibleReturnCodeD1.result.found}\" = \"1\" ] &amp;&amp; echo \"Adding DDC1 to Domain: Everything OK\" || { echo \"Adding DDC1 to the domain failed. Please review the settings. Halting Terraform.\"; exit 1; }"
  }
}

##### End of necessary changes on DDC1 

#### Wait 10s Minute for Backgroud Processes to Settle
resource "time_sleep" "wait_10_seconds_afterddc1" {
  depends_on = [null_resource.IfExitCodeIsNotZeroThenHaltD1]

  create_duration = "10s"
}

...
The same steps are needed for all the other servers
...

##### All necessary changes on the VMs are completeExample Ansible Playbook for changing the IP Address: ...
- name: Change DDC1 IP address
  hosts: ddc1_ipaddr
  gather_facts: yes
  vars:
    ip: "10.10.119.11"
    sn: "16"
    gw: "10.10.0.1"
    dns: "10.10.100.1"

  tasks:
    - name: Set up static IP address
      ansible.windows.win_shell: |
        $interface = Get-NetAdapter | ? {$_.Status -eq "up"}
        $IpAddre = "{{ ip }}"
        $Prefx = "{{ sn }}"
        $GW = "{{ gw }}"
        $DNSAddre = "{{ dns }}"
        $interface | New-NetIPAddress -IPAddress $IpAddre -PrefixLength $Prefx -DefaultGateway $GW
        $interface | Set-DnsClientServerAddress -ServerAddresses $DNSAddre
        Start-Sleep -Seconds 10
      async: 100
      poll: 0
      register: ip_resultExample Ansible Playbook for changing the etc/ansible/HOSTS file: ...
- name: Replace old IP with new IP in /etc/ansible/hosts
  hosts: localhost
  connection: local
  become: true
  vars:
    ip: "10.10.119.11"
    oldip: "10.10.11.163"
  tasks:
    - name: Replace old IP with new IP in /etc/ansible/hosts
      lineinfile:
        path: /etc/ansible/hosts
        search_string: '{{ oldip }}'  
        line: '{{ ip }}'       
        state: presentExample Ansible Playbook for adding the VM to the AD domain: ...
- name: join host to domain with automatic reboot
  hosts: ddc1_ipaddr

  tasks:
  - name: join host to domain with automatic reboot
    microsoft.ad.membership:
      dns_domain_name: the-austrian-citrix-guy.at
      hostname: tf-cvad-ddc1
      domain_admin_user: XxXxXxXxX@the-austrian-citrix-guy.at
      domain_admin_password: "!XxXxXxXxXxXxXxXxX!"
      domain_ou_path: "OU=_COMPUTER,OU=TACG-CVAD,DC=the-austrian-citrix-guy,DC=at"
      state: domain
      reboot: trueRunning the snippet should fulfill all needed steps: azadmin@tacg-cicd:/etc/terraform-tmm/CVADXS/_UseAnsibleToJoinDomain$ terraform apply

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create
 &lt;= read (data resources)

Terraform will perform the following actions:

  # data.external.CheckAnsibleReturnCode1 will be read during apply
  # (depends on a resource or a module with changes pending)
 &lt;= data "external" "CheckAnsibleReturnCode1" {
      + id      = (known after apply)
      + program = [
          + "bash",
          + "/etc/ansible/assets/CheckAnsibleReturnCode.sh",
          + "/etc/ansible/assets/ansible_exit_code.txt",
          + "0",
        ]
      + result  = (known after apply)
    }

  # data.external.CheckAnsibleReturnCode2 will be read during apply
  # (depends on a resource or a module with changes pending)
 &lt;= data "external" "CheckAnsibleReturnCode2" {
      + id      = (known after apply)
      + program = [
          + "bash",
          + "/etc/ansible/assets/CheckAnsibleReturnCode.sh",
          + "/etc/ansible/assets/ansible_exit_code.txt",
          + "0",
        ]
      + result  = (known after apply)
    }

  ...

  # time_sleep.wait_10_seconds_afterddc2 will be created
  + resource "time_sleep" "wait_10_seconds_afterddc2" {
      + create_duration = "10s"
      + id              = (known after apply)
    }

  # time_sleep.wait_10_seconds_aftersf will be created
  + resource "time_sleep" "wait_10_seconds_aftersf" {
      + create_duration = "10s"
      + id              = (known after apply)
    }

Plan: 32 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.CopyPlaybooksForWebStudioToAnsibleServer: Creating...
null_resource.CopyPlaybooksForCC2ToAnsibleServer: Creating...
null_resource.CopyPlaybooksForCC1ToAnsibleServer: Creating...
null_resource.CopyPlaybooksForSFToAnsibleServer: Creating...
null_resource.CopyPlaybooksForCC2ToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPlaybooksForCC1ToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPlaybooksForWebStudioToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPlaybooksForSFToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPlaybooksForSFToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPlaybooksForCC2ToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPlaybooksForWebStudioToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPlaybooksForCC1ToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPlaybooksForSFToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPlaybooksForCC2ToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPlaybooksForCC1ToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPlaybooksForWebStudioToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPlaybooksForSFToAnsibleServer: Creation complete after 0s [id=9099395716454281124]
null_resource.CopyPlaybooksForCC2ToAnsibleServer: Creation complete after 1s [id=2239027084142302025]
null_resource.CopyPlaybooksForCC1ToAnsibleServer: Creation complete after 1s [id=8241870746583503880]
null_resource.CopyPlaybooksForWebStudioToAnsibleServer: Creation complete after 1s [id=85414301571074153]
time_sleep.wait_10_seconds: Creating...
time_sleep.wait_10_seconds: Still creating... [00m10s elapsed]
time_sleep.wait_10_seconds: Creation complete after 10s [id=2025-11-03T12:50:18Z]
null_resource.ChangeIPOfDDC1: Creating...
null_resource.ChangeIPOfDDC1: Provisioning with 'local-exec'...
null_resource.ChangeIPOfDDC1 (local-exec): Executing: ["/bin/sh" "-c" "ansible-playbook /etc/ansible/assets/ChangeIPAddress-DDC1.microsoft.ad.ansible.yml -v -c winrm; echo $? &gt; /etc/ansible/assets/ansible_exit_code.txt"]

null_resource.ChangeIPOfDDC1 (local-exec): PLAY [Change DDC1 IP address] **************************************************

null_resource.ChangeIPOfDDC1 (local-exec): TASK [Gathering Facts] *********************************************************
null_resource.ChangeIPOfDDC1 (local-exec): ok: [10.10.11.163]

null_resource.ChangeIPOfDDC1 (local-exec): TASK [Set up static IP address] ************************************************
null_resource.ChangeIPOfDDC1 (local-exec): ok: [10.10.11.163] =&gt; {"ansible_async_watchdog_pid": 2392, "ansible_job_id": "j399189549114.3256", "changed": false, "finished": false, "results_file": "C:\\Users\\Administrator\\.ansible_async\\j399189549114.3256", "started": true}

null_resource.ChangeIPOfDDC1 (local-exec): PLAY RECAP *********************************************************************
null_resource.ChangeIPOfDDC1 (local-exec): 10.10.11.163               : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

null_resource.ChangeIPOfDDC1: Creation complete after 5s [id=679400677203292951]
null_resource.ChangeHostIPOfDDC1: Creating...
null_resource.ChangeHostIPOfDDC1: Provisioning with 'local-exec'...
null_resource.ChangeHostIPOfDDC1 (local-exec): Executing: ["/bin/sh" "-c" "ansible-playbook /etc/ansible/assets/ChangeHostFile-DDC1.microsoft.ad.ansible.yml -v -c winrm; echo $? &gt; /etc/ansible/assets/ansible_exit_code.txt"]

null_resource.ChangeHostIPOfDDC1 (local-exec): PLAY [Replace old IP with new IP in /etc/ansible/hosts] ************************

null_resource.ChangeHostIPOfDDC1 (local-exec): TASK [Gathering Facts] *********************************************************
null_resource.ChangeHostIPOfDDC1 (local-exec): ok: [localhost]

null_resource.ChangeHostIPOfDDC1 (local-exec): TASK [Replace old IP with new IP in /etc/ansible/hosts] ************************
null_resource.ChangeHostIPOfDDC1 (local-exec): changed: [localhost] =&gt; {"backup": "", "changed": true, "msg": "line replaced"}

null_resource.ChangeHostIPOfDDC1 (local-exec): PLAY RECAP *********************************************************************
null_resource.ChangeHostIPOfDDC1 (local-exec): localhost                  : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

null_resource.ChangeHostIPOfDDC1: Creation complete after 1s [id=4435900660469540446]
data.external.CheckAnsibleReturnCode1: Reading...
data.external.CheckAnsibleReturnCode1: Read complete after 0s [id=-]
null_resource.IfExitCodeIsNotZeroThenHalt1: Creating...
null_resource.IfExitCodeIsNotZeroThenHalt1: Provisioning with 'local-exec'...
null_resource.IfExitCodeIsNotZeroThenHalt1 (local-exec): Executing: ["/bin/bash" "-c" "[ \"1\" = \"1\" ] &amp;&amp; echo \"Changing IP/HOSTS: Everything OK\" || { echo \"Changing the IP of DDC1 and/or changing the Ansible HOSTS file failed. Please review the settings. Halting Terraform.\"; exit 1; }"]
null_resource.IfExitCodeIsNotZeroThenHalt1 (local-exec): Changing IP/HOSTS: Everything OK
null_resource.IfExitCodeIsNotZeroThenHalt1: Creation complete after 0s [id=8720650856500940604]
time_sleep.wait_10_seconds_1: Creating...
time_sleep.wait_10_seconds_1: Still creating... [00m10s elapsed]
time_sleep.wait_10_seconds_1: Creation complete after 10s [id=2025-11-03T12:50:35Z]
null_resource.AddCC1ToDomain: Creating...
null_resource.AddCC1ToDomain: Provisioning with 'local-exec'...
null_resource.AddCC1ToDomain (local-exec): Executing: ["/bin/sh" "-c" "ansible-playbook /etc/ansible/assets/Join-VMToDomain-DDC1.microsoft.ad.ansible.yml -v -c winrm; echo $? &gt; /etc/ansible/assets/ansible_exit_code.txt"]

null_resource.AddCC1ToDomain (local-exec): PLAY [join host to domain with automatic reboot] *******************************

null_resource.AddCC1ToDomain (local-exec): TASK [Gathering Facts] *********************************************************
null_resource.AddCC1ToDomain (local-exec): ok: [10.10.119.11]

null_resource.AddCC1ToDomain (local-exec): TASK [join host to domain with automatic reboot] *******************************
null_resource.AddCC1ToDomain: Still creating... [00m10s elapsed]
null_resource.AddCC1ToDomain: Still creating... [00m20s elapsed]
null_resource.AddCC1ToDomain (local-exec): changed: [10.10.119.11] =&gt; {"changed": true, "reboot_required": false}

null_resource.AddCC1ToDomain (local-exec): PLAY RECAP *********************************************************************
null_resource.AddCC1ToDomain (local-exec): 10.10.119.11               : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

null_resource.AddCC1ToDomain: Creation complete after 28s [id=6625761552218837946]
data.external.CheckAnsibleReturnCodeD1: Reading...
data.external.CheckAnsibleReturnCodeD1: Read complete after 0s [id=-]
null_resource.IfExitCodeIsNotZeroThenHaltD1: Creating...
null_resource.IfExitCodeIsNotZeroThenHaltD1: Provisioning with 'local-exec'...
null_resource.IfExitCodeIsNotZeroThenHaltD1 (local-exec): Executing: ["/bin/bash" "-c" "[ \"1\" = \"1\" ] &amp;&amp; echo \"Adding DDC1 to Domain: Everything OK\" || { echo \"Adding DDC1 to the domain failed. Please review the settings. Halting Terraform.\"; exit 1; }"]
null_resource.IfExitCodeIsNotZeroThenHaltD1 (local-exec): Adding DDC1 to Domain: Everything OK
null_resource.IfExitCodeIsNotZeroThenHaltD1: Creation complete after 0s [id=7818126381059512591]
time_sleep.wait_10_seconds_afterddc1: Creating...
time_sleep.wait_10_seconds_afterddc1: Still creating... [00m10s elapsed]
time_sleep.wait_10_seconds_afterddc1: Creation complete after 10s [id=2025-11-03T12:51:13Z]
...
All these steps are the same for the other three servers.
Output omitted due to length
...
data.external.CheckAnsibleReturnCodeDWS: Reading...
data.external.CheckAnsibleReturnCodeDWS: Read complete after 0s [id=-]
null_resource.IfExitCodeIsNotZeroThenHaltDWS: Creating...
null_resource.IfExitCodeIsNotZeroThenHaltDWS: Provisioning with 'local-exec'...
null_resource.IfExitCodeIsNotZeroThenHaltDWS (local-exec): Executing: ["/bin/bash" "-c" "[ \"1\" = \"1\" ] &amp;&amp; echo \"Adding WebStudio to Domain: Everything OK\" || { echo \"Adding WebStudio to the domain failed. Please review the settings. Halting Terraform.\"; exit 1; }"]
null_resource.IfExitCodeIsNotZeroThenHaltDWS (local-exec): Adding WebStudio to Domain: Everything OK
null_resource.IfExitCodeIsNotZeroThenHaltDWS: Creation complete after 0s [id=975638781208917805]

Apply complete! Resources: 32 added, 0 changed, 0 destroyed.
azadmin@tacg-cicd:/etc/terraform-tmm/CVADXS/_UseAnsibleToJoinDomain$The etc/ansible/HOSTS file is updated with the correct IPs: ...
[ddc1_ipaddr]
10.10.119.11

[ddc2_ipaddr]
10.10.119.12

[sf_ipaddr]
10.10.119.13

[ws_ipaddr]
10.10.119.14
..All VMs are added to the AD domain:  Ansible´s further used inventory.ini file also reflects all these changes: [cvad:children]
cvad-ddcs
cvad-sfs
cvad-ws

[cvad-ddcs]
tf-cvad-ddc1.the-austrian-citrix-guy.at
tf-cvad-ddc2.the-austrian-citrix-guy.at

[cvad-ddc1]
tf-cvad-ddc1.the-austrian-citrix-guy.at

[cvad-ddc2]
tf-cvad-ddc2.the-austrian-citrix-guy.at

[cvad-ddc1-nb]
TF-CVAD-DDC1

[cvad-ddc2-nb]
TF-CVAD-DDC2

[cvad-sfs]
tf-cvad-sf.the-austrian-citrix-guy.at

[cvad-ws]
tf-cvad-ws.the-austrian-citrix-guy.at

[all:vars]
ansible_user="svc_ansible"
ansible_domainuser="XxXxXxXxX@the-austrian-citrix-guy.at"
ansible_password="!XxXxXxXxXxXxXxXxXx!”
ansible_connection=winrm
ansible_winrm_scheme=https
ansible_winrm_transport=certificate
ansible_winrm_port=5986
ansible_winrm_server_cert_validation=ignore
ansible_winrm_cert_pem=/etc/ssl/certs/svc_ansible.pem
ansible_winrm_cert_key_pem=/etc/ssl/certs/svc_ansible.pem
ansible_winrm_server_cert_validation=ignoreSince the VMs and their dependencies were successfully reconfigured, we can proceed to the next step.  Part 4: Using Terraform and Ansible to deploy the Delivery ControllersThe VMs dedicated to holding the Delivery Controller™ roles are now ready for the deployment of the Delivery Controller components. This is a multi-step approach: Mount the CVAD ISO installer Copy the publicly signed SSL certificate to the DDCs Install all required server roles and features before installing the DDCs Change some necessary registry settings Therefore, we again use the well-proven combination of Terraform and Ansible. Initially, no Citrix software is installed on the VMs:  Terraform monitors each Ansible Playbook run and stops further steps if a Playbook fails. Example Terraform snippet: # Terraform Deployment of Citrix CVAD on XenServer 8 - 25.11 
## Definition of XenServer Provider Variables - not all available variables are defined, only these we use
### Reference https://registry.terraform.io/providers/xenserver/xenserver/latest/docs
#### Copy the required Ansible Assets to the Ansible Server
###### ***** IMPORTANT: You must set the permissions to the destination directory accordingly (sudo) chmod 777 otherwise the provisioning will fail *****
resource "null_resource" "CopyAnsibleDDCAssetsToAnsibleServer" {
  connection {
    type     = var.TACG-TMM-XS-CL-Ansible-Connection-Type
    user     = var.TACG-TMM-XS-CL-Ansible-Connection-User
    password = var.TACG-TMM-XS-CL-Ansible-Connection-Password
    host     = var.TACG-TMM-XS-CL-Ansible-Connection-HostIP
    port     = var.TACG-TMM-XS-CL-Ansible-Connection-Port
    https    = var.TACG-TMM-XS-CL-Ansible-Connection-HTTPS
    timeout  = var.TACG-TMM-XS-CL-Ansible-Connection-Timeout
  }

  # Copy vars-ddcs.yml file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-Vars-Source
    destination = var.TACG-TMM-XS-CL-Ansible-Vars-Destination
  }

  # Copy inventory.ini file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-Inventory-Source
    destination = var.TACG-TMM-XS-CL-Ansible-Inventory-Destination
  }

  # Copy InitialConfig Playbook file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-InitialPlaybookForDDCs-Source
    destination = var.TACG-TMM-XS-CL-Ansible-InitialPlaybookForDDCs-Destination
  }
}

#### Wait 10s for Backgroud Processes to Settle
resource "time_sleep" "wait_10_seconds" {
  depends_on      = [null_resource.CopyAnsibleDDCAssetsToAnsibleServer]
  create_duration = "10s"
}

##### Connect to Ansible Interpreter and run Initial Configuration Playbook on DDCs
resource "null_resource" "RunInitialPlaybook" {
  depends_on = [time_sleep.wait_10_seconds]

  provisioner "local-exec" {
    working_dir = "/etc/ansible"
    command     = var.TACG-TMM-XS-CL-Ansible-InitialCMDForDDCs
  }
}

##### Check Ansible Return Code
data "external" "CheckAnsibleReturnCode" {
  depends_on = [null_resource.RunInitialPlaybook]
  program    = ["bash", "/etc/ansible/assets/CheckAnsibleReturnCode.sh", "/etc/ansible/assets/ansible_exit_code.txt", "0"]
}

##### Terminate if Return Code is not 0
resource "null_resource" "IfExitCodeIsNotZeroThenHalt" {
  depends_on = [data.external.CheckAnsibleReturnCode]
  provisioner "local-exec" {
    interpreter = ["/bin/bash", "-c"]
    command     = "[ \"${data.external.CheckAnsibleReturnCode.result.found}\" = \"1\" ] &amp;&amp; echo \"Deploying CVAD: Everything OK\" || { echo \"Deploying the CVAD software and its prerequisites failed. Please review the settings. Halting Terraform.\"; exit 1; }"
  }
}
Example Ansible Playbook for deploying and configuring the DDCs and all needed further configurations: ---
# Terraform Deployment of Citrix CVAD on XenServer 8 - 25.11 
## For installation and configuration of the Delivery Controllers, StoreFront-Servers and WebStudio-Servers, we use Ansible Playbooks 
### Reference https://docs.ansible.com/ansible/latest/getting_started/index.html
#### We assume that the communication between the Ansible Server and all needed Hosts via WinRM is working as intended - check your Group Policies
##### All hosts and its corresponding communication-related Ansible variables are stored in assets/inventory.ini

###### Prepare the Delivery Controllers
- name: Configure the DDCs initially
  hosts: cvad-ddcs
  vars_files:
    - ./vars-ddcs.yml
  tasks:

  - name: Create Installer directory  
    ansible.windows.win_file: 
      path: "{{ installdir }}"
      state: directory

  - name: Create Log Folder
    ansible.windows.win_file:
      path: "{{ logdir }}"
      state: directory

  - name: Copy Citrix iso to the host
    ansible.windows.win_copy:
      src: "{{ sourceiso }}"
      dest: "{{ destiso }}" 
      force: no

  - name: Copy the SSL certificate to the hosts
    ansible.windows.win_copy:
      src: "{{ certificate_source_path }}"
      dest: "{{ certificate_destination_path }}" 
      force: no

  - name: Install Required Server Roles for Citrix
    ansible.windows.win_feature:
      name: "{{ ddc_req_roles }}"
      state: present
    register: requirements

  - name: Reboot when Required Roles need a restart
    ansible.windows.win_reboot:
      post_reboot_delay: 120
    when: requirements.reboot_required

  - name: Install SSL-Certificate on Servers
    ansible.windows.win_certificate_store:
      path: "{{ certificate_destination_path }}" 
      password: "{{ certificate_pfx_password }}"
      key_exportable: yes
      file_type: pkcs12
      store_location: LocalMachine
      key_storage: machine
      state: present
    become: true
    become_method: runas
    become_user: SYSTEM

  - name: Run installer from mounted ISO
    ansible.windows.win_package:
      path: "{{ install_exe }} "
      arguments: "{{ install_params }}"
      state: present
      expected_return_code: [0, 3, 3010]
      creates_path: C:\Program Files\Citrix\Desktop Studio
    register: ddc_install

  - name: Reboot after DDC
    ansible.windows.win_reboot:
    when: ddc_install.changed

  - name: Remove DDC RunOnce Key
    ansible.windows.win_regedit:
      path: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce
      name: "!XenDesktopSetup"
      state: absent
    register: ddc_resume

  - name: Resume DDC Install
    ansible.windows.win_package:
      path: C:\ProgramData\Citrix\XenDesktopSetup\XenDesktopServerSetup.exe
      state: present
      expected_return_code: [0, 3, 3010]
      creates_path: C:\Program Files\Citrix\Desktop Studio
    when: ddc_resume.changed

  - name: Alter unwanted Registry Keys-UserIEESC
    ansible.windows.win_regedit:
      path: HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A8-37EF-4b3f-8CFC-4F3A74704073}
      name: IsInstalled
      data: 0

  - name: Alter unwanted Registry Keys-AdminIEESC
    ansible.windows.win_regedit:
      path: HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A7-37EF-4b3f-8CFC-4F3A74704073}
      name: IsInstalled
      data: 0

  - name: Restart a service
    ansible.windows.win_service:
      name: CitrixHostService
      state: startedRunning the snippet should fulfill all needed steps: azadmin@tacg-cicd:/etc/terraform-tmm/CVADXS/_InstallCVAD$ terraform apply

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
  + create
 &lt;= read (data resources)

Terraform will perform the following actions:

  # data.external.CheckAnsibleReturnCode will be read during apply
  # (depends on a resource or a module with changes pending)
 &lt;= data "external" "CheckAnsibleReturnCode" {
      + id      = (known after apply)
      + program = [
          + "bash",
          + "/etc/ansible/assets/CheckAnsibleReturnCode.sh",
          + "/etc/ansible/assets/ansible_exit_code.txt",
          + "0",
        ]
      + result  = (known after apply)
    }

  # null_resource.CopyAnsibleDDCAssetsToAnsibleServer will be created
  + resource "null_resource" "CopyAnsibleDDCAssetsToAnsibleServer" {
      + id = (known after apply)
    }

  # null_resource.IfExitCodeIsNotZeroThenHalt will be created
  + resource "null_resource" "IfExitCodeIsNotZeroThenHalt" {
      + id = (known after apply)
    }

  # null_resource.RunInitialPlaybook will be created
  + resource "null_resource" "RunInitialPlaybook" {
      + id = (known after apply)
    }

  # time_sleep.wait_10_seconds will be created
  + resource "time_sleep" "wait_10_seconds" {
      + create_duration = "10s"
      + id              = (known after apply)
    }

Plan: 4 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.CopyAnsibleDDCAssetsToAnsibleServer: Creating...
null_resource.CopyAnsibleDDCAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyAnsibleDDCAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyAnsibleDDCAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyAnsibleDDCAssetsToAnsibleServer: Creation complete after 0s [id=3392935108841645613]
time_sleep.wait_10_seconds: Creating...
time_sleep.wait_10_seconds: Still creating... [00m10s elapsed]
time_sleep.wait_10_seconds: Creation complete after 10s [id=2025-11-12T07:13:29Z]
null_resource.RunInitialPlaybook: Creating...
null_resource.RunInitialPlaybook: Provisioning with 'local-exec'...
null_resource.RunInitialPlaybook (local-exec): Executing: ["/bin/sh" "-c" "ansible-playbook /etc/ansible/assets/InitialConfigurationForDDCs.ansible.yml -i /etc/ansible/assets/inventory.ini -v -c winrm; echo $? &gt; /etc/ansible/assets/ansible_exit_code.txt"]
null_resource.RunInitialPlaybook (local-exec): Using /etc/ansible/ansible.cfg as config file

null_resource.RunInitialPlaybook (local-exec): PLAY [Configure the DDCs initially] ********************************************

null_resource.RunInitialPlaybook (local-exec): TASK [Gathering Facts] *********************************************************
null_resource.RunInitialPlaybook (local-exec): ok: [tf-cvad-ddc1.the-austrian-citrix-guy.at]
null_resource.RunInitialPlaybook (local-exec): ok: [tf-cvad-ddc2.the-austrian-citrix-guy.at]

null_resource.RunInitialPlaybook (local-exec): TASK [Create Installer directory] **********************************************
null_resource.RunInitialPlaybook (local-exec): ok: [tf-cvad-ddc2.the-austrian-citrix-guy.at] =&gt; {"changed": false}
null_resource.RunInitialPlaybook (local-exec): ok: [tf-cvad-ddc1.the-austrian-citrix-guy.at] =&gt; {"changed": false}

null_resource.RunInitialPlaybook (local-exec): TASK [Create Log Folder] *******************************************************
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ddc1.the-austrian-citrix-guy.at] =&gt; {"changed": true}
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ddc2.the-austrian-citrix-guy.at] =&gt; {"changed": true}

null_resource.RunInitialPlaybook (local-exec): TASK [Copy Citrix iso to the host] *********************************************
null_resource.RunInitialPlaybook: Still creating... [00m10s elapsed]
null_resource.RunInitialPlaybook: Still creating... [00m20s elapsed]

...

null_resource.RunInitialPlaybook: Still creating... [12m10s elapsed]
null_resource.RunInitialPlaybook: Still creating... [12m20s elapsed]
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ddc1.the-austrian-citrix-guy.at] =&gt; {"changed": true, "checksum": null, "dest": "C:\\InstallCVAD\\Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso", "operation": "file_copy", "original_basename": "Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso", "size": 4124442624, "src": "/etc/ansible/isos/Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso"}
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ddc2.the-austrian-citrix-guy.at] =&gt; {"changed": true, "checksum": null, "dest": "C:\\InstallCVAD\\Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso", "operation": "file_copy", "original_basename": "Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso", "size": 4124442624, "src": "/etc/ansible/isos/Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso"}

null_resource.RunInitialPlaybook (local-exec): TASK [Copy the SSL certificate to the hosts] ***********************************
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ddc2.the-austrian-citrix-guy.at] =&gt; {"changed": true, "checksum": null, "dest": "C:\\InstallCVAD\\2025-wildcard.the-austrian-citrix-guy.at.PFX", "operation": "file_copy", "original_basename": "2025-wildcard.the-austrian-citrix-guy.at.PFX", "size": 3593, "src": "/etc/ansible/assets/2025-wildcard.the-austrian-citrix-guy.at.PFX"}
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ddc1.the-austrian-citrix-guy.at] =&gt; {"changed": true, "checksum": null, "dest": "C:\\InstallCVAD\\2025-wildcard.the-austrian-citrix-guy.at.PFX", "operation": "file_copy", "original_basename": "2025-wildcard.the-austrian-citrix-guy.at.PFX", "size": 3593, "src": "/etc/ansible/assets/2025-wildcard.the-austrian-citrix-guy.at.PFX"}

null_resource.RunInitialPlaybook (local-exec): TASK [Install Required Server Roles for Citrix] ********************************

null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ddc1.the-austrian-citrix-guy.at] =&gt; {"changed": true, "exitcode": "Success", "feature_result": [{"display_name": "Group Policy Management", "id": 69, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ASP.NET 4.8", "id": 429, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Remote Server Administration Tools", "id": 67, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "AD DS and AD LDS Tools", "id": 329, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "AD DS Tools", "id": 257, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "AD DS Snap-Ins and Command-Line Tools", "id": 299, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Role Administration Tools", "id": 256, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Application Development", "id": 147, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ASP.NET 4.8", "id": 413, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Common HTTP Features", "id": 141, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Default Document", "id": 143, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Directory Browsing", "id": 144, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Request Filtering", "id": 169, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Health and Diagnostics", "id": 155, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "HTTP Errors", "id": 145, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "HTTP Logging", "id": 156, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ISAPI Extensions", "id": 152, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ISAPI Filters", "id": 153, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "IIS Management Console", "id": 175, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Management Tools", "id": 174, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": ".NET Extensibility 4.8", "id": 414, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Performance", "id": 171, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Security", "id": 162, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Web Server (IIS)", "id": 2, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Static Content Compression", "id": 172, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Static Content", "id": 142, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Web Server", "id": 140, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Windows Authentication", "id": 164, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}], "reboot_required": false, "success": true}
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ddc2.the-austrian-citrix-guy.at] =&gt; {"changed": true, "exitcode": "Success", "feature_result": [{"display_name": "Group Policy Management", "id": 69, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ASP.NET 4.8", "id": 429, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Remote Server Administration Tools", "id": 67, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "AD DS and AD LDS Tools", "id": 329, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "AD DS Tools", "id": 257, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "AD DS Snap-Ins and Command-Line Tools", "id": 299, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Role Administration Tools", "id": 256, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Application Development", "id": 147, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ASP.NET 4.8", "id": 413, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Common HTTP Features", "id": 141, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Default Document", "id": 143, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Directory Browsing", "id": 144, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Request Filtering", "id": 169, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Health and Diagnostics", "id": 155, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "HTTP Errors", "id": 145, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "HTTP Logging", "id": 156, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ISAPI Extensions", "id": 152, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ISAPI Filters", "id": 153, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "IIS Management Console", "id": 175, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Management Tools", "id": 174, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": ".NET Extensibility 4.8", "id": 414, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Performance", "id": 171, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Security", "id": 162, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Web Server (IIS)", "id": 2, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Static Content Compression", "id": 172, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Static Content", "id": 142, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Web Server", "id": 140, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Windows Authentication", "id": 164, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}], "reboot_required": false, "success": true}

null_resource.RunInitialPlaybook (local-exec): TASK [Reboot when Required Roles need a restart] *******************************
null_resource.RunInitialPlaybook (local-exec): skipping: [tf-cvad-ddc1.the-austrian-citrix-guy.at] =&gt; {"changed": false, "false_condition": "requirements.reboot_required", "skip_reason": "Conditional result was False"}
null_resource.RunInitialPlaybook (local-exec): skipping: [tf-cvad-ddc2.the-austrian-citrix-guy.at] =&gt; {"changed": false, "false_condition": "requirements.reboot_required", "skip_reason": "Conditional result was False"}

null_resource.RunInitialPlaybook (local-exec): TASK [Install SSL-Certificate on Servers] **************************************
null_resource.RunInitialPlaybook: Still creating... [15m00s elapsed]
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ddc1.the-austrian-citrix-guy.at] =&gt; {"changed": true, "thumbprints": ["0BB52BDDEB78E6E8E32FF3B1949CAA462B8EC386"]}
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ddc2.the-austrian-citrix-guy.at] =&gt; {"changed": true, "thumbprints": ["0BB52BDDEB78E6E8E32FF3B1949CAA462B8EC386"]}

null_resource.RunInitialPlaybook (local-exec): TASK [Run installer from mounted ISO] ******************************************
null_resource.RunInitialPlaybook: Still creating... [15m10s elapsed]
null_resource.RunInitialPlaybook: Still creating... [15m20s elapsed]

...

null_resource.RunInitialPlaybook: Still creating... [25m10s elapsed]
null_resource.RunInitialPlaybook: Still creating... [25m20s elapsed]
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ddc1.the-austrian-citrix-guy.at] =&gt; {"changed": true, "checksum": "EADE21C7B09DED5626F3BB51D4DF7A4C0E7AC607", "rc": 0, "reboot_required": false}
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ddc2.the-austrian-citrix-guy.at] =&gt; {"changed": true, "checksum": "EADE21C7B09DED5626F3BB51D4DF7A4C0E7AC607", "rc": 0, "reboot_required": false}

null_resource.RunInitialPlaybook (local-exec): TASK [Reboot after DDC] ********************************************************
null_resource.RunInitialPlaybook: Still creating... [25m30s elapsed]
null_resource.RunInitialPlaybook: Still creating... [25m40s elapsed]
null_resource.RunInitialPlaybook: Still creating... [25m50s elapsed]
null_resource.RunInitialPlaybook: Still creating... [26m00s elapsed]
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ddc1.the-austrian-citrix-guy.at] =&gt; {"changed": true, "elapsed": 34, "rebooted": true, "unreachable": false}
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ddc2.the-austrian-citrix-guy.at] =&gt; {"changed": true, "elapsed": 43, "rebooted": true, "unreachable": false}

null_resource.RunInitialPlaybook (local-exec): TASK [Remove DDC RunOnce Key] **************************************************
null_resource.RunInitialPlaybook (local-exec): ok: [tf-cvad-ddc2.the-austrian-citrix-guy.at] =&gt; {"changed": false, "data_changed": false, "data_type_changed": false}
null_resource.RunInitialPlaybook (local-exec): ok: [tf-cvad-ddc1.the-austrian-citrix-guy.at] =&gt; {"changed": false, "data_changed": false, "data_type_changed": false}

null_resource.RunInitialPlaybook (local-exec): TASK [Resume DDC Install] ******************************************************
null_resource.RunInitialPlaybook (local-exec): skipping: [tf-cvad-ddc1.the-austrian-citrix-guy.at] =&gt; {"changed": false, "false_condition": "ddc_resume.changed", "skip_reason": "Conditional result was False"}
null_resource.RunInitialPlaybook (local-exec): skipping: [tf-cvad-ddc2.the-austrian-citrix-guy.at] =&gt; {"changed": false, "false_condition": "ddc_resume.changed", "skip_reason": "Conditional result was False"}

null_resource.RunInitialPlaybook (local-exec): TASK [Alter unwanted Registry Keys-UserIEESC] **********************************
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ddc1.the-austrian-citrix-guy.at] =&gt; {"changed": true, "data_changed": false, "data_type_changed": true}
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ddc2.the-austrian-citrix-guy.at] =&gt; {"changed": true, "data_changed": false, "data_type_changed": true}

null_resource.RunInitialPlaybook (local-exec): TASK [Alter unwanted Registry Keys-AdminIEESC] *********************************
null_resource.RunInitialPlaybook: Still creating... [26m10s elapsed]
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ddc1.the-austrian-citrix-guy.at] =&gt; {"changed": true, "data_changed": false, "data_type_changed": true}
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ddc2.the-austrian-citrix-guy.at] =&gt; {"changed": true, "data_changed": false, "data_type_changed": true}

null_resource.RunInitialPlaybook (local-exec): PLAY RECAP *********************************************************************
null_resource.RunInitialPlaybook (local-exec): tf-cvad-ddc1.the-austrian-citrix-guy.at : ok=12   changed=9    unreachable=0    failed=0    skipped=2    rescued=0    ignored=0
null_resource.RunInitialPlaybook (local-exec): tf-cvad-ddc2.the-austrian-citrix-guy.at : ok=12   changed=9    unreachable=0    failed=0    skipped=2    rescued=0    ignored=0

null_resource.RunInitialPlaybook: Creation complete after 26m11s [id=3927491137125445482]
data.external.CheckAnsibleReturnCode: Reading...
data.external.CheckAnsibleReturnCode: Read complete after 0s [id=-]
null_resource.IfExitCodeIsNotZeroThenHalt: Creating...
null_resource.IfExitCodeIsNotZeroThenHalt: Provisioning with 'local-exec'...
null_resource.IfExitCodeIsNotZeroThenHalt (local-exec): Executing: ["/bin/bash" "-c" "[ \"1\" = \"1\" ] &amp;&amp; echo \"Deploying CVAD: Everything OK\" || { echo \"Deploying the CVAD software and its prerequisites failed. Please review the settings. Halting Terraform.\"; exit 1; }"]
null_resource.IfExitCodeIsNotZeroThenHalt (local-exec): Deploying CVAD: Everything OK
null_resource.IfExitCodeIsNotZeroThenHalt: Creation complete after 0s [id=8978265277076340502]

Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
azadmin@tacg-cicd:/etc/terraform-tmm/CVADXS/_InstallCVAD$ After successful deployment, the CVAD software is installed and can be configured later. Caution: We have noticed that in some environments, the Citrix Host Service does not start after the initial reboot, even though it is set to Automatic start. Therefore, we added a new task to the Ansible Playbook to ensure that the Citrix Host Service starts after the initial reboot and before the following configuration steps.  Since the CVAD software and its dependencies were successfully deployed, we can proceed to the next step.  Part 5: Using Terraform and Ansible to deploy the StoreFront serverThe VM dedicated to holding the StoreFront™ role is now ready for the deployment of the StoreFront components. This is a multi-step approach: Mount the CVAD ISO installer Copy the publicly signed SSL certificate to the DDCs Install all required server roles and features before installing the DDCs Change some necessary registry settings Therefore, we again use the well-proven combination of Terraform and Ansible. Initially, no Citrix software is installed on the VMs. Terraform monitors each Ansible Playbook run and stops further steps if a Playbook fails. Example Terraform snippet: # Terraform Deployment of Citrix CVAD on XenServer 8 - 25.11 
## Definition of XenServer Provider Variables - not all available variables are defined, only these we use
### Reference https://registry.terraform.io/providers/xenserver/xenserver/latest/docs
#### Copy the required Ansible Assets for installing StoreFront to the Ansible Server
###### ***** IMPORTANT: You must set the permissions to the destination directory accordingly (sudo) chmod 777 otherwise the provisioning will fail *****
resource "null_resource" "CopyAnsibleSFAssetsToAnsibleServer" {
  connection {
    type     = var.TACG-TMM-XS-CL-Ansible-Connection-Type
    user     = var.TACG-TMM-XS-CL-Ansible-Connection-User
    password = var.TACG-TMM-XS-CL-Ansible-Connection-Password
    host     = var.TACG-TMM-XS-CL-Ansible-Connection-HostIP
    port     = var.TACG-TMM-XS-CL-Ansible-Connection-Port
    https    = var.TACG-TMM-XS-CL-Ansible-Connection-HTTPS
    timeout  = var.TACG-TMM-XS-CL-Ansible-Connection-Timeout
  }

  # Copy vars-sf.yml file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-Vars-Source
    destination = var.TACG-TMM-XS-CL-Ansible-Vars-Destination
  }

  # Copy inventory.ini file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-Inventory-Source
    destination = var.TACG-TMM-XS-CL-Ansible-Inventory-Destination
  }

  # Copy InitialConfig Playbook file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-InitialPlaybookForSF-Source
    destination = var.TACG-TMM-XS-CL-Ansible-InitialPlaybookForSF-Destination
  }
}

#### Wait 10s for Backgroud Processes to Settle
resource "time_sleep" "wait_10_seconds" {
  depends_on      = [null_resource.CopyAnsibleSFAssetsToAnsibleServer]
  create_duration = "10s"
}

##### Connect to Ansible Interpreter and run Initial Configuration Playbook on SF
resource "null_resource" "RunInitialPlaybook" {
  depends_on = [time_sleep.wait_10_seconds]

  provisioner "local-exec" {
    working_dir = "/etc/ansible"
    command     = var.TACG-TMM-XS-CL-Ansible-InitialCMDForSF
  }
}

##### Check Ansible Return Code
data "external" "CheckAnsibleReturnCode" {
  depends_on = [null_resource.RunInitialPlaybook]
  program    = ["bash", "/etc/ansible/assets/CheckAnsibleReturnCode.sh", "/etc/ansible/assets/ansible_exit_code.txt", "0"]
}

##### Terminate if Return Code is not 0
resource "null_resource" "IfExitCodeIsNotZeroThenHalt" {
  depends_on = [data.external.CheckAnsibleReturnCode]
  provisioner "local-exec" {
    interpreter = ["/bin/bash", "-c"]
    command     = "[ \"${data.external.CheckAnsibleReturnCode.result.found}\" = \"1\" ] &amp;&amp; echo \"Deploying StoreFront - Everything OK\" || { echo \"Deploying the StoreFront software and/or its prerequisites failed. Please review the settings. Halting Terraform.\"; exit 1; }"
  }
}Example Ansible Playbook for deploying and configuring Storefront and all needed further configurations: # Terraform Deployment of Citrix CVAD on XenServer 8 - 25.11 
## For installation and configuration of the Delivery Controllers, StoreFront-Servers and WebStudio-Servers, we use Ansible Playbooks 
### Reference https://docs.ansible.com/ansible/latest/getting_started/index.html
#### We assume that the communication between the Ansible Server and all needed Hosts via WinRM is working as intended - check your Group Policies
##### All hosts and its corresponding communication-related Ansible variables are stored in assets/inventory.ini

###### Prepare the StoreFront Server
- name: Configure the StoreFront Server initially
  hosts: cvad-sf
  vars_files:
    - ./vars-sf.yml
  tasks:

  - name: Create Installer directory  
    ansible.windows.win_file: 
      path: "{{ installdir }}"
      state: directory

  - name: Create Log Folder
    ansible.windows.win_file:
      path: "{{ logdir }}"
      state: directory

  - name: Alter unwanted Registry Keys-UserIEESC
    ansible.windows.win_regedit:
      path: HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A8-37EF-4b3f-8CFC-4F3A74704073}
      name: IsInstalled
      data: 0

  - name: Alter unwanted Registry Keys-AdminIEESC
    ansible.windows.win_regedit:
      path: HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A7-37EF-4b3f-8CFC-4F3A74704073}
      name: IsInstalled
      data: 0

  - name: Copy Citrix iso to the host
    ansible.windows.win_copy:
      src: "{{ sourceiso }}"
      dest: "{{ destiso }}" 
      force: no

  - name: Copy the SSL certificate to the hosts
    ansible.windows.win_copy:
      src: "{{ certificate_source_path }}"
      dest: "{{ certificate_destination_path }}" 
      force: no

  - name: Install Required Server Roles for Citrix
    ansible.windows.win_feature:
      name: "{{ sf_req_roles }}"
      state: present
    register: requirements
    become: true
    become_method: runas
    become_user: SYSTEM

  - name: Reboot when Required Roles need a restart
    ansible.windows.win_reboot:
      post_reboot_delay: 120
    when: requirements.reboot_required

  - name: Install SSL-Certificate on Servers
    ansible.windows.win_certificate_store:
      path: "{{ certificate_destination_path }}" 
      password: "{{ certificate_pfx_password }}"
      key_exportable: yes
      file_type: pkcs12
      store_location: LocalMachine
      key_storage: machine
      state: present
    become: true
    become_method: runas
    become_user: SYSTEM

  - name: Create database server record
    ansible.windows.win_dns_record:
      computer_name: "{{ sf_dns_ip }}"
      name: "{{ sf_dns_name }}"
      type: "A"
      value: "{{ sf_dns_value }}"
      zone: "{{ sf_dns_zone }}"
      state: present
    become: true
    become_method: runas
    become_user: SYSTEM  

  - name: Run installer from mounted ISO
    ansible.windows.win_package:
      path: "{{ install_exe }} "
      arguments: "{{ install_params }}"
      state: present
      expected_return_code: [0, 3, 4, 3010]
      creates_path: C:\ProgramData\Citrix\Storefront Install
    register: sf_install
    become: true
    become_method: runas
    become_user: "{{ ansible_domainuser }}"

  - name: Reboot after sf
    ansible.windows.win_reboot:
    when: sf_install.changed
Running the snippet should fulfill all required steps: At first, all missing Windows features are installed. azadmin@tacg-cicd:/etc/terraform-tmm/CVADXS/_InstallStoreFront$ terraform apply

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create
 &lt;= read (data resources)

Terraform will perform the following actions:

  # data.external.CheckAnsibleReturnCode will be read during apply
  # (depends on a resource or a module with changes pending)
 &lt;= data "external" "CheckAnsibleReturnCode" {
      + id      = (known after apply)
      + program = [
          + "bash",
          + "/etc/ansible/assets/CheckAnsibleReturnCode.sh",
          + "/etc/ansible/assets/ansible_exit_code.txt",
          + "0",
        ]
      + result  = (known after apply)
    }

  # null_resource.CopyAnsibleSFAssetsToAnsibleServer will be created
  + resource "null_resource" "CopyAnsibleSFAssetsToAnsibleServer" {
      + id = (known after apply)
    }

  # null_resource.IfExitCodeIsNotZeroThenHalt will be created
  + resource "null_resource" "IfExitCodeIsNotZeroThenHalt" {
      + id = (known after apply)
    }

  # null_resource.RunInitialPlaybook will be created
  + resource "null_resource" "RunInitialPlaybook" {
      + id = (known after apply)
    }

  # time_sleep.wait_10_seconds will be created
  + resource "time_sleep" "wait_10_seconds" {
      + create_duration = "10s"
      + id              = (known after apply)
    }

Plan: 4 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.CopyAnsibleSFAssetsToAnsibleServer: Creating...
null_resource.CopyAnsibleSFAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyAnsibleSFAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyAnsibleSFAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyAnsibleSFAssetsToAnsibleServer: Creation complete after 0s [id=3119681656948818723]
time_sleep.wait_10_seconds: Creating...
time_sleep.wait_10_seconds: Still creating... [00m10s elapsed]
time_sleep.wait_10_seconds: Creation complete after 10s [id=2025-11-04T16:59:29Z]
null_resource.RunInitialPlaybook: Creating...
null_resource.RunInitialPlaybook: Provisioning with 'local-exec'...
null_resource.RunInitialPlaybook (local-exec): Executing: ["/bin/sh" "-c" "ansible-playbook /etc/ansible/assets/InitialConfigurationForStoreFront.ansible.yml -i /etc/ansible/assets/inventory.ini -v -c winrm; echo $? &gt; /etc/ansible/assets/ansible_exit_code.txt"]
null_resource.RunInitialPlaybook (local-exec): Using /etc/ansible/ansible.cfg as config file

null_resource.RunInitialPlaybook (local-exec): PLAY [Configure the StoreFront Server initially] *******************************

null_resource.RunInitialPlaybook (local-exec): TASK [Gathering Facts] *********************************************************
null_resource.RunInitialPlaybook (local-exec): ok: [tf-cvad-sf.the-austrian-citrix-guy.at]

null_resource.RunInitialPlaybook (local-exec): TASK [Create Installer directory] **********************************************
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-sf.the-austrian-citrix-guy.at] =&gt; {"changed": true}

null_resource.RunInitialPlaybook (local-exec): TASK [Create Log Folder] *******************************************************
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-sf.the-austrian-citrix-guy.at] =&gt; {"changed": true}

null_resource.RunInitialPlaybook (local-exec): TASK [Alter unwanted Registry Keys-UserIEESC] **********************************
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-sf.the-austrian-citrix-guy.at] =&gt; {"changed": true, "data_changed": false, "data_type_changed": true}

null_resource.RunInitialPlaybook (local-exec): TASK [Alter unwanted Registry Keys-AdminIEESC] *********************************
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-sf.the-austrian-citrix-guy.at] =&gt; {"changed": true, "data_changed": false, "data_type_changed": true}

null_resource.RunInitialPlaybook (local-exec): TASK [Copy Citrix iso to the host] *********************************************
null_resource.RunInitialPlaybook: Still creating... [00m10s elapsed]
null_resource.RunInitialPlaybook: Still creating... [00m20s elapsed]
...
null_resource.RunInitialPlaybook: Still creating... [09m50s elapsed]
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-sf.the-austrian-citrix-guy.at] =&gt; {"changed": true, "checksum": null, "dest": "C:\\InstallCVAD\\Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso", "operation": "file_copy", "original_basename": "Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso", "size": 4124442624, "src": "/etc/ansible/isos/Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso"}

null_resource.RunInitialPlaybook (local-exec): TASK [Copy the SSL certificate to the hosts] ***********************************
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-sf.the-austrian-citrix-guy.at] =&gt; {"changed": true, "checksum": null, "dest": "C:\\InstallCVAD\\2025-wildcard.the-austrian-citrix-guy.at.PFX", "operation": "file_copy", "original_basename": "2025-wildcard.the-austrian-citrix-guy.at.PFX", "size": 3593, "src": "/etc/ansible/assets/2025-wildcard.the-austrian-citrix-guy.at.PFX"}

null_resource.RunInitialPlaybook (local-exec): TASK [Install Required Server Roles for Citrix] ********************************
null_resource.RunInitialPlaybook: Still creating... [10m00s elapsed]
null_resource.RunInitialPlaybook: Still creating... [10m10s elapsed]
...
null_resource.RunInitialPlaybook: Still creating... [19m00s elapsed]
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-sf.the-austrian-citrix-guy.at] =&gt; {"changed": true, "exitcode": "Success", "feature_result": [{"display_name": "Message Queuing", "id": 49, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Message Queuing Server", "id": 191, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Message Queuing Services", "id": 190, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ASP.NET 4.8", "id": 429, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": ".NET Framework 3.5 (includes .NET 2.0 and 3.0)", "id": 220, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": ".NET Framework 3.5 Features", "id": 475, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "HTTP Activation", "id": 421, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Message Queuing (MSMQ) Activation", "id": 422, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Named Pipe Activation", "id": 423, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "TCP Activation", "id": 424, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Telnet Client", "id": 44, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Windows Process Activation Service", "id": 41, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Configuration APIs", "id": 217, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Process Model", "id": 219, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Application Development", "id": 147, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Application Initialization", "id": 445, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ASP", "id": 150, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ASP.NET 4.8", "id": 413, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Basic Authentication", "id": 163, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "CGI", "id": 151, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Common HTTP Features", "id": 141, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Default Document", "id": 143, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Directory Browsing", "id": 144, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Dynamic Content Compression", "id": 173, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Request Filtering", "id": 169, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Health and Diagnostics", "id": 155, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "HTTP Errors", "id": 145, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "HTTP Logging", "id": 156, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "HTTP Redirection", "id": 146, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Tracing", "id": 159, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Server Side Includes", "id": 154, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ISAPI Extensions", "id": 152, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ISAPI Filters", "id": 153, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Logging Tools", "id": 157, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "IIS 6 Metabase Compatibility", "id": 179, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "IIS 6 Management Compatibility", "id": 178, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "IIS Management Console", "id": 175, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Management Tools", "id": 174, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": ".NET Extensibility 4.8", "id": 414, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Performance", "id": 171, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "IIS Management Scripts and Tools", "id": 176, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Security", "id": 162, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Web Server (IIS)", "id": 2, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Static Content Compression", "id": 172, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Static Content", "id": 142, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Web Server", "id": 140, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Windows Authentication", "id": 164, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "IIS 6 WMI Compatibility", "id": 180, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}], "reboot_required": false, "success": true}

null_resource.RunInitialPlaybook (local-exec): TASK [Reboot when Required Roles need a restart] *******************************
null_resource.RunInitialPlaybook (local-exec): skipping: [tf-cvad-sf.the-austrian-citrix-guy.at] =&gt; {"changed": false, "false_condition": "requirements.reboot_required", "skip_reason": "Conditional result was False"}

null_resource.RunInitialPlaybook (local-exec): TASK [Install SSL-Certificate on Servers] **************************************
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-sf.the-austrian-citrix-guy.at] =&gt; {"changed": true, "thumbprints": ["0BB52BDDEB78E6E8E32FF3B1949CAA462B8EC386"]}

null_resource.RunInitialPlaybook (local-exec): TASK [Run installer from mounted ISO] ******************************************
null_resource.RunInitialPlaybook: Still creating... [19m10s elapsed]
null_resource.RunInitialPlaybook: Still creating... [19m20s elapsed]
null_resource.RunInitialPlaybook: Still creating... [19m30s elapsed]
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-sf.the-austrian-citrix-guy.at] =&gt; {"changed": true, "checksum": "22A6C5D5BB516ABD6DF5DC6E6CA0C0CEFBB19082", "rc": 3010, "reboot_required": true}

null_resource.RunInitialPlaybook (local-exec): TASK [Reboot after sf] *********************************************************
null_resource.RunInitialPlaybook: Still creating... [19m40s elapsed]
null_resource.RunInitialPlaybook: Still creating... [19m50s elapsed]
null_resource.RunInitialPlaybook: Still creating... [20m00s elapsed]
null_resource.RunInitialPlaybook: Still creating... [20m10s elapsed]
null_resource.RunInitialPlaybook: Still creating... [20m20s elapsed]
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-sf.the-austrian-citrix-guy.at] =&gt; {"changed": true, "elapsed": 42, "rebooted": true, "unreachable": false}

null_resource.RunInitialPlaybook (local-exec): PLAY RECAP *********************************************************************
null_resource.RunInitialPlaybook (local-exec): tf-cvad-sf.the-austrian-citrix-guy.at : ok=11   changed=10   unreachable=0    failed=0    skipped=1    rescued=0    ignored=0

null_resource.RunInitialPlaybook: Creation complete after 20m24s [id=7847329303251523226]
data.external.CheckAnsibleReturnCode: Reading...
data.external.CheckAnsibleReturnCode: Read complete after 0s [id=-]
null_resource.IfExitCodeIsNotZeroThenHalt: Creating...
null_resource.IfExitCodeIsNotZeroThenHalt: Provisioning with 'local-exec'...
null_resource.IfExitCodeIsNotZeroThenHalt (local-exec): Executing: ["/bin/bash" "-c" "[ \"1\" = \"1\" ] &amp;&amp; echo \"Deploying StoreFront - Everything OK\" || { echo \"Deploying the StoreFront software and/or its prerequisites failed. Please review the settings. Halting Terraform.\"; exit 1; }"]
null_resource.IfExitCodeIsNotZeroThenHalt (local-exec): Deploying StoreFront - Everything OK
null_resource.IfExitCodeIsNotZeroThenHalt: Creation complete after 0s [id=6843415180162545622]

Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
azadmin@tacg-cicd:/etc/terraform-tmm/CVADXS/_InstallStoreFront$ After successful deployment, the StoreFront software is installed and can be configured later.  Since the StoreFront software and its dependencies were successfully deployed, we can proceed to the next step.  Part 6: Using Terraform and Ansible to deploy the Web Studio ServerThe VM dedicated to holding the Web Studio role is now ready for the deployment of the Web Studio components. This is a multi-step approach: Mount the CVAD ISO installer Copy the publicly signed SSL certificate to the Web Studio server Install all required server roles and features before installing the Web Studio server Change some necessary registry settings Configure the Web Studio server as a proxy for the DDCs Therefore, we again use the well-proven combination of Terraform and Ansible. Initially, no Citrix software is installed on the VM again. Terraform monitors each Ansible Playbook run and stops further steps if a Playbook fails. Example Terraform snippet: # Terraform Deployment of Citrix CVAD on XenServer 8 - 25.11 
## Definition of XenServer Provider Variables - not all available variables are defined, only these we use
### Reference https://registry.terraform.io/providers/xenserver/xenserver/latest/docs
#### Copy the required Ansible Assets for installing WebStudio to the Ansible Server
###### ***** IMPORTANT: You must set the permissions to the destination directory accordingly (sudo) chmod 777 otherwise the provisioning will fail *****
resource "null_resource" "CopyAnsibleWSAssetsToAnsibleServer" {
  connection {
    type     = var.TACG-TMM-XS-CL-Ansible-Connection-Type
    user     = var.TACG-TMM-XS-CL-Ansible-Connection-User
    password = var.TACG-TMM-XS-CL-Ansible-Connection-Password
    host     = var.TACG-TMM-XS-CL-Ansible-Connection-HostIP
  }

  # Copy vars-ws.yml file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-Vars-Source
    destination = var.TACG-TMM-XS-CL-Ansible-Vars-Destination
  }

  # Copy inventory.ini file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-Inventory-Source
    destination = var.TACG-TMM-XS-CL-Ansible-Inventory-Destination
  }

  # Copy InitialConfig Playbook file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-InitialPlaybookForWS-Source
    destination = var.TACG-TMM-XS-CL-Ansible-InitialPlaybookForWS-Destination
  }

  # Copy SetProxy Playbook file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-SetProxyPlaybookWS-Source
    destination = var.TACG-TMM-XS-CL-Ansible-InitialPlaybookForWS-Destination
  }
}

#### Wait 10s for Backgroud Processes to Settle
resource "time_sleep" "wait_10_seconds" {
  depends_on      = [null_resource.CopyAnsibleWSAssetsToAnsibleServer]
  create_duration = "10s"
}

#### Run initial configuration
##### Connect to Ansible Interpreter and run Initial Configuration Playbook on WS
resource "null_resource" "RunInitialPlaybook" {
  depends_on = [time_sleep.wait_10_seconds]

  provisioner "local-exec" {
    working_dir = "/etc/ansible"
    command     = var.TACG-TMM-XS-CL-Ansible-InitialCMDForWS
  }
}

##### Check Ansible Return Code
data "external" "CheckAnsibleReturnCode" {
  depends_on = [null_resource.RunInitialPlaybook]
  program    = ["bash", "/etc/ansible/assets/CheckAnsibleReturnCode.sh", "/etc/ansible/assets/ansible_exit_code.txt", "0"]
}

##### Terminate if Return Code is not 0
resource "null_resource" "IfExitCodeIsNotZeroThenHalt" {
  depends_on = [data.external.CheckAnsibleReturnCode]
  provisioner "local-exec" {
    interpreter = ["/bin/bash", "-c"]
    command     = "[ \"${data.external.CheckAnsibleReturnCode.result.found}\" = \"1\" ] &amp;&amp; echo \"Deploying WebStudio: Everything OK\" || { echo \"Deploying the WebStudio software and/or its prerequisites failed. Please review the settings. Halting Terraform.\"; exit 1; }"
  }
}

#### Wait 10s for Backgroud Processes to Settle
resource "time_sleep" "wait_10_seconds_IC" {
  depends_on      = [null_resource.IfExitCodeIsNotZeroThenHalt]
  create_duration = "10s"
}
##### End of intitial configuration

#### Run SetProxy configuration
##### Connect to Ansible Interpreter and run SetProxy Playbook on WS
resource "null_resource" "RunSetProxyConfigurationPlaybook" {
  depends_on = [time_sleep.wait_10_seconds_IC]

  provisioner "local-exec" {
    working_dir = "/etc/ansible"
    command     = var.TACG-TMM-XS-CL-Ansible-SetProxyPlaybookWS
  }
}

##### Check Ansible Return Code
data "external" "CheckAnsibleReturnCodePC" {
  depends_on = [null_resource.RunSetProxyConfigurationPlaybook]
  program    = ["bash", "/etc/ansible/assets/CheckAnsibleReturnCode.sh", "/etc/ansible/assets/ansible_exit_code.txt", "0"]
}

##### Terminate if Return Code is not 0
resource "null_resource" "IfExitCodeIsNotZeroThenHaltPC" {
  depends_on = [data.external.CheckAnsibleReturnCodePC]
  provisioner "local-exec" {
    interpreter = ["/bin/bash", "-c"]
    command     = "[ \"${data.external.CheckAnsibleReturnCodePC.result.found}\" = \"1\" ] &amp;&amp; echo \"Configuring WebStudio as Proxy: Everything OK\" || { echo \"Configuring WebStudio as Proxy failed. Please review the settings. Halting Terraform.\"; exit 1; }"
  }
}

#### Wait 10s for Backgroud Processes to Settle
resource "time_sleep" "wait_10_seconds_IC" {
  depends_on      = [null_resource.IfExitCodeIsNotZeroThenHalt]
  create_duration = "10s"
}
##### End of intitial configuration
Example Ansible Playbook for deploying and configuring WebStudio and all needed further configurations: # Terraform Deployment of Citrix CVAD on XenServer 8 - 25.11 
## For installation and configuration of the Delivery Controllers, StoreFront-Servers and WebStudio-Servers, we use Ansible Playbooks 
### Reference https://docs.ansible.com/ansible/latest/getting_started/index.html
#### We assume that the communication between the Ansible Server and all needed Hosts via WinRM is working as intended - check your Group Policies
##### All hosts and its corresponding communication-related Ansible variables are stored in assets/inventory.ini

###### Prepare the WebStudio Server
- name: Configure the WebStudio Server initially
  hosts: cvad-ws
  vars_files:
    - ./vars-ws.yml
  tasks:

  - name: Create Installer directory  
    ansible.windows.win_file: 
      path: "{{ installdir }}"
      state: directory

  - name: Create Log Folder
    ansible.windows.win_file:
      path: "{{ logdir }}"
      state: directory

  - name: Alter unwanted Registry Keys-UserIEESC
    ansible.windows.win_regedit:
      path: HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A8-37EF-4b3f-8CFC-4F3A74704073}
      name: IsInstalled
      data: 0

  - name: Alter unwanted Registry Keys-AdminIEESC
    ansible.windows.win_regedit:
      path: HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A7-37EF-4b3f-8CFC-4F3A74704073}
      name: IsInstalled
      data: 0

  - name: Copy Citrix iso to the host
    ansible.windows.win_copy:
      src: "{{ sourceiso }}"
      dest: "{{ destiso }}" 
      force: no

  - name: Copy the SSL certificate to the hosts
    ansible.windows.win_copy:
      src: "{{ certificate_source_path }}"
      dest: "{{ certificate_destination_path }}" 
      force: no

  - name: Install Required Server Roles for Citrix
    ansible.windows.win_feature:
      name: "{{ ws_req_roles }}"
      state: present
    register: requirements
    become: true
    become_method: runas
    become_user: SYSTEM

  - name: Reboot when Required Roles need a restart
    ansible.windows.win_reboot:
      post_reboot_delay: 120
    when: requirements.reboot_required

  - name: Install SSL-Certificate on Servers
    ansible.windows.win_certificate_store:
      path: "{{ certificate_destination_path }}" 
      password: "{{ certificate_pfx_password }}"
      key_exportable: yes
      file_type: pkcs12
      store_location: LocalMachine
      key_storage: machine
      state: present
    become: true
    become_method: runas
    become_user: SYSTEM

  - name: Run installer from mounted ISO
    ansible.windows.win_package:
      path: "{{ install_exe }} "
      arguments: "{{ install_params }}"
      state: present
      expected_return_code: [0, 3, 4, 3010]
      creates_path: C:\ProgramData\Citrix\Storefront Install
    register: ws_install
    become: true
    become_method: runas
    become_user: "{{ ansible_domainuser }}"

  - name: Reboot after ws
    ansible.windows.win_reboot:
    when: ws_install.changedExample Ansible Playbook for configuring Web Studio as Proxy: # Terraform Deployment of Citrix CVAD on XenServer 8 - 25.11 
## For installation and configuration of the Delivery Controllers, StoreFront-Servers and WebStudio-Servers, we use Ansible Playbooks 
### Reference https://docs.ansible.com/ansible/latest/getting_started/index.html
#### We assume that the communication between the Ansible Server and all needed Hosts via WinRM is working as intended - check your Group Policies
##### All hosts and its corresponding communication-related Ansible variables are stored in assets/inventory.ini
###### Configure WS as Proxy
- name: Configure WebStudio as Proxy
  hosts: cvad-ws
  gather_facts: no

  tasks:
    - name: Run PS to enable Proxy Mode
      ansible.windows.win_powershell:
        script: |
          &amp; "c:\Program Files\Citrix\Web Studio\Tool\StudioConfig.exe" --enableproxy --proxyserver "tf-cvad-ws.the-austrian-citrix-guy.at"
      register: runps

    - name: Print results
      ansible.builtin.debug:
        var: runps
Running the snippet should fulfill all needed steps: azadmin@tacg-cicd:/etc/terraform-tmm/CVADXS/_InstallWebStudio$ terraform apply

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
  + create
 &lt;= read (data resources)

Terraform will perform the following actions:

  # data.external.CheckAnsibleReturnCode will be read during apply
  # (depends on a resource or a module with changes pending)
 &lt;= data "external" "CheckAnsibleReturnCode" {
      + id      = (known after apply)
      + program = [
          + "bash",
          + "/etc/ansible/assets/CheckAnsibleReturnCode.sh",
          + "/etc/ansible/assets/ansible_exit_code.txt",
          + "0",
        ]
      + result  = (known after apply)
    }

  # null_resource.CopyAnsibleWSAssetsToAnsibleServer will be created
  + resource "null_resource" "CopyAnsibleWSAssetsToAnsibleServer" {
      + id = (known after apply)
    }

  # null_resource.IfExitCodeIsNotZeroThenHalt will be created
  + resource "null_resource" "IfExitCodeIsNotZeroThenHalt" {
      + id = (known after apply)
    }

  # null_resource.RunInitialPlaybook will be created
  + resource "null_resource" "RunInitialPlaybook" {
      + id = (known after apply)
    }

  # time_sleep.wait_10_seconds will be created
  + resource "time_sleep" "wait_10_seconds" {
      + create_duration = "10s"
      + id              = (known after apply)
    }

Plan: 4 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.CopyAnsibleWSAssetsToAnsibleServer: Creating...
null_resource.CopyAnsibleWSAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyAnsibleWSAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyAnsibleWSAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyAnsibleWSAssetsToAnsibleServer: Creation complete after 1s [id=8218302926752497323]
time_sleep.wait_10_seconds: Creating...
time_sleep.wait_10_seconds: Still creating... [00m10s elapsed]
time_sleep.wait_10_seconds: Creation complete after 10s [id=2025-11-12T07:52:23Z]
null_resource.RunInitialPlaybook: Creating...
null_resource.RunInitialPlaybook: Provisioning with 'local-exec'...
null_resource.RunInitialPlaybook (local-exec): Executing: ["/bin/sh" "-c" "ansible-playbook /etc/ansible/assets/InitialConfigurationForWS.ansible.yml -i /etc/ansible/assets/inventory.ini -v -c winrm; echo $? &gt; /etc/ansible/assets/ansible_exit_code.txt"]

null_resource.RunInitialPlaybook (local-exec): Using /etc/ansible/ansible.cfg as config file

null_resource.RunInitialPlaybook (local-exec): PLAY [Configure the WebStudio Server initially] ********************************

null_resource.RunInitialPlaybook (local-exec): TASK [Gathering Facts] *********************************************************
null_resource.RunInitialPlaybook (local-exec): ok: [tf-cvad-ws.the-austrian-citrix-guy.at]

null_resource.RunInitialPlaybook (local-exec): TASK [Create Installer directory] **********************************************
null_resource.RunInitialPlaybook (local-exec): ok: [tf-cvad-ws.the-austrian-citrix-guy.at] =&gt; {"changed": false}

null_resource.RunInitialPlaybook (local-exec): TASK [Create Log Folder] *******************************************************
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ws.the-austrian-citrix-guy.at] =&gt; {"changed": true}

null_resource.RunInitialPlaybook (local-exec): TASK [Alter unwanted Registry Keys-UserIEESC] **********************************
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ws.the-austrian-citrix-guy.at] =&gt; {"changed": true, "data_changed": false, "data_type_changed": true}

null_resource.RunInitialPlaybook (local-exec): TASK [Alter unwanted Registry Keys-AdminIEESC] *********************************
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ws.the-austrian-citrix-guy.at] =&gt; {"changed": true, "data_changed": false, "data_type_changed": true}

null_resource.RunInitialPlaybook (local-exec): TASK [Copy Citrix iso to the host] *********************************************
null_resource.RunInitialPlaybook: Still creating... [00m10s elapsed]

...

null_resource.RunInitialPlaybook: Still creating... [09m20s elapsed]
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ws.the-austrian-citrix-guy.at] =&gt; {"changed": true, "checksum": null, "dest": "C:\\InstallCVAD\\Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso", "operation": "file_copy", "original_basename": "Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso", "size": 4124442624, "src": "/etc/ansible/isos/Citrix_Virtual_Apps_and_Desktops_7_2507_LTSR.iso"}

null_resource.RunInitialPlaybook (local-exec): TASK [Copy the SSL certificate to the hosts] ***********************************
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ws.the-austrian-citrix-guy.at] =&gt; {"changed": true, "checksum": null, "dest": "C:\\InstallCVAD\\2025-wildcard.the-austrian-citrix-guy.at.PFX", "operation": "file_copy", "original_basename": "2025-wildcard.the-austrian-citrix-guy.at.PFX", "size": 3593, "src": "/etc/ansible/assets/2025-wildcard.the-austrian-citrix-guy.at.PFX"}

null_resource.RunInitialPlaybook (local-exec): TASK [Install Required Server Roles for Citrix] ********************************
null_resource.RunInitialPlaybook: Still creating... [09m30s elapsed]

...

null_resource.RunInitialPlaybook: Still creating... [18m40s elapsed]
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ws.the-austrian-citrix-guy.at] =&gt; {"changed": true, "exitcode": "Success", "feature_result": [{"display_name": "Message Queuing", "id": 49, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Message Queuing Server", "id": 191, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Message Queuing Services", "id": 190, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ASP.NET 4.8", "id": 429, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": ".NET Framework 3.5 (includes .NET 2.0 and 3.0)", "id": 220, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": ".NET Framework 3.5 Features", "id": 475, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "HTTP Activation", "id": 421, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Message Queuing (MSMQ) Activation", "id": 422, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Named Pipe Activation", "id": 423, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "TCP Activation", "id": 424, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Telnet Client", "id": 44, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Windows Process Activation Service", "id": 41, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Configuration APIs", "id": 217, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Process Model", "id": 219, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Application Development", "id": 147, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Application Initialization", "id": 445, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ASP", "id": 150, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ASP.NET 4.8", "id": 413, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Basic Authentication", "id": 163, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "CGI", "id": 151, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Common HTTP Features", "id": 141, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Default Document", "id": 143, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Directory Browsing", "id": 144, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Dynamic Content Compression", "id": 173, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Request Filtering", "id": 169, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Health and Diagnostics", "id": 155, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "HTTP Errors", "id": 145, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "HTTP Logging", "id": 156, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "HTTP Redirection", "id": 146, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Tracing", "id": 159, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Server Side Includes", "id": 154, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ISAPI Extensions", "id": 152, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "ISAPI Filters", "id": 153, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Logging Tools", "id": 157, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "IIS 6 Metabase Compatibility", "id": 179, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "IIS 6 Management Compatibility", "id": 178, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "IIS Management Console", "id": 175, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Management Tools", "id": 174, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": ".NET Extensibility 4.8", "id": 414, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Performance", "id": 171, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "IIS Management Scripts and Tools", "id": 176, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Security", "id": 162, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Web Server (IIS)", "id": 2, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Static Content Compression", "id": 172, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Static Content", "id": 142, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Web Server", "id": 140, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "Windows Authentication", "id": 164, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}, {"display_name": "IIS 6 WMI Compatibility", "id": 180, "message": [], "reboot_required": false, "skip_reason": "NotSkipped", "success": true}], "reboot_required": false, "success": true}

null_resource.RunInitialPlaybook (local-exec): TASK [Reboot when Required Roles need a restart] *******************************
null_resource.RunInitialPlaybook (local-exec): skipping: [tf-cvad-ws.the-austrian-citrix-guy.at] =&gt; {"changed": false, "false_condition": "requirements.reboot_required", "skip_reason": "Conditional result was False"}

null_resource.RunInitialPlaybook (local-exec): TASK [Install SSL-Certificate on Servers] **************************************
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ws.the-austrian-citrix-guy.at] =&gt; {"changed": true, "thumbprints": ["0BB52BDDEB78E6E8E32FF3B1949CAA462B8EC386"]}

null_resource.RunInitialPlaybook (local-exec): TASK [Run installer from mounted ISO] ******************************************
null_resource.RunInitialPlaybook: Still creating... [18m50s elapsed]
...
null_resource.RunInitialPlaybook: Still creating... [22m20s elapsed]
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ws.the-austrian-citrix-guy.at] =&gt; {"changed": true, "checksum": "EADE21C7B09DED5626F3BB51D4DF7A4C0E7AC607", "rc": 0, "reboot_required": false}

null_resource.RunInitialPlaybook (local-exec): TASK [Reboot after sf] *********************************************************
null_resource.RunInitialPlaybook: Still creating... [22m30s elapsed]
null_resource.RunInitialPlaybook: Still creating... [22m40s elapsed]
null_resource.RunInitialPlaybook (local-exec): changed: [tf-cvad-ws.the-austrian-citrix-guy.at] =&gt; {"changed": true, "elapsed": 23, "rebooted": true, "unreachable": false}

null_resource.RunInitialPlaybook (local-exec): PLAY RECAP *********************************************************************
null_resource.RunInitialPlaybook (local-exec): tf-cvad-ws.the-austrian-citrix-guy.at : ok=11   changed=9    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0

null_resource.RunInitialPlaybook: Creation complete after 22m45s [id=7191293509250199804]
data.external.CheckAnsibleReturnCode: Reading...
data.external.CheckAnsibleReturnCode: Read complete after 0s [id=-]
null_resource.IfExitCodeIsNotZeroThenHalt: Creating...
null_resource.IfExitCodeIsNotZeroThenHalt: Provisioning with 'local-exec'...
null_resource.IfExitCodeIsNotZeroThenHalt (local-exec): Executing: ["/bin/bash" "-c" "[ \"1\" = \"1\" ] &amp;&amp; echo \"Deploying WebStudio: Everything OK\" || { echo \"Deploying the WebStudio software and/or its prerequisites failed. Please review the settings. Halting Terraform.\"; exit 1; }"]
null_resource.IfExitCodeIsNotZeroThenHalt (local-exec): Deploying WebStudio: Everything OK
null_resource.IfExitCodeIsNotZeroThenHalt: Creation complete after 0s [id=416418525198442619]

Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
azadmin@tacg-cicd:/etc/terraform-tmm/CVADXS/_InstallWebStudio$
After successful deployment, the WebStudio server is now installed and ready. We can continue with the next step.  Part 7: Using Terraform and Ansible to change the SSL BindingsWe want to configure all transport in our environment as securely as possible. Therefore, Terraform and Ansible take additional steps to configure all SSL bindings across all servers and listeners to enable complete HTTPS communication. Where applicable, the SSL certificate is changed to a publicly signed certificate to avoid certificate trust errors. The change is applied by a PowerShell script, which Ansible runs on each applicable host. To obtain all required elevated privileges, we use PsExec64.exe as the host to run the PowerShell scripts. Terraform monitors each Ansible Playbook run and stops further steps if a Playbook fails. Example Terraform snippet: # Terraform Deployment of Citrix CVAD on XenServer 8 - 25.11 
## Definition of XenServer Provider Variables - not all available variables are defined, only these we use
### Reference https://registry.terraform.io/providers/xenserver/xenserver/latest/docs

#### Copy the required Ansible Assets to the Ansible Server
###### ***** IMPORTANT: You must set the permissions to the destination directory accordingly (sudo) chmod 777 otherwise the provisioning will fail *****
resource "null_resource" "CopyAnsibleSiteAssetsToAnsibleServer" {
  connection {
    type     = var.TACG-TMM-XS-CL-WinRM-Connection-Type
    user     = var.TACG-TMM-XS-CL-WinRM-Connection-User
    password = var.TACG-TMM-XS-CL-WinRM-Connection-Password
    host     = var.TACG-TMM-XS-CL-WinRM-Connection-Ansible
    port     = var.TACG-TMM-XS-CL-WinRM-Connection-Port
    https    = var.TACG-TMM-XS-CL-WinRM-Connection-HTTPS
    timeout  = var.TACG-TMM-XS-CL-WinRM-Connection-Timeout
  }

  # Copy AdditionalSiteConfiguration Playbook file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-ChangeSSLOnServers-Playbook-Source
    destination = var.TACG-TMM-XS-CL-Ansible-ChangeSSLOnServers-Playbook-Destination
  }
}

#### Copy the required PowerShell Scripts to the DDC1
resource "null_resource" "CopyPowerShellScriptsToDDC1" {
  connection {
    type     = var.TACG-TMM-XS-CL-WinRM-Connection-Type
    user     = var.TACG-TMM-XS-CL-WinRM-Connection-User
    password = var.TACG-TMM-XS-CL-WinRM-Connection-Password
    host     = var.TACG-TMM-XS-CL-WinRM-Connection-DDC1
    port     = var.TACG-TMM-XS-CL-WinRM-Connection-Port
    https    = var.TACG-TMM-XS-CL-WinRM-Connection-HTTPS
    timeout  = var.TACG-TMM-XS-CL-WinRM-Connection-Timeout
  }

  # Copy ChangeSSLCOnServers.ps1 file To the DDC1
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-ChangeSSLOnServers-Source
    destination = var.TACG-TMM-XS-CL-Ansible-ChangeSSLOnServers-Destination
  }

  # Copy PSExec64.exe file To the DDC1
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-PsExec64-Source
    destination = var.TACG-TMM-XS-CL-PsExec64-Destination
  }
}

#### Copy the required PowerShell Scripts to the DDC2
resource "null_resource" "CopyPowerShellScriptsToDDC2" {
  connection {
    type     = var.TACG-TMM-XS-CL-WinRM-Connection-Type
    user     = var.TACG-TMM-XS-CL-WinRM-Connection-User
    password = var.TACG-TMM-XS-CL-WinRM-Connection-Password
    host     = var.TACG-TMM-XS-CL-WinRM-Connection-DDC2
    port     = var.TACG-TMM-XS-CL-WinRM-Connection-Port
    https    = var.TACG-TMM-XS-CL-WinRM-Connection-HTTPS
    timeout  = var.TACG-TMM-XS-CL-WinRM-Connection-Timeout
  }

  # Copy ChangeSSLCOnServers.ps1 file To the DDC2
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-ChangeSSLOnServers-Source
    destination = var.TACG-TMM-XS-CL-Ansible-ChangeSSLOnServers-Destination
  }

  # Copy PSExec64.exe file To the DDC2
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-PsExec64-Source
    destination = var.TACG-TMM-XS-CL-PsExec64-Destination
  }
}

#### Copy the required PowerShell Scripts to the SF
resource "null_resource" "CopyPowerShellScriptsToSF" {
  connection {
    type     = var.TACG-TMM-XS-CL-WinRM-Connection-Type
    user     = var.TACG-TMM-XS-CL-WinRM-Connection-User
    password = var.TACG-TMM-XS-CL-WinRM-Connection-Password
    host     = var.TACG-TMM-XS-CL-WinRM-Connection-SF
    port     = var.TACG-TMM-XS-CL-WinRM-Connection-Port
    https    = var.TACG-TMM-XS-CL-WinRM-Connection-HTTPS
    timeout  = var.TACG-TMM-XS-CL-WinRM-Connection-Timeout
  }

  # Copy ChangeSSLCOnServers.ps1 file To the SF
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-ChangeSSLOnServers-Source
    destination = var.TACG-TMM-XS-CL-Ansible-ChangeSSLOnServers-Destination
  }

  # Copy PSExec64.exe file To the SF
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-PsExec64-Source
    destination = var.TACG-TMM-XS-CL-PsExec64-Destination
  }
}

#### Copy the required PowerShell Scripts to the WS
resource "null_resource" "CopyPowerShellScriptsToWS" {
  connection {
    type     = var.TACG-TMM-XS-CL-WinRM-Connection-Type
    user     = var.TACG-TMM-XS-CL-WinRM-Connection-User
    password = var.TACG-TMM-XS-CL-WinRM-Connection-Password
    host     = var.TACG-TMM-XS-CL-WinRM-Connection-WS
    port     = var.TACG-TMM-XS-CL-WinRM-Connection-Port
    https    = var.TACG-TMM-XS-CL-WinRM-Connection-HTTPS
    timeout  = var.TACG-TMM-XS-CL-WinRM-Connection-Timeout
  }

  # Copy ChangeSSLCOnServers.ps1 file To the WS
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-ChangeSSLOnServers-Source
    destination = var.TACG-TMM-XS-CL-Ansible-ChangeSSLOnServers-Destination
  }

  # Copy PSExec64.exe file To the WS
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-PsExec64-Source
    destination = var.TACG-TMM-XS-CL-PsExec64-Destination
  }
}

resource "time_sleep" "wait_10_seconds" {
  depends_on      = [null_resource.CopyAnsibleSiteAssetsToAnsibleServer, null_resource.CopyPowerShellScriptsToDDC1]
  create_duration = "10s"
}

##### Change the SSL Bindings Start
##### Connect to Ansible Interpreter and run ChangeSSLOnServers-Playbook
resource "null_resource" "RunChangeSSLPlaybook" {
  depends_on = [time_sleep.wait_10_seconds]

  provisioner "local-exec" {
    working_dir = "/etc/ansible"
    command     = var.TACG-TMM-XS-CL-Ansible-ChangeSSLOnServers-CMD
  }
}

##### Check Ansible Return Code
data "external" "CheckAnsibleReturnCode" {
  depends_on = [null_resource.RunChangeSSLPlaybook]
  program    = ["bash", "/etc/ansible/assets/CheckAnsibleReturnCode.sh", "/etc/ansible/assets/ansible_exit_code.txt", "0"]
}

##### Terminate if Return Code is not 0
resource "null_resource" "IfExitCodeIsNotZeroThenHalt" {
  depends_on = [data.external.CheckAnsibleReturnCode]
  provisioner "local-exec" {
    interpreter = ["/bin/bash", "-c"]
    command     = "[ \"${data.external.CheckAnsibleReturnCode.result.found}\" = \"1\" ] &amp;&amp; echo \"Change SSL Bindings - Everything OK\" || { echo \"Changing the SSL bindings failed. Please review the settings. Halting Terraform.\"; exit 1; }"
  }
}

##### Change the SSL Bindings EndExample Ansible Playbook for changing the SSL bindings on the DDCs: # Terraform Deployment of Citrix CVAD on XenServer 8 - 25.11 
## For installation and configuration of the Delivery Controllers, StoreFront-Servers and WebStudio-Servers, we use Ansible Playbooks 
### Reference https://docs.ansible.com/ansible/latest/getting_started/index.html
#### We assume that the communication between the Ansible Server and all needed Hosts via WinRM is working as intended - check your Group Policies
##### All hosts and its corresponding communication-related Ansible variables are stored in assets/inventory.ini
###### Change SSL Certificate bindings to the previously uploaded public SSL Certificate by running a PowerShell script on all CVAD servers
- name: Change SSL Certificate bindings
  hosts: cvad-ddcs
  gather_facts: no
  collections:
    - community.windows
  vars:
    domain_user: "TACG\\XxXxXxXxX"
    domain_pass: "!XxXxXxXxXxXxXxXxX!"
    psexec_path: "C:\\InstallCVAD\\PsExec64.exe"
    remote_script: "C:\\InstallCVAD\\ChangeSSLOnServers.ps1"
    ansible_user: "svc_ansible"      
    ansible_password: "!XxXxXxXxXxXxXxXxX!"    
    ansible_connection: winrm
    ansible_winrm_transport: certificate

  tasks:
   - name: Run the ChangeSSLOnDDCs-PowerShell script as Domain Administrator
     community.windows.win_psexec:
       command: powershell.exe -NoProfile -ExecutionPolicy Bypass -File "{{ remote_script }}"
       executable: "{{ psexec_path }}"
       username: "{{ domain_user }}"
       password: "{{ domain_pass }}"
       elevated: true
       noprofile: true
       interactive: true
       wait: true
Example PowerShell script to change the SSL bindings: Try {  
$friendlyName = "*.the-austrian-citrix-guy.at"
$Thumbprint = (Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object {$_.FriendlyName -eq $friendlyName}).Thumbprint -join ';'
$ThumbprintC = ($ThumbPrint.Replace(" ",""))

netsh http delete sslcert ipport=0.0.0.0:443

$NGuid = [guid]::NewGuid().ToString("B")   

netsh http add sslcert ipport=0.0.0.0:443 certhash=$ThumbprintC appid=$NGuid disablelegacytls=enable

netsh http show sslcert

    } catch {
    $logFile = "C:\Logs\MyLogFile.log"
    $errorMessage = $_.Exception.Message
    Add-Content -Path $logFile -Value "$(Get-Date) - ChangeSSLCertificateBindings: Error: $errorMessage"
    Write-Output "An error occurred trying to change the SSL-Certificate bindings (error: $($Error[0]))"
    Exit 1  

    }Running the snippet should fulfill all needed steps – example for running it on the DDCs: azadmin@tacg-cicd:/etc/terraform-tmm/CVADXS/_ChangeSSLOnServers$ terraform apply

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create
 &lt;= read (data resources)

Terraform will perform the following actions:

  # data.external.CheckAnsibleReturnCode will be read during apply
  # (depends on a resource or a module with changes pending)
 &lt;= data "external" "CheckAnsibleReturnCode" {
      + id      = (known after apply)
      + program = [
          + "bash",
          + "/etc/ansible/assets/CheckAnsibleReturnCode.sh",
          + "/etc/ansible/assets/ansible_exit_code.txt",
          + "0",
        ]
      + result  = (known after apply)
    }

  # null_resource.CopyAnsibleSiteAssetsToAnsibleServer will be created
  + resource "null_resource" "CopyAnsibleSiteAssetsToAnsibleServer" {
      + id = (known after apply)
    }

  # null_resource.CopyPowerShellScriptsToDDC1 will be created
  + resource "null_resource" "CopyPowerShellScriptsToDDC1" {
      + id = (known after apply)
    }

  # null_resource.CopyPowerShellScriptsToDDC2 will be created
  + resource "null_resource" "CopyPowerShellScriptsToDDC2" {
      + id = (known after apply)
    }

  # null_resource.CopyPowerShellScriptsToSF will be created
  + resource "null_resource" "CopyPowerShellScriptsToSF" {
      + id = (known after apply)
    }

  # null_resource.CopyPowerShellScriptsToWS will be created
  + resource "null_resource" "CopyPowerShellScriptsToWS" {
      + id = (known after apply)
    }

  # null_resource.IfExitCodeIsNotZeroThenHalt will be created
  + resource "null_resource" "IfExitCodeIsNotZeroThenHalt" {
      + id = (known after apply)
    }

  # null_resource.RunChangeSSLPlaybook will be created
  + resource "null_resource" "RunChangeSSLPlaybook" {
      + id = (known after apply)
    }

  # time_sleep.wait_10_seconds will be created
  + resource "time_sleep" "wait_10_seconds" {
      + create_duration = "10s"
      + 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

null_resource.CopyAnsibleSiteAssetsToAnsibleServer: Creating...
null_resource.CopyPowerShellScriptsToWS: Creating...
null_resource.CopyPowerShellScriptsToDDC1: Creating...
null_resource.CopyPowerShellScriptsToDDC1: Provisioning with 'file'...
null_resource.CopyPowerShellScriptsToWS: Provisioning with 'file'...
null_resource.CopyPowerShellScriptsToSF: Creating...
null_resource.CopyPowerShellScriptsToDDC2: Creating...
null_resource.CopyAnsibleSiteAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPowerShellScriptsToDDC2: Provisioning with 'file'...
null_resource.CopyPowerShellScriptsToSF: Provisioning with 'file'...
null_resource.CopyAnsibleSiteAssetsToAnsibleServer: Creation complete after 0s [id=8882922836300404953]
#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;
...
null_resource.CopyPowerShellScriptsToDDC1: Creation complete after 10s [id=4814951330333809760]
null_resource.CopyPowerShellScriptsToSF: Creation complete after 10s [id=6247443398511140586]
null_resource.CopyPowerShellScriptsToDDC2: Creation complete after 10s [id=4396737003381006003]

...
time_sleep.wait_10_seconds: Still creating... [00m10s elapsed]
time_sleep.wait_10_seconds: Creation complete after 10s [id=2025-11-13T12:24:08Z]
null_resource.RunChangeSSLPlaybook: Creating...
null_resource.RunChangeSSLPlaybook: Provisioning with 'local-exec'...
null_resource.RunChangeSSLPlaybook (local-exec): Executing: ["/bin/sh" "-c" "ansible-playbook /etc/ansible/assets/ChangeSSLOnServers.ansible.yml -i /etc/ansible/assets/inventory.ini -vvv -c winrm; echo $? &gt; /etc/ansible/assets/ansible_exit_code.txt"]
null_resource.RunChangeSSLPlaybook (local-exec): Using /etc/ansible/ansible.cfg as config file

null_resource.RunChangeSSLPlaybook (local-exec): PLAYBOOK: ChangeSSLOnServers.ansible.yml ***************************************
null_resource.RunChangeSSLPlaybook (local-exec): 1 plays in /etc/ansible/assets/ChangeSSLOnServers.ansible.yml

null_resource.RunChangeSSLPlaybook (local-exec): PLAY [Change SSL Certificate bindings] *****************************************

null_resource.RunChangeSSLPlaybook (local-exec): TASK [Run the ChangeSSLOnDDCs-PowerShell script as Domain Administrator] *******
null_resource.RunChangeSSLPlaybook (local-exec): task path: /etc/ansible/assets/ChangeSSLOnServers.ansible.yml:26
null_resource.RunChangeSSLPlaybook (local-exec): Using module file /usr/lib/python3/dist-packages/ansible_collections/community/windows/plugins/modules/win_psexec.ps1
null_resource.RunChangeSSLPlaybook (local-exec): Pipelining is enabled.
null_resource.RunChangeSSLPlaybook (local-exec): &lt;tf-cvad-ddc1.the-austrian-citrix-guy.at&gt; ESTABLISH WINRM CONNECTION FOR USER: svc_ansible on PORT 5986 TO tf-cvad-ddc1.the-austrian-citrix-guy.at
null_resource.RunChangeSSLPlaybook (local-exec): EXEC (via pipeline wrapper)
null_resource.RunChangeSSLPlaybook (local-exec): changed: [tf-cvad-ddc1.the-austrian-citrix-guy.at] =&gt; {
null_resource.RunChangeSSLPlaybook (local-exec):     "changed": true,
null_resource.RunChangeSSLPlaybook (local-exec):     "delta": "0:00:01.130213",
null_resource.RunChangeSSLPlaybook (local-exec):     "end": "2025-11-13 12:24:09.735282",
null_resource.RunChangeSSLPlaybook (local-exec):     "invocation": {
null_resource.RunChangeSSLPlaybook (local-exec):         "module_args": {
null_resource.RunChangeSSLPlaybook (local-exec):             "chdir": null,
null_resource.RunChangeSSLPlaybook (local-exec):             "command": "powershell.exe -NoProfile -ExecutionPolicy Bypass -File \"C:\\InstallCVAD\\ChangeSSLOnServers.ps1\"",
null_resource.RunChangeSSLPlaybook (local-exec):             "elevated": true,
null_resource.RunChangeSSLPlaybook (local-exec):             "executable": "C:\\InstallCVAD\\PsExec64.exe",
null_resource.RunChangeSSLPlaybook (local-exec):             "hostnames": null,
null_resource.RunChangeSSLPlaybook (local-exec):             "interactive": true,
null_resource.RunChangeSSLPlaybook (local-exec):             "limited": false,
null_resource.RunChangeSSLPlaybook (local-exec):             "nobanner": false,
null_resource.RunChangeSSLPlaybook (local-exec):             "noprofile": true,
null_resource.RunChangeSSLPlaybook (local-exec):             "password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
null_resource.RunChangeSSLPlaybook (local-exec):             "priority": null,
null_resource.RunChangeSSLPlaybook (local-exec):             "session": null,
null_resource.RunChangeSSLPlaybook (local-exec):             "system": false,
null_resource.RunChangeSSLPlaybook (local-exec):             "timeout": null,
null_resource.RunChangeSSLPlaybook (local-exec):             "username": "TACG\\administrator",
null_resource.RunChangeSSLPlaybook (local-exec):             "wait": true
null_resource.RunChangeSSLPlaybook (local-exec):         }
null_resource.RunChangeSSLPlaybook (local-exec):     },
null_resource.RunChangeSSLPlaybook (local-exec):     "psexec_command": "C:\\InstallCVAD\\PsExec64.exe -u TACG\\administrator -p *PASSWORD_REPLACED* -e -h -i -accepteula powershell.exe -NoProfile -ExecutionPolicy Bypass -File \"C:\\InstallCVAD\\ChangeSSLOnServers.ps1\"",
null_resource.RunChangeSSLPlaybook (local-exec):     "rc": 0,
null_resource.RunChangeSSLPlaybook (local-exec):     "start": "2025-11-13 12:24:08.605068",
null_resource.RunChangeSSLPlaybook (local-exec):     "stderr": "Connecting to local system...\r\r\rStarting PSEXESVC service on local system...\r\r\rCopying authentication key to TF-CVAD-DDC1...\r\r\rConnecting with PsExec service on TF-CVAD-DDC1...\r\r\rStarting powershell.exe on TF-CVAD-DDC1...\r\r\r\r\npowershell.exe exited on TF-CVAD-DDC1 with error code 0.\r\n",
null_resource.RunChangeSSLPlaybook (local-exec):     "stderr_lines": [
null_resource.RunChangeSSLPlaybook (local-exec):         "Connecting to local system...",
null_resource.RunChangeSSLPlaybook (local-exec):         "",
null_resource.RunChangeSSLPlaybook (local-exec):         "",
null_resource.RunChangeSSLPlaybook (local-exec):         "Starting PSEXESVC service on local system...",
null_resource.RunChangeSSLPlaybook (local-exec):         "",
null_resource.RunChangeSSLPlaybook (local-exec):         "",
null_resource.RunChangeSSLPlaybook (local-exec):         "Copying authentication key to TF-CVAD-DDC1...",
null_resource.RunChangeSSLPlaybook (local-exec):         "",
null_resource.RunChangeSSLPlaybook (local-exec):         "",
null_resource.RunChangeSSLPlaybook (local-exec):         "Connecting with PsExec service on TF-CVAD-DDC1...",
null_resource.RunChangeSSLPlaybook (local-exec):         "",
null_resource.RunChangeSSLPlaybook (local-exec):         "",
null_resource.RunChangeSSLPlaybook (local-exec):         "Starting powershell.exe on TF-CVAD-DDC1...",
null_resource.RunChangeSSLPlaybook (local-exec):         "",
null_resource.RunChangeSSLPlaybook (local-exec):         "",
null_resource.RunChangeSSLPlaybook (local-exec):         "",
null_resource.RunChangeSSLPlaybook (local-exec):         "powershell.exe exited on TF-CVAD-DDC1 with error code 0."
null_resource.RunChangeSSLPlaybook (local-exec):     ],
null_resource.RunChangeSSLPlaybook (local-exec):     "stdout": "\r\nPsExec v2.43 - Execute processes remotely\r\nCopyright (C) 2001-2023 Mark Russinovich\r\nSysinternals - www.sysinternals.com\r\n\r\n",
null_resource.RunChangeSSLPlaybook (local-exec):     "stdout_lines": [
null_resource.RunChangeSSLPlaybook (local-exec):         "",
null_resource.RunChangeSSLPlaybook (local-exec):         "PsExec v2.43 - Execute processes remotely",
null_resource.RunChangeSSLPlaybook (local-exec):         "Copyright (C) 2001-2023 Mark Russinovich",
null_resource.RunChangeSSLPlaybook (local-exec):         "Sysinternals - www.sysinternals.com",
null_resource.RunChangeSSLPlaybook (local-exec):         ""
null_resource.RunChangeSSLPlaybook (local-exec):     ]
null_resource.RunChangeSSLPlaybook (local-exec): }
null_resource.RunChangeSSLPlaybook (local-exec): PLAY RECAP *********************************************************************
null_resource.RunChangeSSLPlaybook (local-exec): tf-cvad-ddc1.the-austrian-citrix-guy.at : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
null_resource.RunChangeSSLPlaybook (local-exec): tf-cvad-ddc2.the-austrian-citrix-guy.at : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

null_resource.RunChangeSSLPlaybook: Creation complete after 3s [id=6734265951530601448]
data.external.CheckAnsibleReturnCode: Reading...
data.external.CheckAnsibleReturnCode: Read complete after 0s [id=-]
null_resource.IfExitCodeIsNotZeroThenHalt: Creating...
null_resource.IfExitCodeIsNotZeroThenHalt: Provisioning with 'local-exec'...
null_resource.IfExitCodeIsNotZeroThenHalt (local-exec): Executing: ["/bin/bash" "-c" "[ \"1\" = \"1\" ] &amp;&amp; echo \"Change SSL Bindings - Everything OK\" || { echo \"Changing the SSL bindings failed. Please review the settings. Halting Terraform.\"; exit 1; }"]
null_resource.IfExitCodeIsNotZeroThenHalt (local-exec): Change SSL Bindings - Everything OK
null_resource.IfExitCodeIsNotZeroThenHalt: Creation complete after 0s [id=5221894585109127965]

Apply complete! Resources: 8 added, 0 changed, 0 destroyed.
azadmin@tacg-cicd:/etc/terraform-tmm/CVADXS/_ChangeSSLOnServers$As all server operations are complete, we can now start with creating the CVAD site, the StoreFront site, and all the CVAD entities.  Part 8: Using Terraform and Ansible to create the CVAD site and all its necessitiesCreating the CVAD site, including its requirements and dependencies, is a complex task that involves many steps. Terraform and Ansible together fulfill the following tasks: Create all databases Create the site Add the second DDC to the site Configure additional site configurations Configure a dedicated Administrators group Configure the licensing Before running these tasks, we see that no related databases exist on SQL Server, and no site is configured on the primary Delivery Controller:   Important: All Terraform configurations and Ansible Playbooks, as well as the corresponding PowerShell scripts, must be run in the Site Administrator´s context – it must be a Domain user account. To obtain all required elevated privileges, we use PsExec64.exe as the host to run the PowerShell scripts. If we do not use the correct user account and privileges, the deployment will fail. Terraform monitors each Ansible Playbook run and stops further steps if a Playbook fails. Example Terraform snippet: # Terraform Deployment of Citrix CVAD on XenServer 8 - 25.11 
## Definition of XenServer Provider Variables - not all available variables are defined, only these we use
### Reference https://registry.terraform.io/providers/xenserver/xenserver/latest/docs

#### Copy the required Ansible Assets to the Ansible Server
###### ***** IMPORTANT: You must set the permissions to the destination directory accordingly (sudo) chmod 777 otherwise the provisioning will fail *****
resource "null_resource" "CopyAnsibleSiteAssetsToAnsibleServer" {
  connection {
    type     = var.TACG-TMM-XS-CL-WinRM-Connection-Type
    user     = var.TACG-TMM-XS-CL-WinRM-Connection-User
    password = var.TACG-TMM-XS-CL-WinRM-Connection-Password
    host     = var.TACG-TMM-XS-CL-WinRM-Connection-HostIP
    port     = var.TACG-TMM-XS-CL-WinRM-Connection-Port
    https    = var.TACG-TMM-XS-CL-WinRM-Connection-HTTPS
    timeout  = var.TACG-TMM-XS-CL-WinRM-Connection-Timeout
  }

  # Copy AdditionalSiteConfiguration Playbook file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-AdditionalSiteConfiguration-Playbook-Source
    destination = var.TACG-TMM-XS-CL-Ansible-AdditionalSiteConfiguration-Playbook-Destination
  }

  # Copy AddSecondDDCToSite Playbook file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-AddSecondDDCToSite-Playbook-Source
    destination = var.TACG-TMM-XS-CL-Ansible-AddSecondDDCToSite-Playbook-Destination
  }

  # Copy ConfigureAdminGroup Playbook file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-ConfigureAdminGroup-Playbook-Source
    destination = var.TACG-TMM-XS-CL-Ansible-ConfigureAdminGroup-Playbook-Destination
  }

  # Copy CreateDatabase Playbook file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-CreateDatabase-Playbook-Source
    destination = var.TACG-TMM-XS-CL-Ansible-CreateDatabase-Playbook-Destination
  }

  # Copy CreateSite Playbook file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-CreateSite-Playbook-Source
    destination = var.TACG-TMM-XS-CL-Ansible-CreateSite-Playbook-Destination
  }

  # Copy SetLicensing Playbook file To Ansible Server
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-Ansible-SetLicensing-Playbook-Source
    destination = var.TACG-TMM-XS-CL-Ansible-SetLicensing-Playbook-Destination
  }
}

#### Copy the required PowerShell Scripts to the DDC
resource "null_resource" "CopyPowerShellScriptsToDDC1" {
  connection {
    type     = var.TACG-TMM-XS-CL-WinRM-Connection-Type
    user     = var.TACG-TMM-XS-CL-WinRM-Connection-User
    password = var.TACG-TMM-XS-CL-WinRM-Connection-Password
    host     = var.TACG-TMM-XS-CL-WinRM-Connection-HostIP
    port     = var.TACG-TMM-XS-CL-WinRM-Connection-Port
    https    = var.TACG-TMM-XS-CL-WinRM-Connection-HTTPS
    timeout  = var.TACG-TMM-XS-CL-WinRM-Connection-Timeout
  }

  # Copy AdditionalSiteConfiguration.ps1 file To the DDC
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-DDC-AdditionalSiteConfiguration-Source
    destination = var.TACG-TMM-XS-CL-DDC-AdditionalSiteConfiguration-Destination
  }

  # Copy AddSecondDDCToSite.ps1 file To the DDC
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-DDC-AddSecondDDCToSite-Source
    destination = var.TACG-TMM-XS-CL-DDC-AddSecondDDCToSite-Destination
  }

  # Copy ConfigureAdminGroup.ps1 file To the DDC
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-DDC-ConfigureAdminGroup-Source
    destination = var.TACG-TMM-XS-CL-DDC-ConfigureAdminGroup-Destination
  }

  # Copy CreateDatabase.ps1 file To the DDC
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-DDC-CreateDatabase-Source
    destination = var.TACG-TMM-XS-CL-DDC-CreateDatabase-Destination
  }

  # Copy CreateSite.ps1 file To the DDC
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-DDC-CreateSite-Source
    destination = var.TACG-TMM-XS-CL-DDC-CreateSite-Destination
  }

  # Copy SetLicensing.ps1 file To the DDC
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-DDC-SetLicensing-Source
    destination = var.TACG-TMM-XS-CL-DDC-SetLicensing-Destination
  }

  # Copy PsExec.exe file To the DDC
  provisioner "file" {
    source      = var.TACG-TMM-XS-CL-DDC-PsExec-Source
    destination = var.TACG-TMM-XS-CL-DDC-PsExec-Destination
  }
}

resource "time_sleep" "wait_10_seconds" {
  depends_on      = [null_resource.CopyAnsibleSiteAssetsToAnsibleServer, null_resource.CopyPowerShellScriptsToDDC1]
  create_duration = "10s"
}

##### Create Databases Start
##### Connect to Ansible Interpreter and run CreateDatabase-Playbook on DDCs
resource "null_resource" "RunCreateDatabasePlaybook" {
  depends_on = [time_sleep.wait_10_seconds]

  provisioner "local-exec" {
    working_dir = "/etc/ansible"
    command     = var.TACG-TMM-XS-CL-Ansible-CreateDatabase-CMD
  }
}

##### Check Ansible Return Code
data "external" "CheckAnsibleReturnCodeCDB" {
  depends_on = [null_resource.RunCreateDatabasePlaybook]
  program    = ["bash", "/etc/ansible/assets/CheckAnsibleReturnCode.sh", "/etc/ansible/assets/ansible_exit_code.txt", "0"]
}

##### Terminate if Return Code is not 0
resource "null_resource" "IfExitCodeIsNotZeroThenHaltCDB" {
  depends_on = [data.external.CheckAnsibleReturnCodeCDB]
  provisioner "local-exec" {
    interpreter = ["/bin/bash", "-c"]
    command     = "[ \"${data.external.CheckAnsibleReturnCodeCDB.result.found}\" = \"1\" ] &amp;&amp; echo \"Create Databases - Everything OK\" || { echo \"Deploying the CVAD Databases failed. Please review the settings. Halting Terraform.\"; exit 1; }"
  }
}

#### Wait 10s for Background Processes to Settle
resource "time_sleep" "wait_300_secondsCDB" {
  depends_on      = [null_resource.IfExitCodeIsNotZeroThenHaltCDB]
  create_duration = "300s"
}

##### Create Databases End

##### Create Site Start
##### Connect to Ansible Interpreter and run CreateSite-Playbook on DDCs
resource "null_resource" "RunCreateSitePlaybook" {
  depends_on = [time_sleep.wait_300_secondsCDB]
  provisioner "local-exec" {
    working_dir = "/etc/ansible"
    command     = var.TACG-TMM-XS-CL-Ansible-CreateSite-CMD
  }
}

##### Check Ansible Return Code
data "external" "CheckAnsibleReturnCodeCS" {
  depends_on = [null_resource.RunCreateSitePlaybook]
  program    = ["bash", "/etc/ansible/assets/CheckAnsibleReturnCode.sh", "/etc/ansible/assets/ansible_exit_code.txt", "0"]
}

##### Terminate if Return Code is not 0
resource "null_resource" "IfExitCodeIsNotZeroThenHaltCS" {
  depends_on = [data.external.CheckAnsibleReturnCodeCS]
  provisioner "local-exec" {
    interpreter = ["/bin/bash", "-c"]
    command     = "[ \"${data.external.CheckAnsibleReturnCodeCS.result.found}\" = \"1\" ] &amp;&amp; echo \"Create Site - Everything OK\" || { echo \"Deploying the CVAD Site failed. Please review the settings. Halting Terraform.\"; exit 1; }"
  }
}

#### Wait 10s for Background Processes to Settle
resource "time_sleep" "wait_60_secondsCS" {
  depends_on      = [null_resource.IfExitCodeIsNotZeroThenHaltCS]
  create_duration = "60s"
}

##### Create Site End

##### Create SiteAddSecondDDC Start
##### Connect to Ansible Interpreter and run AddSecondDDC-Playbook on DDCs
resource "null_resource" "RunAddSecondDDCPlaybook" {
  depends_on = [time_sleep.wait_60_secondsCS]
  provisioner "local-exec" {
    working_dir = "/etc/ansible"
    command     = var.TACG-TMM-XS-CL-Ansible-AddSecondDDCToSite-CMD
  }
}

##### Check Ansible Return Code
data "external" "CheckAnsibleReturnCodeSecondDDC" {
  depends_on = [null_resource.RunAddSecondDDCPlaybook]
  program    = ["bash", "/etc/ansible/assets/CheckAnsibleReturnCode.sh", "/etc/ansible/assets/ansible_exit_code.txt", "0"]
}

##### Terminate if Return Code is not 0
resource "null_resource" "IfExitCodeIsNotZeroThenHaltSecondDDC" {
  depends_on = [data.external.CheckAnsibleReturnCodeSecondDDC]
  provisioner "local-exec" {
    interpreter = ["/bin/bash", "-c"]
    command     = "[ \"${data.external.CheckAnsibleReturnCodeSecondDDC.result.found}\" = \"1\" ] &amp;&amp; echo \"Add Second DDC - Everything OK\" || { echo \"Adding second DDC to the CVAD Site failed. Please review the settings. Halting Terraform.\"; exit 1; }"
  }
}

#### Wait 10s for Background Processes to Settle
resource "time_sleep" "wait_10_secondsSecondDDC" {
  depends_on      = [null_resource.IfExitCodeIsNotZeroThenHaltSecondDDC]
  create_duration = "10s"
}

##### Create SiteAddSecondDDC End

##### Create AdditionalSiteConfiguration Start
##### Connect to Ansible Interpreter and run AdditionalSiteConfiguration-Playbook on DDCs
resource "null_resource" "RunAdditionalSiteConfigurationPlaybook" {
  depends_on = [time_sleep.wait_10_secondsSecondDDC]

  provisioner "local-exec" {
    working_dir = "/etc/ansible"
    command     = var.TACG-TMM-XS-CL-Ansible-AdditionalSiteConfiguration-CMD
  }
}

##### Check Ansible Return Code
data "external" "CheckAnsibleReturnCodeAdditionalSiteConfiguration" {
  depends_on = [null_resource.RunAdditionalSiteConfigurationPlaybook]
  program    = ["bash", "/etc/ansible/assets/CheckAnsibleReturnCode.sh", "/etc/ansible/assets/ansible_exit_code.txt", "0"]
}

##### Terminate if Return Code is not 0
resource "null_resource" "IfExitCodeIsNotZeroThenHaltAdditionalSiteConfiguration" {
  depends_on = [data.external.CheckAnsibleReturnCodeAdditionalSiteConfiguration]
  provisioner "local-exec" {
    interpreter = ["/bin/bash", "-c"]
    command     = "[ \"${data.external.CheckAnsibleReturnCodeAdditionalSiteConfiguration.result.found}\" = \"1\" ] &amp;&amp; echo \"Additional Site Configuration - Everything OK\" || { echo \"Running the additional Site Configurations failed. Please review the settings. Halting Terraform.\"; exit 1; }"
  }
}

#### Wait 10s for Background Processes to Settle
resource "time_sleep" "wait_10_secondsAdditionalSiteConfiguration" {
  depends_on      = [null_resource.IfExitCodeIsNotZeroThenHaltAdditionalSiteConfiguration]
  create_duration = "10s"
}

##### Create AdditionalSiteConfiguration End

##### Create ConfigureAdminGroup Start
##### Connect to Ansible Interpreter and run ConfigureAdminGroup-Playbook on DDCs
resource "null_resource" "RunConfigureAdminGroupPlaybook" {
  depends_on = [time_sleep.wait_10_secondsAdditionalSiteConfiguration]

  provisioner "local-exec" {
    working_dir = "/etc/ansible"
    command     = var.TACG-TMM-XS-CL-Ansible-ConfigureAdminGroup-CMD
  }
}

##### Check Ansible Return Code
data "external" "CheckAnsibleReturnCodeConfigureAdminGroup" {
  depends_on = [null_resource.RunConfigureAdminGroupPlaybook]
  program    = ["bash", "/etc/ansible/assets/CheckAnsibleReturnCode.sh", "/etc/ansible/assets/ansible_exit_code.txt", "0"]
}

##### Terminate if Return Code is not 0
resource "null_resource" "IfExitCodeIsNotZeroThenHaltConfigureAdminGroup" {
  depends_on = [data.external.CheckAnsibleReturnCodeConfigureAdminGroup]
  provisioner "local-exec" {
    interpreter = ["/bin/bash", "-c"]
    command     = "[ \"${data.external.CheckAnsibleReturnCodeConfigureAdminGroup.result.found}\" = \"1\" ] &amp;&amp; echo \"Admin Group Configuration - Everything OK\" || { echo \"Running the Configuration of the AdminGroup failed. Please review the settings. Halting Terraform.\"; exit 1; }"
  }
}

#### Wait 10s for Background Processes to Settle
resource "time_sleep" "wait_10_secondsConfigureAdminGroup" {
  depends_on      = [null_resource.IfExitCodeIsNotZeroThenHaltConfigureAdminGroup]
  create_duration = "10s"
}

##### Create ConfigureAdminGroup End

##### Create SetLicensing Start
##### Connect to Ansible Interpreter and run SetLicensing-Playbook on DDCs
resource "null_resource" "RunConfigureSetLicensingPlaybook" {
  depends_on = [time_sleep.wait_10_secondsConfigureAdminGroup]

  provisioner "local-exec" {
    working_dir = "/etc/ansible"
    command     = var.TACG-TMM-XS-CL-Ansible-SetLicensing-CMD
  }
}

##### Check Ansible Return Code
data "external" "CheckAnsibleReturnCodeSetLicensing" {
  depends_on = [null_resource.RunConfigureSetLicensingPlaybook]
  program    = ["bash", "/etc/ansible/assets/CheckAnsibleReturnCode.sh", "/etc/ansible/assets/ansible_exit_code.txt", "0"]
}

##### Terminate if Return Code is not 0
resource "null_resource" "IfExitCodeIsNotZeroThenHaltSetLicensing" {
  depends_on = [data.external.CheckAnsibleReturnCodeSetLicensing]
  provisioner "local-exec" {
    interpreter = ["/bin/bash", "-c"]
    command     = "[ \"${data.external.CheckAnsibleReturnCodeSetLicensing.result.found}\" = \"1\" ] &amp;&amp; echo \"Licensing Configuration - Everything OK\" || { echo \"Configuration of Licensing failed. Please review the settings. Halting Terraform.\"; exit 1; }"
  }
}

##### Create SetLicensing End
Example Ansible Playbook calling the corresponding PowerShell script in PsExec64.exe for creating the required databases: # Terraform Deployment of Citrix CVAD on XenServer 8 - 25.11 
## For installation and configuration of the Delivery Controllers, StoreFront-Servers and WebStudio-Servers, we use Ansible Playbooks 
### Reference https://docs.ansible.com/ansible/latest/getting_started/index.html
#### We assume that the communication between the Ansible Server and all needed Hosts via WinRM is working as intended - check your Group Policies
##### All hosts and its corresponding communication-related Ansible variables are stored in assets/inventory.ini
###### Create CVAD Databases by running a PowerShell script on DDC1
- name: Create CVAD Databases
  hosts: cvad-ddc1
  gather_facts: no
  collections:
    - community.windows
  vars:
    domain_user: "TACG\\XxXxXxXxXxXxX"
    domain_pass: "!XxXxXxXxXxXxXxXxX!"
    psexec_path: "C:\\InstallCVAD\\PsExec64.exe"
    target_host: "tf-cvad-ddc1.the-austrian-citrix-guy.at"
    remote_script: "C:\\InstallCVAD\\CreateDatabases.ps1"
    ansible_user: "svc_ansible"      
    ansible_password: "!XxXxXxXxXxXxXxXxX!"
    ansible_connection: winrm
    ansible_winrm_transport: certificate

  tasks:
   - name: Run the Create Database-PowerShell script as Domain Administrator
     community.windows.win_psexec:
       command: powershell.exe -NoProfile -ExecutionPolicy Bypass -File "{{ remote_script }}"
       executable: "{{ psexec_path }}"
       username: "{{ domain_user }}"
       password: "{{ domain_pass }}"
       elevated: true
       noprofile: true
       interactive: true
       wait: trueExample PowerShell script for creating the required databases: Import-Module Citrix.XenDesktop®.Admin
asnp citrix.*
$SiteName = "TF-TACG-CVAD2507"
$DatabaseServer = "tacg-sql.the-austrian-citrix-guy.at"
$DDCName = "tf-cvad-ddc1.the-austrian-citrix-guy.at"
$DatabasePrefix = "DB-"

try {
        New-XDDatabase -AdminAddress $DDCName -SiteName $SiteName -AllDefaultDatabases -DatabaseServer $DatabaseServer -DatabaseNamePrefix $DatabasePrefix -Verbose

  } catch {
    $logFile = "C:\Logs\MyLogFile.log"
    $errorMessage = $_.Exception.Message
    Add-Content -Path $logFile -Value "$(Get-Date) - CreateDB: Error: $errorMessage"
    Write-Output "An error occurred trying to create the databases (error: $($Error[0]))"
    Exit 1     
}
 Note: The corresponding Ansible Playbooks for all the needed steps mentioned above are mostly the same – they differ only in running the feasible PowerShell script. Example PowerShell script for creating the CVAD site: Import-Module Citrix.XenDesktop.Admin
asnp citrix.*

$SiteName = "TF-TACG-CVAD2507"
$DatabaseServer = "tacg-sql.the-austrian-citrix-guy.at"
$DatabaseName_Site = "DB-CitrixTF-TACG-CVAD2507Site"
$DatabaseName_Logging = "DB-CitrixTF-TACG-CVAD2507Logging"
$DatabaseName_Monitoring = "DB-CitrixTF-TACG-CVAD2507Monitoring"
$DDCName = "tf-cvad-ddc1.the-austrian-citrix-guy.at"

try {
        New-XDSite -DatabaseServer $DatabaseServer -LoggingDatabaseName $DatabaseName_Logging -MonitorDatabaseName $DatabaseName_Monitoring -SiteDatabaseName $DatabaseName_Site -SiteName $SiteName -AdminAddress $DDCName -Verbose
  
  } catch {
    $logFile = "C:\Logs\MyLogFile.log"
    $errorMessage = $_.Exception.Message
    Add-Content -Path $logFile -Value "$(Get-Date) - CreateSite: Error: $errorMessage"
    Write-Output "An error occurred trying to create the CVAD site (error: $($Error[0]))"
    Exit 1  
  }

Example PowerShell script for adding the second DDC to the site:
Import-Module Citrix.XenDesktop.Admin
asnp citrix.*

$DDCName = "tf-cvad-ddc1.the-austrian-citrix-guy.at"
$NewDDCName = "tf-cvad-ddc2.the-austrian-citrix-guy.at"

try {
        Add-XDController -AdminAddress $NewDDCName -SiteControllerAddress $DDCName -Verbose
  
     } catch {
    $logFile = "C:\Logs\MyLogFile.log"
    $errorMessage = $_.Exception.Message
    Add-Content -Path $logFile -Value "$(Get-Date) - Add2ndDDC: Error: $errorMessage"
    Write-Output "An error occurred trying to add the 2nd DDC (error: $($Error[0]))"
    Exit 1  
    }
Example PowerShell script for adding the second DDC to the site: Import-Module Citrix.XenDesktop.Admin
asnp citrix.*

$DDCName = "tf-cvad-ddc1.the-austrian-citrix-guy.at"
$NewDDCName = "tf-cvad-ddc2.the-austrian-citrix-guy.at"

try {
        Add-XDController -AdminAddress $NewDDCName -SiteControllerAddress $DDCName -Verbose
  
     } catch {
    $logFile = "C:\Logs\MyLogFile.log"
    $errorMessage = $_.Exception.Message
    Add-Content -Path $logFile -Value "$(Get-Date) - Add2ndDDC: Error: $errorMessage"
    Write-Output "An error occurred trying to add the 2nd DDC (error: $($Error[0]))"
    Exit 1  
    }Example PowerShell script for running additional site configurations: Import-Module Citrix.XenDesktop.Admin
asnp citrix.*

$GroomingDays = 90

try {  
      Set-MonitorConfiguration -GroomApplicationInstanceRetentionDays $GroomingDays -GroomDeletedRetentionDays $GroomingDays -GroomFailuresRetentionDays $GroomingDays -GroomLoadIndexesRetentionDays $GroomingDays -GroomMachineHotfixLogRetentionDays $GroomingDays -GroomNotificationLogRetentionDays $GroomingDays -GroomResourceUsageDayDataRetentionDays $GroomingDays -GroomSessionsRetentionDays $GroomingDays -GroomSummariesRetentionDays $GroomingDays 
      Set-BrokerSite -TrustRequestsSentToTheXmlServicePort $true
      Set-BrokerSite -ConnectionLeasingEnabled $false
      Set-BrokerSite -LocalHostCacheEnabled $true
      Set-AnalyticsSite -Enabled $false

    } catch {
    $logFile = "C:\Logs\MyLogFile.log"
    $errorMessage = $_.Exception.Message
    Add-Content -Path $logFile -Value "$(Get-Date) - AdditionalSiteConfiguration: Error: $errorMessage"
    Write-Output "An error occurred trying to run the additional Site configuration (error: $($Error[0]))"
    Exit 1  
    }Example PowerShell script for adding a dedicated Administrators group to the site: Import-Module Citrix.XenDesktop.Admin
asnp citrix.*

$DDCName = "tf-cvad-ddc1.the-austrian-citrix-guy.at"
$AdminGroup = "TACG-CTX-Admins"

try {  
      New-AdminAdministrator -AdminAddress $DDCName -Name $AdminGroup
      Add-AdminRight -AdminAddress $DDCName -Administrator $AdminGroup -Role 'Full Administrator' -Scope "All"

    } catch {
    $logFile = "C:\Logs\MyLogFile.log"
    $errorMessage = $_.Exception.Message
    Add-Content -Path $logFile -Value "$(Get-Date) - ConfigureCTXAdminGroup: Error: $errorMessage"
    Write-Output "An error occurred trying to run the CTXAdminGroup script (error: $($Error[0]))"
    Exit 1  
    }Example PowerShell script for configuring the Licensing: Import-Module Citrix.XenDesktop.Admin
asnp citrix.*

$DDCName = "tf-cvad-ddc1.the-austrian-citrix-guy.at"
$LicenseServer = "tacg-dc.the-austrian-citrix-guy.at"
$LicenseServerPort = 27000
$LicensingModel = "UserDevice"
$ProductCode = "XDT"
$ProductEdition = "PLT"

try {  
      Set-XDLicensing -AdminAddress $DDCName -LicenseServerAddress $LicenseServer -LicenseServerPort $LicenseServerPort -Force
      Set-ConfigSite  -AdminAddress $DDCName -LicensingModel $LicensingModel -ProductCode $ProductCode -ProductEdition $ProductEdition -UseLicenseActivationService $true
      Set-ConfigSiteMetadata -AdminAddress $DDCName -Name 'CertificateHash' -Value $(Get-LicCertificate -AdminAddress "https://$($LicenseServer):8083").CertHash

    } catch {
    $logFile = "C:\Logs\MyLogFile.log"
    $errorMessage = $_.Exception.Message
    Add-Content -Path $logFile -Value "$(Get-Date) - ConfigureLicensing: Error: $errorMessage"
    Write-Output "An error occurred trying to run the ConfigureLicensing script (error: $($Error[0]))"
    Exit 1 
	}Running the snippet should fulfill all the steps mentioned above and create the CVAD site: azadmin@tacg-cicd:/etc/terraform-tmm/CVADXS/_ConfigureSite$ terraform apply
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
  + create
 &lt;= read (data resources)

Terraform will perform the following actions:

  # data.external.CheckAnsibleReturnCodeAdditionalSiteConfiguration will be read during apply
  # (depends on a resource or a module with changes pending)
 &lt;= data "external" "CheckAnsibleReturnCodeAdditionalSiteConfiguration" {
      + id      = (known after apply)
      + program = [
          + "bash",
          + "/etc/ansible/assets/CheckAnsibleReturnCode.sh",
          + "/etc/ansible/assets/ansible_exit_code.txt",
          + "0",
        ]
      + result  = (known after apply)
    }

  # data.external.CheckAnsibleReturnCodeCDB will be read during apply
  # (depends on a resource or a module with changes pending)
 &lt;= data "external" "CheckAnsibleReturnCodeCDB" {
      + id      = (known after apply)
      + program = [
          + "bash",
          + "/etc/ansible/assets/CheckAnsibleReturnCode.sh",
          + "/etc/ansible/assets/ansible_exit_code.txt",
          + "0",
        ]
      + result  = (known after apply)
    }

  # data.external.CheckAnsibleReturnCodeCS will be read during apply
  # (depends on a resource or a module with changes pending)
 &lt;= data "external" "CheckAnsibleReturnCodeCS" {
      + id      = (known after apply)
      + program = [
          + "bash",
          + "/etc/ansible/assets/CheckAnsibleReturnCode.sh",
          + "/etc/ansible/assets/ansible_exit_code.txt",
          + "0",
        ]
      + result  = (known after apply)
    }

  # data.external.CheckAnsibleReturnCodeConfigureAdminGroup will be read during apply
  # (depends on a resource or a module with changes pending)
 &lt;= data "external" "CheckAnsibleReturnCodeConfigureAdminGroup" {
      + id      = (known after apply)
      + program = [
          + "bash",
          + "/etc/ansible/assets/CheckAnsibleReturnCode.sh",
          + "/etc/ansible/assets/ansible_exit_code.txt",
          + "0",
        ]
      + result  = (known after apply)
    }

  # data.external.CheckAnsibleReturnCodeSecondDDC will be read during apply
  # (depends on a resource or a module with changes pending)
 &lt;= data "external" "CheckAnsibleReturnCodeSecondDDC" {
      + id      = (known after apply)
      + program = [
          + "bash",
          + "/etc/ansible/assets/CheckAnsibleReturnCode.sh",
          + "/etc/ansible/assets/ansible_exit_code.txt",
          + "0",
        ]
      + result  = (known after apply)
    }

  # data.external.CheckAnsibleReturnCodeSetLicensing will be read during apply
  # (depends on a resource or a module with changes pending)
 &lt;= data "external" "CheckAnsibleReturnCodeSetLicensing" {
      + id      = (known after apply)
      + program = [
          + "bash",
          + "/etc/ansible/assets/CheckAnsibleReturnCode.sh",
          + "/etc/ansible/assets/ansible_exit_code.txt",
          + "0",
        ]
      + result  = (known after apply)
    }

  # null_resource.CopyAnsibleSiteAssetsToAnsibleServer will be created
  + resource "null_resource" "CopyAnsibleSiteAssetsToAnsibleServer" {
      + id = (known after apply)
    }

  # null_resource.CopyPowerShellScriptsToDDC1 will be created
  + resource "null_resource" "CopyPowerShellScriptsToDDC1" {
      + id = (known after apply)
    }

  # null_resource.IfExitCodeIsNotZeroThenHaltAdditionalSiteConfiguration will be created
  + resource "null_resource" "IfExitCodeIsNotZeroThenHaltAdditionalSiteConfiguration" {
      + id = (known after apply)
    }

  # null_resource.IfExitCodeIsNotZeroThenHaltCDB will be created
  + resource "null_resource" "IfExitCodeIsNotZeroThenHaltCDB" {
      + id = (known after apply)
    }

  # null_resource.IfExitCodeIsNotZeroThenHaltCS will be created
  + resource "null_resource" "IfExitCodeIsNotZeroThenHaltCS" {
      + id = (known after apply)
    }

  # null_resource.IfExitCodeIsNotZeroThenHaltConfigureAdminGroup will be created
  + resource "null_resource" "IfExitCodeIsNotZeroThenHaltConfigureAdminGroup" {
      + id = (known after apply)
    }

  # null_resource.IfExitCodeIsNotZeroThenHaltSecondDDC will be created
  + resource "null_resource" "IfExitCodeIsNotZeroThenHaltSecondDDC" {
      + id = (known after apply)
    }

  # null_resource.IfExitCodeIsNotZeroThenHaltSetLicensing will be created
  + resource "null_resource" "IfExitCodeIsNotZeroThenHaltSetLicensing" {
      + id = (known after apply)
    }

  # null_resource.RunAddSecondDDCPlaybook will be created
  + resource "null_resource" "RunAddSecondDDCPlaybook" {
      + id = (known after apply)
    }

  # null_resource.RunAdditionalSiteConfigurationPlaybook will be created
  + resource "null_resource" "RunAdditionalSiteConfigurationPlaybook" {
      + id = (known after apply)
    }

  # null_resource.RunConfigureAdminGroupPlaybook will be created
  + resource "null_resource" "RunConfigureAdminGroupPlaybook" {
      + id = (known after apply)
    }

  # null_resource.RunConfigureSetLicensingPlaybook will be created
  + resource "null_resource" "RunConfigureSetLicensingPlaybook" {
      + id = (known after apply)
    }

  # null_resource.RunCreateDatabasePlaybook will be created
  + resource "null_resource" "RunCreateDatabasePlaybook" {
      + id = (known after apply)
    }

  # null_resource.RunCreateSitePlaybook will be created
  + resource "null_resource" "RunCreateSitePlaybook" {
      + id = (known after apply)
    }

  # time_sleep.wait_10_seconds will be created
  + resource "time_sleep" "wait_10_seconds" {
      + create_duration = "10s"
      + id              = (known after apply)
    }

  # time_sleep.wait_10_secondsAdditionalSiteConfiguration will be created
  + resource "time_sleep" "wait_10_secondsAdditionalSiteConfiguration" {
      + create_duration = "10s"
      + id              = (known after apply)
    }

  # time_sleep.wait_10_secondsConfigureAdminGroup will be created
  + resource "time_sleep" "wait_10_secondsConfigureAdminGroup" {
      + create_duration = "10s"
      + id              = (known after apply)
    }

  # time_sleep.wait_10_secondsSecondDDC will be created
  + resource "time_sleep" "wait_10_secondsSecondDDC" {
      + create_duration = "10s"
      + id              = (known after apply)
    }

  # time_sleep.wait_300_secondsCDB will be created
  + resource "time_sleep" "wait_300_secondsCDB" {
      + create_duration = "300s"
      + id              = (known after apply)
    }

  # time_sleep.wait_60_secondsCS will be created
  + resource "time_sleep" "wait_60_secondsCS" {
      + create_duration = "60s"
      + id              = (known after apply)
    }

Plan: 20 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.CopyPowerShellScriptsToDDC1: Creating...
null_resource.CopyAnsibleSiteAssetsToAnsibleServer: Creating...
null_resource.CopyAnsibleSiteAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPowerShellScriptsToDDC1: Provisioning with 'file'...
null_resource.CopyAnsibleSiteAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyAnsibleSiteAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyAnsibleSiteAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyAnsibleSiteAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyPowerShellScriptsToDDC1: Provisioning with 'file'...
null_resource.CopyAnsibleSiteAssetsToAnsibleServer: Provisioning with 'file'...
null_resource.CopyAnsibleSiteAssetsToAnsibleServer: Creation complete after 1s [id=3364084582988389224]
time_sleep.wait_10_seconds: Creating...
#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;
null_resource.CopyPowerShellScriptsToDDC1: Provisioning with 'file'...
#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;
...
null_resource.CopyPowerShellScriptsToDDC1: Creation complete after 10s [id=6861429796874371568]
time_sleep.wait_10_seconds: Still creating... [00m10s elapsed]
time_sleep.wait_10_seconds: Creation complete after 10s [id=2025-11-12T16:58:47Z]
null_resource.RunCreateDatabasePlaybook: Creating...
null_resource.RunCreateDatabasePlaybook: Provisioning with 'local-exec'...
null_resource.RunCreateDatabasePlaybook (local-exec): Executing: ["/bin/sh" "-c" "ansible-playbook /etc/ansible/assets/CreateDatabases.ansible.yml -i /etc/ansible/assets/inventory.ini -vvv -c winrm; echo $? &gt; /etc/ansible/assets/ansible_exit_code.txt"]

null_resource.RunCreateDatabasePlaybook (local-exec): PLAYBOOK: CreateDatabases.ansible.yml ******************************************
null_resource.RunCreateDatabasePlaybook (local-exec): 1 plays in /etc/ansible/assets/CreateDatabases.ansible.yml

null_resource.RunCreateDatabasePlaybook (local-exec): PLAY [Run PowerShell script on Windows Server as Domain Admin] *****************

null_resource.RunCreateDatabasePlaybook (local-exec): TASK [Run the StoreFront installer PowerShell script as domain user] ***********
null_resource.RunCreateDatabasePlaybook (local-exec): task path: /etc/ansible/assets/CreateDatabases.ansible.yml:20
null_resource.RunCreateDatabasePlaybook (local-exec): Using module file /usr/lib/python3/dist-packages/ansible_collections/community/windows/plugins/modules/win_psexec.ps1
null_resource.RunCreateDatabasePlaybook (local-exec): Pipelining is enabled.
null_resource.RunCreateDatabasePlaybook (local-exec): &lt;tf-cvad-ddc1.the-austrian-citrix-guy.at&gt; ESTABLISH WINRM CONNECTION FOR USER: svc_ansible on PORT 5986 TO tf-cvad-ddc1.the-austrian-citrix-guy.at
null_resource.RunCreateDatabasePlaybook (local-exec): EXEC (via pipeline wrapper)
null_resource.RunCreateDatabasePlaybook (local-exec): changed: [tf-cvad-ddc1.the-austrian-citrix-guy.at] =&gt; {
null_resource.RunCreateDatabasePlaybook (local-exec):     "changed": true,
null_resource.RunCreateDatabasePlaybook (local-exec):     "delta": "0:00:32.533635",
null_resource.RunCreateDatabasePlaybook (local-exec):     "end": "2025-11-12 04:59:20.201075",
null_resource.RunCreateDatabasePlaybook (local-exec):     "invocation": {
null_resource.RunCreateDatabasePlaybook (local-exec):         "module_args": {
null_resource.RunCreateDatabasePlaybook (local-exec):             "chdir": null,
null_resource.RunCreateDatabasePlaybook (local-exec):             "command": "powershell.exe -NoProfile -ExecutionPolicy Bypass -File \"C:\\InstallCVAD\\CreateDatabases.ps1\"",
null_resource.RunCreateDatabasePlaybook (local-exec):             "elevated": true,
null_resource.RunCreateDatabasePlaybook (local-exec):             "executable": "C:\\InstallCVAD\\PsExec64.exe",
null_resource.RunCreateDatabasePlaybook (local-exec):             "hostnames": null,
null_resource.RunCreateDatabasePlaybook (local-exec):             "interactive": true,
null_resource.RunCreateDatabasePlaybook (local-exec):             "limited": false,
null_resource.RunCreateDatabasePlaybook (local-exec):             "nobanner": false,
null_resource.RunCreateDatabasePlaybook (local-exec):             "noprofile": true,
null_resource.RunCreateDatabasePlaybook (local-exec):             "password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
null_resource.RunCreateDatabasePlaybook (local-exec):             "priority": null,
null_resource.RunCreateDatabasePlaybook (local-exec):             "session": null,
null_resource.RunCreateDatabasePlaybook (local-exec):             "system": false,
null_resource.RunCreateDatabasePlaybook (local-exec):             "timeout": null,
null_resource.RunCreateDatabasePlaybook (local-exec):             "username": "TACG\\administrator",
null_resource.RunCreateDatabasePlaybook (local-exec):             "wait": true
null_resource.RunCreateDatabasePlaybook (local-exec):         }
null_resource.RunCreateDatabasePlaybook (local-exec):     },
null_resource.RunCreateDatabasePlaybook (local-exec):     "psexec_command": "C:\\InstallCVAD\\PsExec64.exe -u TACG\\administrator -p *PASSWORD_REPLACED* -e -h -i -accepteula powershell.exe -NoProfile -ExecutionPolicy Bypass -File \"C:\\InstallCVAD\\CreateDatabases.ps1\"",
null_resource.RunCreateDatabasePlaybook (local-exec):     "rc": 0,
null_resource.RunCreateDatabasePlaybook (local-exec):     "start": "2025-11-12 04:58:47.667440",
null_resource.RunCreateDatabasePlaybook (local-exec):     "stderr": "Connecting to local system...\r\r\rStarting PSEXESVC service on local system...\r\r\rCopying authentication key to TF-CVAD-DDC1...\r\r\rConnecting with PsExec service on TF-CVAD-DDC1...\r\r\rStarting powershell.exe on TF-CVAD-DDC1...\r\r\r\r\npowershell.exe exited on TF-CVAD-DDC1 with error code 0.\r\n",
null_resource.RunCreateDatabasePlaybook (local-exec):     "stderr_lines": [
null_resource.RunCreateDatabasePlaybook (local-exec):         "Connecting to local system...",
null_resource.RunCreateDatabasePlaybook (local-exec):         "",
null_resource.RunCreateDatabasePlaybook (local-exec):         "",
null_resource.RunCreateDatabasePlaybook (local-exec):         "Starting PSEXESVC service on local system...",
null_resource.RunCreateDatabasePlaybook (local-exec):         "",
null_resource.RunCreateDatabasePlaybook (local-exec):         "",
null_resource.RunCreateDatabasePlaybook (local-exec):         "Copying authentication key to TF-CVAD-DDC1...",
null_resource.RunCreateDatabasePlaybook (local-exec):         "",
null_resource.RunCreateDatabasePlaybook (local-exec):         "",
null_resource.RunCreateDatabasePlaybook (local-exec):         "Connecting with PsExec service on TF-CVAD-DDC1...",
null_resource.RunCreateDatabasePlaybook (local-exec):         "",
null_resource.RunCreateDatabasePlaybook (local-exec):         "",
null_resource.RunCreateDatabasePlaybook (local-exec):         "Starting powershell.exe on TF-CVAD-DDC1...",
null_resource.RunCreateDatabasePlaybook (local-exec):         "",
null_resource.RunCreateDatabasePlaybook (local-exec):         "",
null_resource.RunCreateDatabasePlaybook (local-exec):         "",
null_resource.RunCreateDatabasePlaybook (local-exec):         "powershell.exe exited on TF-CVAD-DDC1 with error code 0."
null_resource.RunCreateDatabasePlaybook (local-exec):     ],
null_resource.RunCreateDatabasePlaybook (local-exec):     "stdout": "\r\nPsExec v2.43 - Execute processes remotely\r\nCopyright (C) 2001-2023 Mark Russinovich\r\nSysinternals - www.sysinternals.com\r\n\r\n",
null_resource.RunCreateDatabasePlaybook (local-exec):     "stdout_lines": [
null_resource.RunCreateDatabasePlaybook (local-exec):         "",
null_resource.RunCreateDatabasePlaybook (local-exec):         "PsExec v2.43 - Execute processes remotely",
null_resource.RunCreateDatabasePlaybook (local-exec):         "Copyright (C) 2001-2023 Mark Russinovich",
null_resource.RunCreateDatabasePlaybook (local-exec):         "Sysinternals - www.sysinternals.com",
null_resource.RunCreateDatabasePlaybook (local-exec):         ""
null_resource.RunCreateDatabasePlaybook (local-exec):     ]
null_resource.RunCreateDatabasePlaybook (local-exec): }

null_resource.RunCreateDatabasePlaybook (local-exec): PLAY RECAP *********************************************************************
null_resource.RunCreateDatabasePlaybook (local-exec): tf-cvad-ddc1.the-austrian-citrix-guy.at : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

null_resource.RunCreateDatabasePlaybook: Creation complete after 35s [id=5576848404034541586]
data.external.CheckAnsibleReturnCodeCDB: Reading...
data.external.CheckAnsibleReturnCodeCDB: Read complete after 0s [id=-]
null_resource.IfExitCodeIsNotZeroThenHaltCDB: Creating...
null_resource.IfExitCodeIsNotZeroThenHaltCDB: Provisioning with 'local-exec'...
null_resource.IfExitCodeIsNotZeroThenHaltCDB (local-exec): Executing: ["/bin/bash" "-c" "[ \"1\" = \"1\" ] &amp;&amp; echo \"Create Databases - Everything OK\" || { echo \"Deploying the CVAD Databases failed. Please review the settings. Halting Terraform.\"; exit 1; }"]
null_resource.IfExitCodeIsNotZeroThenHaltCDB (local-exec): Create Databases - Everything OK
null_resource.IfExitCodeIsNotZeroThenHaltCDB: Creation complete after 0s [id=449387280135483830]
...
Running all Playbooks looks similar, omitted due to the length of the output.
...
null_resource.RunConfigureSetLicensingPlaybook (local-exec): PLAY RECAP *********************************************************************
null_resource.RunConfigureSetLicensingPlaybook (local-exec): tf-cvad-ddc1.the-austrian-citrix-guy.at : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

null_resource.RunConfigureSetLicensingPlaybook: Creation complete after 13s [id=5106544801416722566]
data.external.CheckAnsibleReturnCodeSetLicensing: Reading...
data.external.CheckAnsibleReturnCodeSetLicensing: Read complete after 0s [id=-]
null_resource.IfExitCodeIsNotZeroThenHaltSetLicensing: Creating...
null_resource.IfExitCodeIsNotZeroThenHaltSetLicensing: Provisioning with 'local-exec'...
null_resource.IfExitCodeIsNotZeroThenHaltSetLicensing (local-exec): Executing: ["/bin/bash" "-c" "[ \"1\" = \"1\" ] &amp;&amp; echo \"Licensing Configuration - Everything OK\" || { echo \"Configuration of Licensing failed. Please review the settings. Halting Terraform.\"; exit 1; }"]
null_resource.IfExitCodeIsNotZeroThenHaltSetLicensing (local-exec): Licensing Configuration - Everything OK
null_resource.IfExitCodeIsNotZeroThenHaltSetLicensing: Creation complete after 0s [id=1454545395290283154]

Apply complete! Resources: 26 added, 0 changed, 0 destroyed.
azadmin@tacg-cicd:/etc/terraform-tmm/CVADXS/_ConfigureSite$The Terraform snippet has successfully created the site and all needed configurations: The CVAD site was successfully created:  All site tests are successful:  With the CVAD site ready, we can proceed to the next step.  Part 9: Using Terraform to create the StoreFront deployment and all its necessitiesCreating a StoreFront deployment is a simple, straightforward task. Terraform can create the Storefront site on its own – no Ansible interaction is needed. Caution: As already mentioned, this module, Module 9, must be run on a Windows-based, domain-joined VM due to limitations in StoreFront´s automation capabilities. Terraform will sequentially run the following tasks: Create the StoreFront deployment Create a StoreFront Authentication service Create a StoreFront Store service Create a StoreFront WebReceiver service  Warning: The current Automation snippets of StoreFront will only run on Windows Powershell &lt;=5.1. They will not run on Powershell &gt;=6. Be sure to start the Terraform code on the correct PowerShell version. You may encounter this error after starting the Terraform snippet: Error: Error fetching StoreFront version
│
│   with citrix_stf_webreceiver_service.CreateSFWebReceiverService,
│   on CVADOnXS-ConfigureStoreFront.tf line 48, in resource "citrix_stf_webreceiver_service" "CreateSFWebReceiverService":
│   48: resource "citrix_stf_webreceiver_service" "CreateSFWebReceiverService" {
│
│ Error message: error executing command: Get-STFVersion
│  Error Message:
│  ConvertTo-SecureString : "ConvertTo-SecureString" was found in module "Microsoft.PowerShell.Security",
│ we were unable to load the module. Run "Import-Module
│ Microsoft.PowerShell.Security".
│ In row:1 column:205
│ + ... n@the-austrian-citrix-guy.at',(ConvertTo-SecureString -Force  ...
│ +                                            ~~~~~~~~~~~~~~~~~~~~~~
│     + CategoryInfo          : ObjectNotFound: (ConvertTo-SecureString:String) [], CommandNotFoundException
│     + FullyQualifiedErrorId : CouldNotAutoloadMatchingModuleThat error means you have not loaded the module Microsoft.PowerShell.Security in Windows PowerShell. Be sure to load the module in Windows PowerShell, not PowerShell: PS C:\__PPMM\__TF\_CVADOnXS-25.11\_ConfigureStoreFront&gt; Import-Module Microsoft.PowerShell.Security After loading the module in Windows PowerShell, the error should be gone and the Terraform snippet run through. Before running the snippet, the StoreFront server has no StoreFront site configured:  Example Terraform snippet to create a StoreFront site: # Terraform Deployment of Citrix CVAD on XenServer 8 - 25.11 
## Definition of XenServer Provider Variables - not all available variables are defined, only these we use
### Reference https://registry.terraform.io/providers/xenserver/xenserver/latest/docs
#### Configuring StoreFront 
##### Reference https://github.com/citrix/terraform-provider-citrix/blob/main/StoreFront.md
###### Create an initial StoreFront Deployment
resource "citrix_stf_deployment" "CreateInitialSFDeployment" {
  site_id       = var.TACG-TMM-SF-SiteID
  host_base_url = var.TACG-TMM-SF-SiteURL
  roaming_gateway = [
    {
      name                         = var.TACG-TMM-SF-Gateway-Name
      logon_type                   = var.TACG-TMM-SF-Gateway-LogonType
      gateway_url                  = var.TACG-TMM-SF-Gateway-URL
      callback_url                 = var.TACG-TMM-SF-Gateway-CallbackURL
      subnet_ip_address            = var.TACG-TMM-SF-Gateway-SNIP
      secure_ticket_authority_urls = var.TACG-TMM-SF-Gateway-STAs
    }
  ]
  roaming_beacon = {
    internal_address   = var.TACG-TMM-SF-Beacon-Internal
    external_addresses = var.TACG-TMM-SF-Beacon-External
  }
}

##### Create an Authentication service
resource "citrix_stf_authentication_service" "CreateSFAuthenticationService" {
  depends_on    = [citrix_stf_deployment.CreateInitialSFDeployment]
  site_id       = citrix_stf_deployment.CreateInitialSFDeployment.site_id
  friendly_name = var.TACG-TMM-SF-AuthService-Name
  virtual_path  = var.TACG-TMM-SF-AuthService-Path
}

##### Create a Store service
resource "citrix_stf_store_service" "CreateSFStoreService" {
  depends_on                          = [citrix_stf_authentication_service.CreateSFAuthenticationService]
  site_id                             = citrix_stf_deployment.CreateInitialSFDeployment.site_id
  virtual_path                        = var.TACG-TMM-SF-StoreService-Path
  friendly_name                       = var.TACG-TMM-SF-StoreService-Name
  authentication_service_virtual_path = citrix_stf_authentication_service.CreateSFAuthenticationService.virtual_path
  pna = {
    enable = false
  }
  farms = var.TACG-TMM-SF-StoreService-Farms
}

##### Create a WebReceiver service
resource "citrix_stf_webreceiver_service" "CreateSFWebReceiverService" {
  depends_on         = [citrix_stf_deployment.CreateInitialSFDeployment, citrix_stf_store_service.CreateSFStoreService]
  site_id            = citrix_stf_deployment.CreateInitialSFDeployment.site_id
  friendly_name      = var.TACG-TMM-SF-WebService-Name
  virtual_path       = var.TACG-TMM-SF-WebService-Path
  store_virtual_path = citrix_stf_store_service.CreateSFStoreService.virtual_path
  authentication_methods = [
    "ExplicitForms",
    "CitrixAGBasic"
  ]
  plugin_assistant = {
    enabled                 = var.TACG-TMM-SF-WebService-PA-Enabled
    html5_single_tab_launch = var.TACG-TMM-SF-WebService-PA-HTML5SingleTab
    upgrade_at_login        = var.TACG-TMM-SF-WebService-PA-UpgradeAtLogin
    html5_enabled           = var.TACG-TMM-SF-WebService-PA-HTML5Enabled
  }
  application_shortcuts = {
    prompt_for_untrusted_shortcuts = var.TACG-TMM-SF-WebService-AppSC-Prompt
    trusted_urls                   = var.TACG-TMM-SF-WebService-AppSC-TrustedURLs
  }
  user_interface = {
    auto_launch_desktop     = var.TACG-TMM-SF-WebService-UI-AutoLaunchDesktop
    multi_click_timeout     = var.TACG-TMM-SF-WebService-UI-Timeout
    enable_apps_folder_view = var.TACG-TMM-SF-WebService-UI-AppFolderView
    workspace_control = {
      enabled                 = var.TACG-TMM-SF-WebService-UI-WC-Enabled
      auto_reconnect_at_logon = var.TACG-TMM-SF-WebService-UI-WC-AutoReconnect
      logoff_action           = var.TACG-TMM-SF-WebService-UI-LogoffAction
      show_reconnect_button   = var.TACG-TMM-SF-WebService-UI-WC-ShowReconnect
      show_disconnect_button  = var.TACG-TMM-SF-WebService-UI-WC-ShowDisconnect
    }
    receiver_configuration = {
      enabled = var.TACG-TMM-SF-WebService-RC-Enabled
    }
    app_shortcuts = {
      enabled               = var.TACG-TMM-SF-WebService-ASC-Enabled
      show_desktop_shortcut = var.TACG-TMM-SF-WebService-ASC-ShowDesktopShortcut
    }
    ui_views = {
      show_apps_view     = var.TACG-TMM-SF-WebService-UIViews-Apps
      show_desktops_view = var.TACG-TMM-SF-WebService-UIViews-Desktops
      default_view       = var.TACG-TMM-SF-WebService-UIViews-Default
    }
    category_view_collapsed   = var.TACG-TMM-SF-WebService-CategoryView
    move_app_to_uncategorized = var.TACG-TMM-SF-WebService-Uncategorized
    progressive_web_app = {
      enabled             = var.TACG-TMM-SF-WebService-PWA-Enabled
      show_install_prompt = var.TACG-TMM-SF-WebService-PWA-Install
    }
    show_activity_manager = var.TACG-TMM-SF-WebService-ActivityManager
    show_first_time_use   = var.TACG-TMM-SF-WebService-FirstTimeUse
    prevent_ica_downloads = var.TACG-TMM-SF-WebService-PreventICADownloads
  }
  resources_service = {
    ica_file_cache_expiry         = var.TACG-TMM-SF-WebService-RS-IcaFileExpiry
    persistent_icon_cache_enabled = var.TACG-TMM-SF-WebService-RS-IconCacheEnabled
  }
}

##### A PNA site is not installed due to no effective needs
#resource "citrix_stf_xenapp_default_store" "CreateSFXenAppPath" {
#  depends_on         = [citrix_stf_store_service.CreateSFStoreService]
#  store_virtual_path = citrix_stf_store_service.CreateSFStoreService.virtual_path
#  store_site_id      = citrix_stf_store_service.CreateSFStoreService.site_id
#}
Running the snippet should fulfill all needed steps – during the run, the StoreFront server installs missing roles and features:  PS C:\_ppmm\__TF\CVADOnXS-2511\_ConfigureStoreFront&gt; terraform apply

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_stf_authentication_service.CreateSFAuthenticationService will be created
  + resource "citrix_stf_authentication_service" "CreateSFAuthenticationService" {
      + claims_factory_name = "standardClaimsFactory"
      + friendly_name       = "TACG-SF-AuthService"
      + site_id             = "1"
      + virtual_path        = "/Citrix/Authentication"
    }

  # citrix_stf_deployment.CreateInitialSFDeployment will be created
  + resource "citrix_stf_deployment" "CreateInitialSFDeployment" {
      + host_base_url   = "https://storefront.the-austrian-citrix-guy.at"
      + roaming_beacon  = {
          + external_addresses = [
              + "https://www.orf.at/",
              + "https://www.microsoft.com/",
            ]
          + internal_address   = "http://10.10.100.1/"
        }
      + roaming_gateway = [
          + {
              + callback_url                   = "https://access.the-austrian-citrix-guy.at/CitrixAuthService/AuthService.asmx"
              + gateway_url                    = "https://access.the-austrian-citrix-guy.at/"
              + is_cloud_gateway               = false
              + logon_type                     = "Domain"
              + name                           = "TACG-NS"
              + request_ticket_from_two_stas   = false
              + secure_ticket_authority_urls   = [
                  + {
                      + sta_url                = "http://tf-cvad-ddc1.the-austrian-citrix-guy.at/scripts/ctxsta.dll"
                      + sta_validation_enabled = false
                    },
                  + {
                      + sta_url                = "http://tf-cvad-ddc2.the-austrian-citrix-guy.at/scripts/ctxsta.dll"
                      + sta_validation_enabled = false
                    },
                ]
              + session_reliability            = false
              + smart_card_fallback_logon_type = "None"
              + stas_bypass_duration           = "0.1:0:0"
              + stas_use_load_balancing        = false
              + subnet_ip_address              = "10.10.119.112"
              + version                        = "Version10_0_69_4"
            },
        ]
      + site_id         = "1"
    }

  # citrix_stf_store_service.CreateSFStoreService will be created
  + resource "citrix_stf_store_service" "CreateSFStoreService" {
      + authentication_service_virtual_path = "/Citrix/Authentication"
      + farms                               = [
          + {
              + all_failed_bypass_duration     = 0
              + bypass_duration                = 60
              + farm_name                      = "TACG80"
              + farm_type                      = "XenDesktop"
              + load_balance                   = true
              + max_failed_servers_per_request = 0
              + port                           = 80
              + rade_ticket_time_to_live       = 100
              + server_urls                    = []
              + servers                        = [
                  + "tf-cvad-sf.the-austrian-citrix-guy.at",
                ]
              + ssl_relay_port                 = 443
              + ticket_time_to_live            = 200
              + transport_type                 = "HTTPS"
              + xml_validation_enabled         = false
              + zones                          = []
                # (4 unchanged attributes hidden)
            },
          + {
              + all_failed_bypass_duration     = 0
              + bypass_duration                = 60
              + farm_name                      = "TACG443"
              + farm_type                      = "XenDesktop"
              + load_balance                   = true
              + max_failed_servers_per_request = 0
              + port                           = 443
              + rade_ticket_time_to_live       = 100
              + server_urls                    = []
              + servers                        = [
                  + "tf-cvad-sf.the-austrian-citrix-guy.at",
                ]
              + ssl_relay_port                 = 443
              + ticket_time_to_live            = 200
              + transport_type                 = "HTTPS"
              + xml_validation_enabled         = false
              + zones                          = []
                # (4 unchanged attributes hidden)
            },
        ]
      + friendly_name                       = "TACG-SF-StoreService"
      + pna                                 = {
          + enable = false
        }
      + site_id                             = "1"
      + virtual_path                        = "/Citrix/Store"
    }

  # citrix_stf_webreceiver_service.CreateSFWebReceiverService will be created
  + resource "citrix_stf_webreceiver_service" "CreateSFWebReceiverService" {
      + application_shortcuts  = {
          + gateway_urls                   = []
          + prompt_for_untrusted_shortcuts = true
          + trusted_urls                   = [
              + "https://storefront.the-austrian-citrix-guy.at/",
            ]
        }
      + authentication_methods = [
          + "CitrixAGBasic",
          + "ExplicitForms",
        ]
      + friendly_name          = "TACG-SF-WebReceiverService"
      + plugin_assistant       = {
          + enabled                 = true
          + html5_enabled           = "Fallback"
          + html5_single_tab_launch = true
          + show_after_login        = false
          + upgrade_at_login        = true
        }
      + resources_service      = {
          + ica_file_cache_expiry         = 80
          + icon_size                     = 128
          + persistent_icon_cache_enabled = true
          + show_desktop_viewer           = true
        }

      + site_id                = "1"
      + store_virtual_path     = "/Citrix/Store"
      + user_interface         = {
          + app_shortcuts             = {
              + allow_session_reconnect = false
            }
          + auto_launch_desktop       = true
          + category_view_collapsed   = false
          + enable_apps_folder_view   = true
          + move_app_to_uncategorized = true
          + multi_click_timeout       = 3
          + prevent_ica_downloads     = false
          + progressive_web_app       = {
              + enabled             = false
              + show_install_prompt = false
            }
          + receiver_configuration    = {
              + download_url = (known after apply)
              + enabled      = true
            }
          + show_activity_manager     = true
          + show_first_time_use       = true
          + ui_views                  = {
              + default_view       = "Auto"
              + show_apps_view     = true
              + show_desktops_view = true
            }
          + workspace_control         = {
              + auto_reconnect_at_logon = true
              + enabled                 = true
              + logoff_action           = "Disconnect"
              + show_disconnect_button  = false
              + show_reconnect_button   = false
            }
        }
      + virtual_path           = "/Citrix/StoreWeb"
    }

Plan: 4 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_stf_deployment.CreateInitialSFDeployment: Creating...
citrix_stf_deployment.CreateInitialSFDeployment: Still creating... [00m10s elapsed]
citrix_stf_deployment.CreateInitialSFDeployment: Still creating... [00m20s elapsed]
citrix_stf_deployment.CreateInitialSFDeployment: Creation complete after 25s
citrix_stf_authentication_service.CreateSFAuthenticationService: Creating...
citrix_stf_authentication_service.CreateSFAuthenticationService: Creation complete after 6s
citrix_stf_store_service.CreateSFStoreService: Creating...
citrix_stf_store_service.CreateSFStoreService: Still creating... [00m10s elapsed]
citrix_stf_store_service.CreateSFStoreService: Creation complete after 16s
citrix_stf_webreceiver_service.CreateSFWebReceiverService: Creating...
citrix_stf_webreceiver_service.CreateSFWebReceiverService: Still creating... [00m10s elapsed]
citrix_stf_webreceiver_service.CreateSFWebReceiverService: Creation complete after 15s

Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
PS C:\_ppmm\__TF\CVADOnXS-2511\_ConfigureStoreFront&gt;The StoreFront server now has a valid StoreFront site deployed:  The StoreFront server now accepts logons:  With the StoreFront site ready, we can proceed to the final step: deploying all CVAD-related entities. Important: You can now switch back to the Ubuntu IaC machine or stay on the Windows machine to run the last Terraform snippet.  Part 10: Using Terraform to create all CVAD entities and all their necessitiesCreating the final steps is a simple, straightforward task. Terraform can create the CVAD entities site on its own – no Ansible interaction is needed. Create a StoreFront Server object Create a Hypervisor connection Create a Hypervisor connection resource pool Create a Machine Catalog Create a Delivery Group Create a Policy Set and its policies Before running the snippet, no CVAD-related entity is configured: No Hypervisor Connection and Hypervisor Connection pool exist:  No Machine Catalog exists:  No Delivery Group exists:  No dedicated Policy Set exists:  Example Terraform snippet to create all needed CVAD entities as mentioned above: # Terraform Deployment of Citrix CVAD on XenServer 8 - 25.11 
## Definition of XenCVADServer Provider Variables - not all available variables are defined, only these we use
### Reference https://registry.terraform.io/providers/citrix/citrix/latest/docs
#### Definition of all required local variables or data
##### Get XenServer-related Data
###### Get the VM SR object
data "xenserver_sr" "vmsr" {
  name_label = var.TACG-TMM-XS-CL-VMSR
}

/* output "name" {
  value = data.xenserver_sr.vmsr.data_items[0].name_label
} */

###### Get the Network object
data "xenserver_network" "network" {
}

##### Get CVAD-related Data
###### Get the Zone in CVAD
data "citrix_zone" "TACG-TMM-CVAD-Zone" {
  name = var.TACG-TMM-CVAD-Zone-Name
}

#### Create a StoreFront Server object - we assume a load-balanced server
resource "citrix_storefront_server" "CreateSFServer" {
  name        = var.TACG-TMM-SF-Server-Name
  description = var.TACG-TMM-SF-Server-Description
  url         = var.TACG-TMM-SF-Server-URL
  enabled     = true
}

#### Create a Hypervisor Connection
resource "citrix_xenserver_hypervisor" "TACG-TMM-CVAD-HypConn" {
  depends_on      = [data.citrix_zone.TACG-TMM-CVAD-Zone]
  name            = var.TACG-TMM-CVAD-HypConn-Name
  zone            = data.citrix_zone.TACG-TMM-CVAD-Zone.id
  username        = var.XS-CL-UN
  password        = var.XS-CL-PW
  password_format = "PlainText"
  addresses       = var.XS-CL-IPs
  ssl_thumbprints = var.XS-CL-SSLTP
}

resource "null_resource" "WriteProgress1" {
  depends_on = [citrix_xenserver_hypervisor.TACG-TMM-CVAD-HypConn]
  provisioner "local-exec" {
    command = "echo The Hypervisor Connection was successfully created..."
  }
}

#### Create a Hypervisor Connection Resource Pool
resource "citrix_xenserver_hypervisor_resource_pool" "TACG-TMM-CVAD-HypConnPool" {
  depends_on = [citrix_xenserver_hypervisor.TACG-TMM-CVAD-HypConn]
  name       = var.TACG-TMM-CVAD-HypConnPool-Name
  hypervisor = citrix_xenserver_hypervisor.TACG-TMM-CVAD-HypConn.id
  networks   = [data.xenserver_network.network.data_items[0].name_label]

  storage = [
    {
      storage_name = data.xenserver_sr.vmsr.data_items[0].name_label
    }
  ]
  temporary_storage = [
    {
      storage_name = data.xenserver_sr.vmsr.data_items[0].name_label
    }
  ]
  use_local_storage_caching = false
}

resource "null_resource" "WriteProgress2" {
  depends_on = [citrix_xenserver_hypervisor_resource_pool.TACG-TMM-CVAD-HypConnPool]
  provisioner "local-exec" {
    command = "echo The Hypervisor Connection Pool was successfully created..."
  }
}

#### Create the Machine Catalog
##### Important: Using CVAD Image Definitions and Image Versions is currently not supported on a XenServer-based deployment.
##### Important: You need to use standard Master Image VMs/Snapshots for creation of a Machine Catalog!
resource "citrix_machine_catalog" "TACG-TMM-CVAD-MC" {
  depends_on        = [citrix_xenserver_hypervisor_resource_pool.TACG-TMM-CVAD-HypConnPool]
  name              = var.TACG-TMM-CVAD-MC-Name
  description       = var.TACG-TMM-CVAD-MC-Description
  provisioning_type = var.TACG-TMM-CVAD-MC-ProvisioningType
  allocation_type   = var.TACG-TMM-CVAD-MC-AllocationType
  session_support   = var.TACG-TMM-CVAD-MC-SessionType
  zone              = data.citrix_zone.TACG-TMM-CVAD-Zone.id
  provisioning_scheme = {
    hypervisor               = citrix_xenserver_hypervisor.TACG-TMM-CVAD-HypConn.id
    hypervisor_resource_pool = citrix_xenserver_hypervisor_resource_pool.TACG-TMM-CVAD-HypConnPool.id
    identity_type            = var.TACG-TMM-CVAD-MC-IdentityType
    machine_domain_identity = {
      domain                   = var.TACG-TMM-CVAD-MC-VM-DomainFQDN
      domain_ou                = var.TACG-TMM-CVAD-MC-VM-DomainOU
      service_account          = var.TACG-TMM-CVAD-MC-VM-ServiceUserName
      service_account_password = var.TACG-TMM-CVAD-MC-VM-ServiceUserPassword
    }
    xenserver_machine_config = {
      master_image_vm                  = var.TACG-TMM-CVAD-MC-MasterImageVMName
      cpu_count                        = var.TACG-TMM-CVAD-MC-VM-CPUCount
      memory_mb                        = var.TACG-TMM-CVAD-MC-VM-MemorySize
      image_snapshot                   = var.TACG-TMM-CVAD-MC-MasterImageVMName-SnapshotName
      use_full_disk_clone_provisioning = var.TACG-TMM-CVAD-MC-MasterImageVMName-UseFullDiskClone
    }
    number_of_total_machines = var.TACG-TMM-CVAD-MC-VM-NumberOfVMs
    machine_account_creation_rules = {
      naming_scheme      = var.TACG-TMM-CVAD-MC-VM-NamingScheme
      naming_scheme_type = var.TACG-TMM-CVAD-MC-VM-NamingSchemeType
    }
  }
}

resource "null_resource" "WriteProgress3" {
  depends_on = [citrix_machine_catalog.TACG-TMM-CVAD-MC]
  provisioner "local-exec" {
    command = "echo The Machine Catalog was successfully created..."
  }
}

/* data "citrix_machine_catalog" "TACG-TMM-CVAD-MC" {
  name = var.TACG-TMM-CVAD-MC-Name
} */

#### Create the Delivery Group
resource "citrix_delivery_group" "TACG-TMM-CVAD-DG" {
  depends_on = [citrix_machine_catalog.TACG-TMM-CVAD-MC, citrix_storefront_server.CreateSFServer]
  #depends_on               = [data.citrix_machine_catalog.TACG-TMM-CVAD-MC]
  name                     = var.TACG-TMM-CVAD-DG-Name
  description              = var.TACG-TMM-CVAD-DG-Description
  minimum_functional_level = var.TACG-TMM-CVAD-DG-FunctionalLevel
  storefront_servers       = [citrix_storefront_server.CreateSFServer.id]
  associated_machine_catalogs = [
    {
      machine_catalog = citrix_machine_catalog.TACG-TMM-CVAD-MC.id
      #machine_catalog = data.citrix_machine_catalog.TACG-TMM-CVAD-MC.id
      machine_count = var.TACG-TMM-CVAD-MC-VM-NumberOfVMs
    }
  ]
  desktops = [
    {
      published_name = var.TACG-TMM-CVAD-DG-Desktops-Name
      description    = var.TACG-TMM-CVAD-DG-Desktops-Description
      restricted_access_users = {
        allow_list = var.TACG-TMM-CVAD-DG-Desktops-AllowList
      }
      enabled = var.TACG-TMM-CVAD-DG-Desktops-Enabled

      ###### Not needed due to Static assignment type-MC
      # enable_session_roaming = var.TACG-TMM-CVAD-DG-Desktops-SessionRoaming
    }

  ]
  autoscale_settings = {
    autoscale_enabled                   = var.TACG-TMM-CVAD-DG-AS-Enabled
    peak_disconnect_timeout_minutes     = var.TACG-TMM-CVAD-DG-AS-PeakDisconnectTime
    off_peak_disconnect_timeout_minutes = var.TACG-TMM-CVAD-DG-AS-OffPeakDisconnectTime
    peak_disconnect_action              = var.TACG-TMM-CVAD-DG-AS-PeakDisconnectAction
    off_peak_disconnect_action          = var.TACG-TMM-CVAD-DG-AS-OffPeakDisconnectAction
    off_peak_log_off_timeout_minutes    = var.TACG-TMM-CVAD-DG-AS-PeakLogoffTime
    peak_log_off_timeout_minutes        = var.TACG-TMM-CVAD-DG-AS-OffPeakLogoffTime
    peak_log_off_action                 = var.TACG-TMM-CVAD-DG-AS-PeakDisconnectAction
    off_peak_log_off_action             = var.TACG-TMM-CVAD-DG-AS-OffPeakDisconnectAction

    ###### Not needed due to Static assignment type-MC
    #log_off_off_peak_disconnected_session_after_seconds = var.TACG-TMM-CVAD-DG-AS-PeakLogoffTime
    #log_off_peak_disconnected_session_after_seconds     = var.TACG-TMM-CVAD-DG-AS-OffPeakLogoffTime
    #peak_log_off_action                                 = var.TACG-TMM-CVAD-DG-AS-PeakDisconnectAction
    #off_peak_log_off_action                             = var.TACG-TMM-CVAD-DG-AS-OffPeakDisconnectAction
    #log_off_reminder_enabled                            = var.TACG-TMM-CVAD-DG-AS-Enabled
    #log_off_reminder_message                            = var.TACG-TMM-CVAD-DG-RS-Notification-MessageBody
    #log_off_reminder_title                              = var.TACG-TMM-CVAD-DG-AS-Notification-MessageTitle
    #log_off_warning_message                             = var.TACG-TMM-CVAD-DG-RS-Notification-MessageBody
    #log_off_warning_title                               = var.TACG-TMM-CVAD-DG-AS-Notification-MessageTitle
    timezone = var.TACG-TMM-CVAD-DG-AS-TimeZone

    power_time_schemes = [
      {
        days_of_week          = var.TACG-TMM-CVAD-DG-AS-Days
        display_name          = var.TACG-TMM-CVAD-DG-AS-Name
        peak_time_ranges      = var.TACG-TMM-CVAD-DG-AS-PeakTime
        pool_using_percentage = false
      },
    ]
  }
  restricted_access_users = {
    allow_list = var.TACG-TMM-CVAD-DG-Desktops-AllowList
  }
  reboot_schedules = [
    {
      name                    = var.TACG-TMM-CVAD-DG-RS-Name
      reboot_schedule_enabled = var.TACG-TMM-CVAD-DG-RS-Enabled
      frequency               = var.TACG-TMM-CVAD-DG-RS-Frequency
      frequency_factor        = 1
      days_in_week            = var.TACG-TMM-CVAD-DG-RS-RebootDays
      start_time              = var.TACG-TMM-CVAD-DG-RS-RebootStartTime
      start_date              = var.TACG-TMM-CVAD-DG-RS-RebootStartDate
      reboot_duration_minutes = var.TACG-TMM-CVAD-DG-RS-RebootDuration
      ignore_maintenance_mode = var.TACG-TMM-CVAD-DG-RS-IgnoreMaintenanceMode
      natural_reboot_schedule = var.TACG-TMM-CVAD-DG-RS-NormalRebootSchedule

      ###### Not needed due to SingleSession type-MC
      #reboot_notification_to_users = {
      #notification_duration_minutes       = var.TACG-TMM-CVAD-DG-RS-Notification-Duration
      #notification_message                = var.TACG-TMM-CVAD-DG-RS-Notification-MessageBody
      #notification_title                  = var.TACG-TMM-CVAD-DG-RS-Notification-MessageTitle
      #notification_repeat_every_5_minutes = var.TACG-TMM-CVAD-DG-RS-Notification-MessageRepeat
      #} 
    }
  ]
}

resource "null_resource" "WriteProgress4" {
  depends_on = [citrix_delivery_group.TACG-TMM-CVAD-DG]
  provisioner "local-exec" {
    command = "echo The Delivery Group was successfully created..."
  }
}

#### Create a default Policy Set
resource "citrix_policy_set_v2" "TACG-TMM-CVAD-DefaultPolicySet" {
  depends_on      = [citrix_delivery_group.TACG-TMM-CVAD-DG]
  name            = var.TACG-TMM-CVAD-PS-Name
  description     = var.TACG-TMM-CVAD-PS-Description
  delivery_groups = [citrix_delivery_group.TACG-TMM-CVAD-DG.id]
}

#### Create a Policy in the Policy Set
resource "citrix_policy" "TACG-TMM-CVAD-DefaultPolicySet-Printing" {
  depends_on    = [citrix_policy_set_v2.TACG-TMM-CVAD-DefaultPolicySet]
  policy_set_id = citrix_policy_set_v2.TACG-TMM-CVAD-DefaultPolicySet.id
  name          = "Printing"
  description   = "Example of Printer-related policy in default Policy Set"
  enabled       = true
}

#### Create a Policy in the Policy Set
resource "citrix_policy" "TACG-TMM-CVAD-DefaultPolicySet-HDX" {
  depends_on    = [citrix_policy_set_v2.TACG-TMM-CVAD-DefaultPolicySet]
  policy_set_id = citrix_policy_set_v2.TACG-TMM-CVAD-DefaultPolicySet.id
  name          = "HDX™ Graphics"
  description   = "Example of HDX Graphics-related policy in default Policy Set"
  enabled       = true
}

#### Create a Policy in the Policy Set
resource "citrix_policy" "TACG-TMM-CVAD-DefaultPolicySet-ClientDrives" {
  depends_on    = [citrix_policy_set_v2.TACG-TMM-CVAD-DefaultPolicySet]
  policy_set_id = citrix_policy_set_v2.TACG-TMM-CVAD-DefaultPolicySet.id
  name          = "Client Drives"
  description   = "Example of Client Drive-related policy in default Policy Set"
  enabled       = true
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-Printing1" {
  policy_id   = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-Printing.id
  name        = "ClientPrinterAutoCreation"
  value       = "DefaultPrinterOnly"
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-Printing2" {
  policy_id   = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-Printing.id
  name        = "UniversalPrintDriverUsage"
  value       = "FallbackToSpecific"
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-HDX1" {
  policy_id   = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-HDX.id
  name        = "AllowVisuallyLosslessCompression"
  enabled     = true
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-HDX2" {
  policy_id   = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-HDX.id
  name        = "UseVideoCodecForCompression"
  value       = "UseVideoCodecIfPreferred"
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-HDX3" {
  policy_id   = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-HDX.id
  name        = "UseHardwareEncodingForVideoCodec"
  enabled     = true
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-CD1" {
  policy_id   = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-ClientDrives.id
  name        = "AutoConnectDrives"
  enabled     = true
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-CD2" {
  policy_id   = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-ClientDrives.id
  name        = "ClientDriveRedirection"
  enabled     = true
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-CD3" {
  policy_id   = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-ClientDrives.id
  name        = "ClientFixedDrives"
  enabled     = true
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-CD4" {
  policy_id   = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-ClientDrives.id
  name        = "ClientFloppyDrives"
  enabled     = false
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-CD5" {
  policy_id   = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-ClientDrives.id
  name        = "ClientOpticalDrives"
  enabled     = false
  use_default = false
}

#### Create the corresponding Policy settings
resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-CD6" {
  policy_id   = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-ClientDrives.id
  name        = "ClientNetworkDrives"
  enabled     = false
  use_default = false
}

#### Create the corresponding Policy Filters
resource "citrix_delivery_group_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-DGFilter-1" {
  depends_on        = [citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-Printing]
  policy_id         = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-Printing.id
  enabled           = true
  allowed           = true
  delivery_group_id = citrix_delivery_group.TACG-TMM-CVAD-DG.id
}

#### Create the corresponding Policy Filters
resource "citrix_delivery_group_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-DGFilter-2" {
  depends_on        = [citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-HDX]
  policy_id         = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-HDX.id
  enabled           = true
  allowed           = true
  delivery_group_id = citrix_delivery_group.TACG-TMM-CVAD-DG.id
}

#### Create the corresponding Policy Filters
resource "citrix_delivery_group_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-DGFilter-3" {
  depends_on        = [citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-ClientDrives]
  policy_id         = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-ClientDrives.id
  enabled           = true
  allowed           = true
  delivery_group_id = citrix_delivery_group.TACG-TMM-CVAD-DG.id
}

#### Create the corresponding Policy Filters
resource "citrix_user_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-SIDFilter-1" {
  depends_on = [citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-Printing]
  policy_id  = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-Printing.id
  enabled    = true
  allowed    = true
  sid        = var.TACG-TMM-CVAD-PS-UserSID
}

#### Create the corresponding Policy Filters
resource "citrix_user_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-SIDFilter-2" {
  depends_on = [citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-HDX]
  policy_id  = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-HDX.id
  enabled    = true
  allowed    = true
  sid        = var.TACG-TMM-CVAD-PS-UserSID
}

#### Create the corresponding Policy Filters
resource "citrix_user_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-SIDFilter-3" {
  depends_on = [citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-ClientDrives]
  policy_id  = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-ClientDrives.id
  enabled    = true
  allowed    = true
  sid        = var.TACG-TMM-CVAD-PS-UserSID
}

#### Create the corresponding Policy Filters
resource "citrix_client_ip_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-ClientIPFilter-1" {
  depends_on = [citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-Printing]
  policy_id  = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-Printing.id
  enabled    = true
  allowed    = true
  ip_address = var.TACG-TMM-CVAD-PS-IPRange
}

#### Create the corresponding Policy Filters
resource "citrix_client_ip_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-ClientIPFilter-2" {
  depends_on = [citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-HDX]
  policy_id  = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-HDX.id
  enabled    = true
  allowed    = true
  ip_address = var.TACG-TMM-CVAD-PS-IPRange
}

#### Create the corresponding Policy Filters
resource "citrix_client_ip_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-ClientIPFilter-3" {
  depends_on = [citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-ClientDrives]
  policy_id  = citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-ClientDrives.id
  enabled    = true
  allowed    = true
  ip_address = var.TACG-TMM-CVAD-PS-IPRange
}

resource "null_resource" "WriteProgress5" {
  depends_on = [citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD1, citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD2, citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD3, citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD4, citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD5, citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD6, citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-HDX1, citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-HDX2, citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-HDX3, citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-Printing1, citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-Printing2]
  provisioner "local-exec" {
    command = "echo The default Policy Set and its policies and filters were successfully created..."
  }
}Running the snippet should fulfill all needed steps: azadmin@tacg-cicd:/etc/terraform-tmm/CVADXS/_CreateCitrixEntities$ terraform apply
data.xenserver_network.network: Reading...
data.xenserver_sr.vmsr: Reading...
data.xenserver_network.network: Read complete after 0s
data.xenserver_sr.vmsr: Read complete after 0s
data.citrix_zone.TACG-TMM-CVAD-Zone: Reading...
data.citrix_zone.TACG-TMM-CVAD-Zone: Read complete after 0s [id=47010f19-5c99-4414-ab73-01a31aacff9a]

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_machine_catalog.TACG-TMM-CVAD-MC will be created
  + resource "citrix_machine_catalog" "TACG-TMM-CVAD-MC" {
      + allocation_type          = "Static"
      + built_in_scopes          = (known after apply)
      + delete_machine_accounts  = "None"
      + description              = "Terraform-created Machine Catalog for TACG running on XS8-Cluster"
      + id                       = (known after apply)
      + inherited_scopes         = (known after apply)
      + minimum_functional_level = "L7_20"
      + name                     = "TACG-TMM-CVAD-XS8-MC"
      + persist_user_changes     = (known after apply)
      + provisioning_scheme      = {
          + hypervisor                     = (known after apply)
          + hypervisor_resource_pool       = (known after apply)
          + identity_type                  = "ActiveDirectory"
          + machine_account_creation_rules = {
              + naming_scheme      = "tf-mcs-w11-#"
              + naming_scheme_type = "Numeric"
            }
          + machine_domain_identity        = {
              + domain                   = "the-austrian-citrix-guy.at"
              + domain_ou                = "OU=_COMPUTER,OU=TACG-CVAD,DC=the-austrian-citrix-guy,DC=at"
              + service_account          = (sensitive value)
              + service_account_password = (sensitive value)
            }
          + number_of_total_machines       = 2
          + xenserver_machine_config       = {
              + cpu_count                        = 2
              + image_snapshot                   = ""
              + master_image_note                = ""
              + master_image_vm                  = "W1125H2"
              + memory_mb                        = 4096
              + use_full_disk_clone_provisioning = false
            }
        }
      + provisioning_type        = "MCS"
      + scopes                   = []
      + session_support          = "SingleSession"
      + tenants                  = (known after apply)
      + zone                     = "47010f19-5c99-4414-ab73-01a31aacff9a"
    }

  # citrix_xenserver_hypervisor.TACG-TMM-CVAD-HypConn will be created
  + resource "citrix_xenserver_hypervisor" "TACG-TMM-CVAD-HypConn" {
      + addresses                                = [
          + "http://10.10.119.1",
        ]
      + id                                       = (known after apply)
      + max_absolute_active_actions              = 40
      + max_absolute_new_actions_per_minute      = 10
      + max_power_actions_percentage_of_machines = 20
      + name                                     = "TACG-TMM-CVAD-XS8-Cluster"
      + password                                 = (sensitive value)
      + password_format                          = "PlainText"
      + scopes                                   = []
      + ssl_thumbprints                          = (sensitive value)
      + tenants                                  = (known after apply)
      + username                                 = (sensitive value)
      + zone                                     = "47010f19-5c99-4414-ab73-01a31aacff9a"
    }

  # citrix_xenserver_hypervisor_resource_pool.TACG-TMM-CVAD-HypConnPool will be created
  + resource "citrix_xenserver_hypervisor_resource_pool" "TACG-TMM-CVAD-HypConnPool" {
      + hypervisor                = (known after apply)
      + id                        = (known after apply)
      + name                      = "TACG-TMM-CVAD-XS8-Cluster"
      + networks                  = [
          + "Network0",
        ]
      + storage                   = [
          + {
              + storage_name = "local storage"
              + superseded   = false
            },
        ]
      + temporary_storage         = [
          + {
              + storage_name = "local storage"
              + superseded   = false
            },
        ]
      + use_local_storage_caching = false
      + vm_tagging                = true
    }

  # null_resource.WriteProgress1 will be created
  + resource "null_resource" "WriteProgress1" {
      + id = (known after apply)
    }

  # null_resource.WriteProgress2 will be created
  + resource "null_resource" "WriteProgress2" {
      + id = (known after apply)
    }

  # null_resource.WriteProgress3 will be created
  + resource "null_resource" "WriteProgress3" {
      + id = (known after apply)
    }

# citrix_client_ip_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-ClientIPFilter-1 will be created
  + resource "citrix_client_ip_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-ClientIPFilter-1" {
      + allowed    = true
      + enabled    = true
      + id         = (known after apply)
      + ip_address = "10.10.0.0"
      + policy_id  = (known after apply)
    }

  # citrix_client_ip_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-ClientIPFilter-2 will be created
  + resource "citrix_client_ip_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-ClientIPFilter-2" {
      + allowed    = true
      + enabled    = true
      + id         = (known after apply)
      + ip_address = "10.10.0.0"
      + policy_id  = (known after apply)
    }

  # citrix_client_ip_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-ClientIPFilter-3 will be created
  + resource "citrix_client_ip_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-ClientIPFilter-3" {
      + allowed    = true
      + enabled    = true
      + id         = (known after apply)
      + ip_address = "10.10.0.0"
      + policy_id  = (known after apply)
    }

  # citrix_delivery_group.TACG-TMM-CVAD-DG will be created
  + resource "citrix_delivery_group" "TACG-TMM-CVAD-DG" {
      + associated_machine_catalogs = [
          + {
              + machine_catalog = "c052bbc4-d5a7-407a-baa3-d0faf901a0b1"
              + machine_count   = 2
            },
        ]
      + autoscale_settings          = {
          + autoscale_enabled                                     = true
          + disconnect_off_peak_idle_session_after_seconds        = 0
          + disconnect_peak_idle_session_after_seconds            = 0
          + log_off_off_peak_disconnected_session_after_seconds   = 0
          + log_off_peak_disconnected_session_after_seconds       = 0
          + log_off_reminder_enabled                              = false
          + log_off_reminder_message                              = ""
          + log_off_reminder_title                                = ""
          + log_off_warning_message                               = ""
          + log_off_warning_title                                 = ""
          + off_peak_buffer_size_percent                          = 0
          + off_peak_disconnect_action                            = "Suspend"
          + off_peak_disconnect_timeout_minutes                   = 5
          + off_peak_extended_disconnect_action                   = "Nothing"
          + off_peak_extended_disconnect_timeout_minutes          = 0
          + off_peak_limit_seconds_to_force_log_off_user          = 0
          + off_peak_log_off_action                               = "Suspend"
          + off_peak_log_off_reminder_interval                    = 0
          + off_peak_log_off_timeout_minutes                      = 5
          + peak_autoscale_assigned_power_on_idle_action          = "Nothing"
          + peak_autoscale_assigned_power_on_idle_timeout_minutes = 0
          + peak_buffer_size_percent                              = 0
          + peak_disconnect_action                                = "Suspend"
          + peak_disconnect_timeout_minutes                       = 5
          + peak_extended_disconnect_action                       = "Nothing"
          + peak_extended_disconnect_timeout_minutes              = 0
          + peak_limit_seconds_to_force_log_off_user              = 0
          + peak_log_off_action                                   = "Suspend"
          + peak_log_off_reminder_interval                        = 0
          + peak_log_off_timeout_minutes                          = 5
          + power_off_delay_minutes                               = 30
          + power_time_schemes                                    = [
              + {
                  + days_of_week          = [
                      + "Friday",
                      + "Monday",
                      + "Thursday",
                      + "Tuesday",
                      + "Wednesday",
                    ]
                  + display_name          = "TACG-TMM-CVAD-XS8-AS"
                  + peak_time_ranges      = [
                      + "09:00-17:00",
                    ]
                  + pool_using_percentage = false
                },
            ]
          + timezone                                              = "UTC"
        }
      + built_in_scopes             = (known after apply)
      + color_depth                 = "TwentyFourBit"
      + default_desktop_icon        = "1"
      + description                 = "Terraform-created Delivery Group for TACG running on XS8-Cluster"
      + desktops                    = [
          + {
              + description             = "Terraform-created W11 Desktops running on XS8-Cluster"
              + enabled                 = true
              + id                      = (known after apply)
              + published_name          = "TACG-XS8-W11"
              + restricted_access_users = {
                  + allow_list = [
                      + "TACG\\vdaallowed",
                    ]
                  + block_list = []
                }
            },
        ]
      + enabled                     = true
      + force_delete                = false
      + id                          = (known after apply)
      + in_maintenance_mode         = false
      + inherited_scopes            = (known after apply)
      + load_balancing_type         = "None"
      + minimum_functional_level    = "L7_20"
      + name                        = "TACG-TMM-CVAD-XS8-DG"
      + reboot_schedules            = [
          + {
              + days_in_week            = [
                  + "Sunday",
                ]
              + frequency               = "Weekly"
              + frequency_factor        = 1
              + ignore_maintenance_mode = true
              + name                    = "TACG-TMM-CVAD-XS8-RS"
              + natural_reboot_schedule = false
              + reboot_duration_minutes = 0
              + reboot_schedule_enabled = true
              + start_date              = "2025-01-01"
              + start_time              = "02:00"
                # (1 unchanged attribute hidden)
            },
        ]
      + restricted_access_users     = {
          + allow_list = [
              + "TACG\\vdaallowed",
            ]
          + block_list = []
        }
      + scopes                      = []
      + secure_ica_required         = false
      + tenants                     = (known after apply)
      + total_machines              = (known after apply)
    }

  # citrix_delivery_group_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-DGFilter-1 will be created
  + resource "citrix_delivery_group_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-DGFilter-1" {
      + allowed           = true
      + delivery_group_id = (known after apply)
      + enabled           = true
      + id                = (known after apply)
      + policy_id         = (known after apply)
    }

  # citrix_delivery_group_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-DGFilter-2 will be created
  + resource "citrix_delivery_group_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-DGFilter-2" {
      + allowed           = true
      + delivery_group_id = (known after apply)
      + enabled           = true
      + id                = (known after apply)
      + policy_id         = (known after apply)
    }

  # citrix_delivery_group_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-DGFilter-3 will be created
  + resource "citrix_delivery_group_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-DGFilter-3" {
      + allowed           = true
      + delivery_group_id = (known after apply)
      + enabled           = true
      + id                = (known after apply)
      + policy_id         = (known after apply)
    }

  # citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-ClientDrives will be created
  + resource "citrix_policy" "TACG-TMM-CVAD-DefaultPolicySet-ClientDrives" {
      + description   = "Example of Client Drive-related policy in default Policy Set"
      + enabled       = true
      + id            = (known after apply)
      + name          = "Client Drives"
      + policy_set_id = (known after apply)
    }

  # citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-HDX will be created
  + resource "citrix_policy" "TACG-TMM-CVAD-DefaultPolicySet-HDX" {
      + description   = "Example of HDX Graphics-related policy in default Policy Set"
      + enabled       = true
      + id            = (known after apply)
      + name          = "HDX Graphics"
      + policy_set_id = (known after apply)
    }

  # citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-Printing will be created
  + resource "citrix_policy" "TACG-TMM-CVAD-DefaultPolicySet-Printing" {
      + description   = "Example of Printer-related policy in default Policy Set"
      + enabled       = true
      + id            = (known after apply)
      + name          = "Printing"
      + policy_set_id = (known after apply)
    }

  # citrix_policy_set_v2.TACG-TMM-CVAD-DefaultPolicySet will be created
  + resource "citrix_policy_set_v2" "TACG-TMM-CVAD-DefaultPolicySet" {
      + assigned        = (known after apply)
      + delivery_groups = [
          + (known after apply),
        ]
      + description     = "Terraform-created default Policy Set for TACG running on XS8-Cluster"
      + id              = (known after apply)
      + name            = "TACG-TMM-CVAD-XS8-PS-Default"
      + scopes          = []
    }

  # citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD1 will be created
  + resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-CD1" {
      + enabled     = true
      + id          = (known after apply)
      + name        = "AutoConnectDrives"
      + policy_id   = (known after apply)
      + use_default = false
    }

  # citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD2 will be created
  + resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-CD2" {
      + enabled     = true
      + id          = (known after apply)
      + name        = "ClientDriveRedirection"
      + policy_id   = (known after apply)
      + use_default = false
    }

  # citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD3 will be created
  + resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-CD3" {
      + enabled     = true
      + id          = (known after apply)
      + name        = "ClientFixedDrives"
      + policy_id   = (known after apply)
      + use_default = false
    }

  # citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD4 will be created
  + resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-CD4" {
      + enabled     = false
      + id          = (known after apply)
      + name        = "ClientFloppyDrives"
      + policy_id   = (known after apply)
      + use_default = false
    }

  # citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD5 will be created
  + resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-CD5" {
      + enabled     = false
      + id          = (known after apply)
      + name        = "ClientOpticalDrives"
      + policy_id   = (known after apply)
      + use_default = false
    }

  # citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD6 will be created
  + resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-CD6" {
      + enabled     = false
      + id          = (known after apply)
      + name        = "ClientNetworkDrives"
      + policy_id   = (known after apply)
      + use_default = false
    }

  # citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-HDX1 will be created
  + resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-HDX1" {
      + enabled     = true
      + id          = (known after apply)
      + name        = "AllowVisuallyLosslessCompression"
      + policy_id   = (known after apply)
      + use_default = false
    }

  # citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-HDX2 will be created
  + resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-HDX2" {
      + id          = (known after apply)
      + name        = "UseVideoCodecForCompression"
      + policy_id   = (known after apply)
      + use_default = false
      + value       = "UseVideoCodecIfPreferred"
    }

  # citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-HDX3 will be created
  + resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-HDX3" {
      + enabled     = true
      + id          = (known after apply)
      + name        = "UseHardwareEncodingForVideoCodec"
      + policy_id   = (known after apply)
      + use_default = false
    }

  # citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-Printing1 will be created
  + resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-Printing1" {
      + id          = (known after apply)
      + name        = "ClientPrinterAutoCreation"
      + policy_id   = (known after apply)
      + use_default = false
      + value       = "DefaultPrinterOnly"
    }

  # citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-Printing2 will be created
  + resource "citrix_policy_setting" "TACG-TMM-CVAD-DefaultPolicySet-Printing2" {
      + id          = (known after apply)
      + name        = "UniversalPrintDriverUsage"
      + policy_id   = (known after apply)
      + use_default = false
      + value       = "FallbackToSpecific"
    }

  # citrix_user_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-SIDFilter-1 will be created
  + resource "citrix_user_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-SIDFilter-1" {
      + allowed   = true
      + enabled   = true
      + id        = (known after apply)
      + policy_id = (known after apply)
      + sid       = "S-1-5-21-3919795815-764310115-1491410927-2116"
    }

  # citrix_user_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-SIDFilter-2 will be created
  + resource "citrix_user_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-SIDFilter-2" {
      + allowed   = true
      + enabled   = true
      + id        = (known after apply)
      + policy_id = (known after apply)
      + sid       = "S-1-5-21-3919795815-764310115-1491410927-2116"
    }

  # citrix_user_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-SIDFilter-3 will be created
  + resource "citrix_user_policy_filter" "TACG-TMM-CVAD-DefaultPolicySet-SIDFilter-3" {
      + allowed   = true
      + enabled   = true
      + id        = (known after apply)
      + policy_id = (known after apply)
      + sid       = "S-1-5-21-3919795815-764310115-1491410927-2116"
    }

  # null_resource.WriteProgress4 will be created
  + resource "null_resource" "WriteProgress4" {
      + id = (known after apply)
    }

  # null_resource.WriteProgress5 will be created
  + resource "null_resource" "WriteProgress5" {
      + id = (known after apply)
    }

Plan: 33 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_xenserver_hypervisor.TACG-TMM-CVAD-HypConn: Creating...
citrix_xenserver_hypervisor.TACG-TMM-CVAD-HypConn: Still creating... [00m10s elapsed]
citrix_xenserver_hypervisor.TACG-TMM-CVAD-HypConn: Still creating... [00m20s elapsed]
citrix_xenserver_hypervisor.TACG-TMM-CVAD-HypConn: Still creating... [00m30s elapsed]
citrix_xenserver_hypervisor.TACG-TMM-CVAD-HypConn: Still creating... [00m40s elapsed]
citrix_xenserver_hypervisor.TACG-TMM-CVAD-HypConn: Creation complete after 41s [id=f6f129aa-c9c6-4b9f-84c3-cb0834342fa6]
null_resource.WriteProgress1: Creating...
null_resource.WriteProgress1: Provisioning with 'local-exec'...
null_resource.WriteProgress1 (local-exec): Executing: ["/bin/sh" "-c" "echo The Hypervisor Connection was successfully created..."]
null_resource.WriteProgress1 (local-exec): The Hypervisor Connection was successfully created...
null_resource.WriteProgress1: Creation complete after 0s [id=7102860708396185821]
citrix_xenserver_hypervisor_resource_pool.TACG-TMM-CVAD-HypConnPool: Creating...
citrix_xenserver_hypervisor_resource_pool.TACG-TMM-CVAD-HypConnPool: Still creating... [00m10s elapsed]
citrix_xenserver_hypervisor_resource_pool.TACG-TMM-CVAD-HypConnPool: Still creating... [00m20s elapsed]
citrix_xenserver_hypervisor_resource_pool.TACG-TMM-CVAD-HypConnPool: Still creating... [00m30s elapsed]
citrix_xenserver_hypervisor_resource_pool.TACG-TMM-CVAD-HypConnPool: Still creating... [00m40s elapsed]
citrix_xenserver_hypervisor_resource_pool.TACG-TMM-CVAD-HypConnPool: Creation complete after 40s [id=6d2214be-5d23-44bb-8f90-90acc6151e97]
null_resource.WriteProgress2: Creating...
null_resource.WriteProgress2: Provisioning with 'local-exec'...
null_resource.WriteProgress2 (local-exec): Executing: ["/bin/sh" "-c" "echo The Hypervisor Connection Pool was successfully created..."]
null_resource.WriteProgress2 (local-exec): The Hypervisor Connection Pool was successfully created...
null_resource.WriteProgress2: Creation complete after 0s [id=4353748660640660049]
citrix_machine_catalog.TACG-TMM-CVAD-MC: Creating...
citrix_machine_catalog.TACG-TMM-CVAD-MC: Still creating... [00m10s elapsed]
citrix_machine_catalog.TACG-TMM-CVAD-MC: Still creating... [00m20s elapsed]
citrix_machine_catalog.TACG-TMM-CVAD-MC: Still creating... [00m30s elapsed]
citrix_machine_catalog.TACG-TMM-CVAD-MC: Still creating... [00m40s elapsed]
...

citrix_machine_catalog.TACG-TMM-CVAD-MC: Still creating... [09m20s elapsed]
citrix_machine_catalog.TACG-TMM-CVAD-MC: Creation complete after 41s [id=47df73ea-fde2-3431-33f13-ae8909328dd1]
...
citrix_delivery_group.TACG-TMM-CVAD-DG: Creating...
citrix_delivery_group.TACG-TMM-CVAD-DG: Still creating... [00m10s elapsed]
citrix_delivery_group.TACG-TMM-CVAD-DG: Still creating... [00m20s elapsed]
citrix_delivery_group.TACG-TMM-CVAD-DG: Creation complete after 21s [id=e57c8f65-c8a2-4788-bdda-dac19816192b]
null_resource.WriteProgress4: Creating...
citrix_policy_set_v2.TACG-TMM-CVAD-DefaultPolicySet: Creating...
null_resource.WriteProgress4: Provisioning with 'local-exec'...
null_resource.WriteProgress4 (local-exec): Executing: ["/bin/sh" "-c" "echo The Delivery Group was successfully created..."]
null_resource.WriteProgress4 (local-exec): The Delivery Group was successfully created...
null_resource.WriteProgress4: Creation complete after 0s [id=1603009483001819548]
citrix_policy_set_v2.TACG-TMM-CVAD-DefaultPolicySet: Still creating... [00m10s elapsed]
citrix_policy_set_v2.TACG-TMM-CVAD-DefaultPolicySet: Still creating... [00m20s elapsed]
citrix_policy_set_v2.TACG-TMM-CVAD-DefaultPolicySet: Still creating... [00m30s elapsed]
citrix_policy_set_v2.TACG-TMM-CVAD-DefaultPolicySet: Creation complete after 30s [id=f06d0abd-bfba-4da2-853a-22f1d0f29c94]
citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-ClientDrives: Creating...
citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-HDX: Creating...
citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-Printing: Creating...
citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-HDX: Creation complete after 1s [id=7d2605d3-8e15-4358-81c0-3e6fecefe31c]
citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-Printing: Creation complete after 1s [id=ccc01f86-0120-4293-85e6-92ca430553c6]
citrix_policy.TACG-TMM-CVAD-DefaultPolicySet-ClientDrives: Creation complete after 1s [id=51ee45df-68c8-4221-a9f4-b65fa37c0299]
citrix_user_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-SIDFilter-2: Creating...
citrix_delivery_group_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-DGFilter-2: Creating...
citrix_client_ip_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-ClientIPFilter-2: Creating...
citrix_delivery_group_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-DGFilter-1: Creating...
citrix_client_ip_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-ClientIPFilter-1: Creating...
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-HDX3: Creating...
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-HDX1: Creating...
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-Printing1: Creating...
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-HDX2: Creating...
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-Printing2: Creating...
citrix_user_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-SIDFilter-2: Creation complete after 1s [id=f4e7a4ba-b9af-4808-86bf-451f2747729d]
citrix_user_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-SIDFilter-1: Creating...
citrix_delivery_group_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-DGFilter-2: Creation complete after 1s [id=30c858f9-437c-42af-b917-2938284aae76]
citrix_client_ip_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-ClientIPFilter-3: Creating...
citrix_client_ip_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-ClientIPFilter-2: Creation complete after 1s [id=cc621016-8e1e-48e8-bdcd-244d6bcdaf6e]
citrix_delivery_group_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-DGFilter-3: Creating...
citrix_delivery_group_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-DGFilter-1: Creation complete after 1s [id=1b24d545-130a-429e-8c7e-a8340d38d18c]
citrix_client_ip_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-ClientIPFilter-1: Creation complete after 1s [id=b6a0b752-fcc1-4bcc-87a1-34bc132bddb1]
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD3: Creating...
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD6: Creating...
citrix_user_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-SIDFilter-1: Creation complete after 0s [id=64f9bffa-bd49-44f8-953a-ae15f7137838]
citrix_client_ip_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-ClientIPFilter-3: Creation complete after 0s [id=0c1174c4-cc2c-45cb-9183-aca6857b46f1]
citrix_delivery_group_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-DGFilter-3: Creation complete after 0s [id=dc53bebb-9ddf-409c-9df3-a29abf995bd0]
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD2: Creating...
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD5: Creating...
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD4: Creating...
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD2: Creation complete after 1s [id=99327137-cee4-4ad9-81c8-cdc489bf9bf5]
citrix_user_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-SIDFilter-3: Creating...
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-HDX3: Creation complete after 3s [id=d80f6146-6a4f-4950-8f0f-1bf157f64824]
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-Printing1: Creation complete after 3s [id=41fe293e-0f1a-48a9-bc13-83b44f7d1fb9]
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD1: Creating...
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-HDX1: Creation complete after 3s [id=89866698-9c9e-4c9a-946b-a0e6cca6b856]
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-HDX2: Creation complete after 3s [id=e40d7c92-2b67-4d85-94c6-9d648cc4cd16]
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD5: Creation complete after 1s [id=32b89ef9-d542-4a09-8863-391a7132c80d]
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD6: Creation complete after 2s [id=0d7e6233-946a-442b-96ab-004a62e72088]
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD4: Creation complete after 1s [id=45d0697b-b39f-4e70-9c8d-bfb50aef7db2]
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD3: Creation complete after 2s [id=09d39467-ba5a-4057-ad3c-427789900339]
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-Printing2: Creation complete after 3s [id=2297952e-611f-4d08-b9c2-a9587d119838]
citrix_user_policy_filter.TACG-TMM-CVAD-DefaultPolicySet-SIDFilter-3: Creation complete after 0s [id=a9332544-c5d4-495b-b800-12f976d4cdef]
citrix_policy_setting.TACG-TMM-CVAD-DefaultPolicySet-CD1: Creation complete after 0s [id=dde0f25c-7a9e-4429-b1c7-7d5b978a180e]
null_resource.WriteProgress5: Creating...
null_resource.WriteProgress5: Provisioning with 'local-exec'...
null_resource.WriteProgress5 (local-exec): Executing: ["/bin/sh" "-c" "echo The default Policy Set and its policies and filters were successfully created..."]
null_resource.WriteProgress5 (local-exec): The default Policy Set and its policies and filters were successfully created...
null_resource.WriteProgress5: Creation complete after 0s [id=3134505013310827667]

Apply complete! Resources: 33 added, 0 changed, 0 destroyed.
azadmin@tacg-cicd:/etc/terraform-tmm/CVADXS/_CreateCitrixEntities$Terraform has successfully installed all needed Citrix entities: A Hypervisor Connection and Hypervisor Connection Pool were created:  All related tests are OK:  A Machine Catalog was created:  All related tests are OK:  A Delivery Group was created:  All related tests are OK:  A dedicated Policy Set and its policies were created:  WebStudio shows a complete, error-free deployment of Citrix Virtual Apps and Desktops 2507 LTSR:  As the deployment is complete, we can now log on to StoreFront and start our assigned desktop.  Using the DeploymentAs all prerequisites are met, we can now log on to our CVAD site and start our assigned VDI desktop. Logging on through Citrix Workspace App or Storefront is possible:   Let´s choose our desktop and start it:    That completes our guide “Using Infrastructure-as-Code for deploying Citrix Virtual Apps and Desktops 2507 LTSR on XenServer 8.4”. As the importance of Infrastructure-as-Code and Automation steadily increases, we will continue to focus on these topics. Stay tuned for new guides and our soon-to-be-published Automation Handbook on our Automation landing page in Citrix TechZone (https://community.citrix.com/tech-zone/automation), where we cover all relevant aspects of IaC and Automation in a Citrix Environment. DisclaimerDisclaimer: EXCEPT WHERE EXPRESSLY PROVIDED OTHERWISE BY CLOUD SOFTWARE GROUP, THE CONTENT IN THIS DOCUMENT IS PROVIDED “AS IS” AND CLOUD SOFTWARE GROUP HEREBY DISCLAIMS ALL EXPRESS OR IMPLIED REPRESENTATIONS, WARRANTIES, GUARANTIES, AND CONDITIONS, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES OR CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD TO BE LEGALLY INVALID. CLOUD SOFTWARE GROUP MAKES NO REPRESENTATIONS, WARRANTIES, GUARANTIES, OR CONDITIONS AS TO THE QUALITY, SUITABILITY, TRUTH, ACCURACY, OR COMPLETENESS OF ANY OF THE CONTENT CONTAINED ON THE WEBSITE. The above disclaimer applies to any damages, liability, or injuries caused by any failure of performance, error, omission, interruption, deletion, defect, delay in operation or transmission, computer virus, communication line failure, theft or destruction of or unauthorized access to, alteration of, or use, whether for breach of contract, tort, negligence or any other cause of action.]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2025_11/overview.png.b83e5d9c061560c045c559a402e89aff.png" length="152602" type="image/png"/><pubDate>Wed, 19 Nov 2025 13:08:00 +0000</pubDate></item><item><title>Citrix Integration for Windows 365</title><link>https://community.stage.citrix.com/tech-zone/design/reference-architectures/citrix-integration-for-windows-365/</link><description><![CDATA[AudienceThe Citrix Integration for Windows 365 Reference Architecture is intended for Citrix practitioners—including administrators, engineers, architects, partners, and consultants—who design, build, and maintain Citrix platform deployments for enterprises of all sizes. OverviewCitrix Desktops-as-a-Service (DaaS) enables you to deliver high-performance virtual apps and desktops securely to any device. Managed by a Citrix-hosted service, this solution securely grants end-users access to Windows, Linux, and macOS (limited to desktop) applications and desktops from a central location, regardless of the operating system on their endpoints. The Citrix Integration for Windows 365 combines Citrix DaaS™ with Windows 365 to provide a high-performance, secure, and easy-to-manage solution. This integration enables organizations to offer a seamless hybrid work experience, supporting a variety of use cases beyond standard Windows 365 features. The Citrix Integration for Windows 365 Reference Architecture provides an overview of the Windows 365 service, a conceptual architecture of the Citrix DaaS™ and Windows 365 integration, traffic flow diagrams for the integration, and use cases for the integration. Windows 365 OverviewMicrosoft Windows 365 is a fully managed desktop virtualization SaaS platform hosted in Microsoft Azure. Windows 365 provides dedicated, persistent, turn-key desktops for a flat-rate price. Windows 365 is available in four editions as detailed here: Edition Description Windows 365 Business1 Geared towards smaller companies with up to 300 seats that need a ready-to-use solution with easy management options and is only available within a fully Microsoft-managed Azure subscription. Windows 365 Enterprise Provides enterprises with unlimited seats and full integration with Microsoft Intune, allowing for custom image creation, connection to enterprise networks, and additional management options. Windows 365 Government2 Provides Cloud PCs within the regulated US Government Azure cloud, providing a regulated and secure environment. *Please note that Windows 365 Government is currently not supported by Citrix. Windows 365 Frontline Dedicated Provides enterprises with a single Windows 365 license that provisions up to three Cloud PCs for nonconcurrent use. Windows 365 Frontline Shared 2 Provides enterprises with the ability to provision a single Cloud PC that can be shared non-concurrently among a group of users. 1 This edition does not apply to the Citrix DaaS + Windows 365 Reference Architecture. Any references to Windows 365 from now on will only pertain to the Enterprise or Frontline Dedicated editions. 2 Please note that these editions are currently not supported by Citrix. Windows 365 supports four identity and two device join types: Identity Type Description Hybrid Users are created in an on-premises Active Directory and synchronized to Microsoft Entra ID with the Microsoft Entra ID Connector. Cloud-only Users are created directly in Microsoft Entra ID, and the cloud service handles authentication. Federated Provides support for users created in a third-party identity provider other than Active Directory or Entra ID, and then federated with Entra ID. External Users who are created and managed outside of Entra ID are invited into the Entra ID tenant to access resources.  Device Join Type Description Microsoft Entra hybrid joined Joins the Cloud PC to the on-premises Active Directory and Microsoft Entra ID via Microsoft Entra Connect. Microsoft Entra joined Joins the Cloud PC directly to Entra ID. Microsoft Intune manages provisioning, software installs, updates, patching, and policy enforcement for all Cloud PCs. Users connect to Cloud PCs over the Remote Desktop Protocol (RDP) through the Windows 365 Gateway, which is provided by the Azure Virtual Desktop (AVD) service. The AVD service also provides connection brokering and web client service.  Citrix Integration for Windows 365 Conceptual ArchitectureThe conceptual architecture for integrating Citrix DaaS with Windows 365 brings together Citrix’s high-performance application and desktop delivery capabilities with Microsoft’s Windows 365 platform, all under a unified identity and management framework.   The Citrix Integration for Windows 365 supports both Microsoft Entra joined and Microsoft Entra Hybrid joined Cloud PCs. The supported architectures are illustrated at a high level here. Note: Citrix supports connecting multiple Windows 365 tenants to a single Citrix DaaS tenant. This is not detailed here in this architecture. Microsoft Entra Joined Devices Following the standard Citrix architecture layer method, the conceptual architecture for Microsoft Entra joined devices consists of five layers. User Layer The User Layer includes Citrix Workspace™ app endpoints on all supported devices, such as Windows, Linux, macOS, iOS, Android, and browser-based clients. Users access a single Workspace interface to view both Citrix-delivered applications and desktops, as well as access their Windows 365 Cloud PCs. Microsoft Intune handles endpoint management, configuration, application deployment, and compliance reporting, ensuring that devices meet security standards. Citrix real-time deviceTrust checks verify a device's compliance and context (such as location, security status, or the presence of unauthorized USB devices) to enforce granular, contextual access policies. Access Layer In the Microsoft Entra Joined architecture, Citrix Workspace and the Citrix Gateway Service provide the Access Layer. All users authenticate with Citrix Workspace, with Microsoft Entra ID set as the identity provider (IdP). At the same time, the Citrix Gateway service provides VPN-less access to their Windows 365 Cloud PCs alongside other Citrix desktop and application resources (virtual desktops, published applications, Web/SaaS applications). Connections to Windows 365 Cloud PCs use connector-less connections with the Citrix Rendezvous protocol, so Citrix Cloud™ Connectors are not required within the Windows 365 Resource Locations. Control Layer In the Control Layer, Citrix DaaS orchestrates brokering, policy enforcement, resource aggregation, and monitoring for the Windows 365 Cloud PCs. The Citrix Windows 365 Connector is enabled in Intune, allowing a connection between the Windows 365 service and Citrix Cloud. The Citrix DaaS VDA Upgrade service is used to upgrade the Citrix Virtual Delivery Agent on each Cloud PC. From the user’s perspective, all resources, whether hosted in Citrix infrastructure or delivered via Windows 365, appear side by side in a single workspace. The Windows 365 platform is akin to other virtual desktop hosting platforms like Azure, AWS, and on-premises hypervisors, and is viewed by Citrix DaaS as a Resource Location. Resource Layer In the Resource Layer, the Microsoft tenant provides the core Windows 365 Cloud PCs. When a Windows 365 Cloud PC is launched from Citrix Workspace™, the user is seamlessly directed through the Citrix Gateway service and optimized with HDX™. This layered approach ensures that each platform is used for what it does best—Citrix for rich, virtualized applications and complex, multi-session environments, and Windows 365 for personal, always-on cloud desktops—while still delivering a unified and secure user experience. Observability Layer The Observability Layer includes all other layers, providing end-to-end visibility, diagnostics, and user experience analytics. Citrix DaaS Monitor offers detailed insights into Citrix sessions, logon performance, HDX™ bandwidth, and application responsiveness. This enables administrators to identify issues in real-time, analyze trends, and enhance performance across the entire Citrix environment. For endpoint-level telemetry, uberAgent® is installed on Workspace app endpoints to collect comprehensive performance and UX metrics, such as logon duration breakdowns, application load times, and resource usage—covering both Citrix virtual sessions and local workloads. These tools create a unified observability framework that links Citrix-hosted resources, Windows 365 Cloud PCs, and endpoint devices, supporting a proactive approach to troubleshooting and performance improvement. Microsoft Entra hybrid joined DevicesFollowing the standard Citrix architecture layer method, the conceptual architecture for Microsoft Entra hybrid joined devices consists of the same five layers noted above. However, with the Hybrid Entra ID Join architecture, new options are available for the Access Layer, as multiple on-premises components are introduced. Citrix Cloud Access Layer Changes to the Microsoft Entra hybrid joined architecture using the Citrix Cloud Access Layer include: Within the Control Layer, an on-premises site has been added. This site includes Microsoft Entra ID Connect, which enables synchronization between Active Directory and Entra ID. Cloud Connectors are also deployed, enabling Active Directory integration with Citrix and Windows 365 Cloud PCs. Finally, Citrix Federated Authentication Service (FAS) servers are set up to provide Single Sign-On (SSO) access to Windows 365 Cloud PCs. Citrix Workspace and Citrix Gateway continue to manage user authentication; however, in the Hybrid Entra ID join model, any of the supported identity platforms by Citrix Workspace can be used. In this architecture, Hybrid Entra ID authentication is employed. On-Premises Access Layer Changes to the Microsoft Entra hybrid joined architecture using an on-premises Access Layer include: Within the Control Layer, an on-premises location has been added. This location includes Microsoft Entra ID Connect, which enables synchronization between Active Directory and Entra ID. Cloud Connectors are also deployed, enabling Active Directory integration with Citrix and Windows 365 Cloud PCs. By default, the Windows 365 Cloud PCs are connector-less, and the cloud connectors are only needed to connect Citrix Cloud to the AD domain. However, you can choose to have the VDAs register via cloud connectors if required. More details can be found in the product documentation. Lastly, Citrix Federated Authentication Service (FAS) is implemented to provide Single Sign-On (SSO) access to the Windows 365 Cloud PCs. The Access Layer has been moved to the on-premises location, and Citrix StoreFront and NetScaler Gateway are now used for user access, authentication, and enumeration of the Windows 365 Cloud PCs. Citrix Integration for Windows 365 Configuration Configuring the integration between Citrix DaaS and Windows 365 involves several components that administrators should be familiar with, including Citrix Cloud™ and the Citrix DaaS service, as well as the Microsoft Windows 365 service and Windows 365 Cloud PCs. Citrix Cloud handles the integration of the Citrix DaaS and Windows 365 services by registering and assigning Cloud PC licenses within Citrix DaaS. A Cloud PC registration token is generated to establish trust between the Cloud PCs and the Citrix Cloud services. Microsoft Entra ID manages user and Cloud PC identities. It also enables authentication and authorization between Windows 365 and Citrix Cloud services. The process of integrating Citrix DaaS and Windows 365 involves several workflows that Citrix administrators must initiate, as well as background service-related workflows. Windows 365 Connect Flow A one-time operation, Windows 365 Connect Flow provides consent for Citrix Cloud to obtain the necessary permissions. Following the Azure consent framework, an Azure application is registered within the customer's Entra ID tenant. A Global Administrator must grant these permissions, as Citrix Cloud will perform all tasks on behalf of administrators. When consent is granted, a new service principal is created using the Citrix application template with the required permissions. Citrix Cloud is granted an access token on behalf of the customer's tenant and is used to communicate with the Windows 365 service. Once consent is complete, Citrix Cloud stores the connection information in a database that includes the Windows 365 tenant ID. It lets the Windows 365 service know that a new Citrix customer is connected to the tenant. The Citrix Integration for Windows 365 supports connecting to multiple Entra ID tenants. License Assignment Flow The License Assignment Flow begins when the Citrix administrator assigns a Citrix license to a Windows 365 user or group. Citrix Cloud provides a registration token begins the Cloud PC registration process, which includes installation of the Citrix Virtual Delivery Agent (VDA), Cloud PC registration with Citrix DaaS, creation of the Machine Catalog and Delivery Group, adding the Cloud PC to the Machine Catalog and Delivery Group, assign the user to the Cloud PC, and create a Citrix policy enabling the rendezvous protocol for the Delivery Group. It is worth noting that the Citrix DaaS Windows 365 license assignment is per-user rather than per-machine. License Removal Flow The License Removal Flow begins when a Citrix administrator removes a license assignment from a user or group. As a result, each Cloud PC assigned to that user is removed from Citrix DaaS, and Citrix Cloud notifies the Windows 365 service of the license removal, triggering the uninstallation of the Citrix VDA. After the VDA is uninstalled, the Cloud PC can be accessed via the Windows 365 portal or the Windows App using RDP. Windows 365 Disconnect Flow Citrix administrators initiate the Windows 365 Disconnect Flow to completely disconnect their Citrix Cloud tenant from the Windows 365 service. This is only possible when all Citrix licenses have been unassigned from users or groups. When executed, Citrix Cloud notifies the Windows 365 service to offboard the customer, and once confirmed, Citrix Cloud will remove the connection information from the Citrix customer. Citrix Workspace URL Update Running every hour, this task monitors any changes to the Workspace URL used for Windows 365 Cloud PC access. This ensures that the Windows 365 portal has the correct URL for Citrix Workspace if users use this method to access their Cloud PC. Ghost Cloud PC Removal This daily task checks and verifies that each Cloud PC in each Cloud PC Machine Catalog still exists within the Windows 365 service and removes the Cloud PC from the catalog if it no longer exists. When machines are reprovisioned, a real-time cleanup of the old machine is performed before the new machine is created. Citrix Integration for Windows 365 AuthenticationThe Citrix Integration for Windows 365 supports Windows 365 deployments with Entra ID-joined and Entra Hybrid-joined Cloud PCs. For user authentication, this table provides details on the supported identity providers for both Entra ID join and Hybrid Entra ID join machine identity types. Machine Identity Entra ID Active Directory Active Directory + Token Okta SAML Citrix Gateway Adaptive Authentication Microsoft Entra join √ X X X √ X X Microsoft Entra Hybrid join √ √ √ √ √ √ √ √: Supported  X: Not Supported Microsoft Entra Join with Citrix WorkspaceWhen using Microsoft Entra join with the Citrix Integration for Windows 365, end users' Entra IDs are used for authentication into the Cloud PC.  In the diagram above, you see a typical Citrix Integration for a Windows 365 deployment with Microsoft Entra joined Cloud PCs. The user authentication flow to the Cloud PC is as follows: The end user opens the Citrix Workspace app or a web browser and navigates to their organization's Citrix Workspace URL. Citrix Workspace, configured to use Microsoft Entra ID as the identity provider, redirects the user's browser to the Microsoft login page (login.microsoftonline.com). The user enters their Entra ID username and password. Microsoft Entra ID processes the credentials, performs the authentication, and applies any relevant Conditional Access policies. Upon successful authentication, Entra ID issues an authentication token (e.g., SAML assertion or OpenID Connect ID token) back to Citrix Workspace. Citrix Workspace validates the received token and communicates with Citrix DaaS to enumerate the list of available Windows 365 Cloud PCs configured for the user. The user selects the desired Windows 365 Cloud PC from the Workspace interface. The Citrix HDX protocol initiates the connection to the selected Windows 365 Cloud PC. The user is presented with their Windows 365 Cloud PC desktop login screen. Microsoft Entra Hybrid Join with Citrix Workspace and Gateway ServiceWhen using the Microsoft Entra Hybrid join option to integrate Citrix with Windows 365, all identity providers supported in Citrix Workspace are available. In this case, authentication into the Cloud PC is performed using Active Directory credentials rather than Entra ID credentials. Although Cloud PCs are assigned to a user's Entra ID by default, the machines are assigned to the corresponding Active Directory user so that connections can be brokered for those users.     In this diagram, you see a typical Citrix Integration for a Windows 365 deployment with Microsoft Entra Hybrid joined Cloud PCs. As shown, Entra ID Connect is required to synchronize your Active Directory and Entra ID Directory domains, and it is essential for the hybrid join model. Additionally, a VPN or ExpressRoute is set up to provide connectivity between the Azure virtual networks (vNets) and the Citrix Cloud Resource location, allowing the Cloud PCs to join the Active Directory domain. A Citrix Cloud Connector™ is necessary for Citrix Cloud to connect to the Active Directory domain. The user authentication flow to the Cloud PCs is as follows: Microsoft Entra ID Connect synchronizes the on-premises Active Directory and Microsoft Entra ID tenant. The end user opens the Citrix Workspace app or a web browser and navigates to their organization's Citrix Workspace URL. In this case, Citrix Workspace is configured to use Active Directory as the identity provider, so users enter their Active Directory username and password. The credentials are securely transmitted via the established secure tunnel provided by the Cloud Connectors to the Active Directory domain controller to process the credentials and perform the authentication. Upon successful authentication, the successful result is sent back to Cloud Connector, which relays the result to Citrix Workspace. Citrix Workspace establishes the user's session and presents a list of available Windows 365 Cloud PCs configured for the user. The user selects the desired Windows 365 Cloud PC from the Workspace interface. The Citrix HDX protocol initiates the connection to the selected Windows 365 Cloud PC. The user is presented with their Windows 365 Cloud PC desktop via HDX, ready for use. Microsoft Entra Hybrid Join with Citrix Workspace (3rd Party IdP)For environments using an identity provider (IdP) other than Active Directory, the deployment described above still applies. However, in this case, the Citrix Federated Authentication Service (FAS) provides Single Sign-On (SSO) for sessions. In the scenario below, Okta is used as the primary IdP for Citrix Workspace. Okta's role in this scenario is to serve as the primary authentication provider for Citrix Workspace. When a user logs into Citrix Workspace, Okta verifies their credentials and issues a token to Citrix Workspace. However, Okta does not replace the underlying user directory that Windows 365 Cloud PC and its management use. The user account that the Cloud PC recognizes and the identity that FAS uses to issue a certificate for desktop logon still reside in on-premises Active Directory, which is then synced to Entra ID.  Microsoft Entra ID Connect synchronizes the on-premises Active Directory and Microsoft Entra ID tenant. The end user opens the Citrix Workspace app or a web browser and navigates to their organization's Citrix Workspace URL. Citrix Workspace is configured in Citrix Cloud to use Okta as its primary identity provider, and it redirects the user to the Okta login page. The user enters their Okta username and password. Okta processes the credentials, performs the authentication against its user store (which might be Okta's own directory, an integrated Active Directory, or another source), and applies any relevant Okta policies. Upon successful authentication, Okta issues an authentication token back to Citrix Workspace. Citrix Workspace validates the received token from Okta, establishes the user's session, and presents the list of available Windows 365 Cloud PCs that the user is entitled to access. The user selects the desired Windows 365 Cloud PC from the Workspace interface. The Citrix HDX protocol initiates the connection to the selected Windows 365 Cloud PC. In the background, Citrix Federated Authentication Service (FAS) leverages the user's successful authentication to Citrix Workspace to issue a short-lived, digitally signed certificate specifically for that user. The Citrix Virtual Delivery Agent (VDA) on the Hybrid Entra ID joined Windows 365 Cloud PC uses this certificate provided by FAS to perform a "certificate logon" to the on-premises Active Directory Domain Services (AD DS). This process automatically logs the user into their Cloud PC's Windows desktop session, eliminating the need for them to re-enter their credentials. Microsoft Entra Hybrid Join with Citrix StoreFront and NetScaler Gateway Microsoft Entra ID Connect synchronizes the on-premises Active Directory and Microsoft Entra ID tenant. The end user opens the Citrix Workspace app or a web browser and navigates to their organization’s NetScaler Gateway URL. The user connects to the NetScaler Gateway, which is configured to use Active Directory in this case as the identity provider, so users enter their Active Directory username and password. Upon successful authentication, the NetScaler Gateway establishes a secure SSL/TLS tunnel (often DTLS for ICA traffic) from the user's device into your network. This tunnel secures all subsequent communication. The NetScaler Gateway forwards the authenticated user's session information to the internal Citrix StoreFront server. This is often configured for pass-through authentication, meaning StoreFront recognizes the user as already authenticated by the Gateway and does not prompt for credentials again. Upon successful authentication, the successful result is sent back to Cloud Connector, which relays the result to Citrix DaaS. The Citrix DaaS control plane queries your on-premises Active Directory (via the Cloud Connectors) to determine the user's group memberships and entitlements. It then compiles a list of virtual applications and desktops that the user is authorized to access. This list is sent back to StoreFront (via Cloud Connectors). StoreFront then presents the users with the list of available resources, including Cloud PCs The user selects the desired Windows 365 Cloud PC from the Workspace interface. The Citrix HDX protocol initiates the connection to the selected Windows 365 Cloud PC. The user is presented with their Windows 365 Cloud PC desktop via HDX, ready for use. In the background, Citrix Federated Authentication Service (FAS) leverages the user's successful authentication to Citrix Workspace to issue a short-lived, digitally signed certificate specifically for that user. The Citrix Virtual Delivery Agent (VDA) on the Hybrid Entra ID joined Windows 365 Cloud PC uses this certificate provided by FAS to perform a "certificate logon" to the on-premises Active Directory Domain Services (AD DS). This process automatically logs the user into their Cloud PC's Windows desktop session, eliminating the need for them to re-enter their credentials. Monitoring Citrix + Windows 365When integrating Citrix with Windows 365, the user experience (UX) relies on several control planes and hops, including Microsoft Entra ID, Microsoft Intune, Citrix Gateway service, Citrix Workspace, and the endpoint. Comprehensive, correlated monitoring is therefore crucial to: Detect and triage issues in real time (enumeration, brokering, logon phases, session failures) and verify impact across users/sites. Citrix DaaS Monitor offers a live troubleshooting dashboard for DaaS workloads, featuring failure surfacing and site health panels. Quantify UX and identify the root cause (such as network latency, policy issues, codec choice, or bandwidth constraints) at the HDX session layer; Installing Citrix uberAgent on the Windows 365 Cloud PC provides protocol-level and session configuration metrics that explain why a session feels fast or slow. Close visibility gaps caused by spanning Microsoft’s Cloud PC fabric and Citrix services; The Citrix Integration for Windows 365 connects Citrix Cloud with Windows 365 to implement HDX technologies and policies—observability must cover both sides. What To MonitorArea Description Why It Matters Primary Source Site health &amp; failures (enumeration, brokering, launch) Watch the live failures panel and site/region health. Pivot from spikes to impacted users; drill into failure reasons (auth, brokering, STA/gateway). Set alerts on sudden changes. Catches widespread issues fast; validates the end-to-end launch flow for Cloud PCs via HDX. Citrix DaaS Monitor Logon duration breakdown (GPO, profile, shell) Use the phase breakdown to see where time is spent (e.g., GPO processing, profile attach, shell init). Compare outliers to baselines and correlate them with profile/storage health. Speeds root cause for slow logon complaints Citrix DaaS Monitor Session performance (ICA/HDX RTT, jitter, bandwidth) Track HDX RTT, network jitter, and throughput per session. Overlay with user reports and time of day; flag links or sites with unstable latency. Differentiates network vs host bottlenecks Citrix uberAgent HDX policy &amp; codec settings (EDT/Thinwire, video, QoS Capture active HDX policy/codec per session and compare to your standards. Watch for unintended policy drift after changes. Confirms runtime behavior matches design (e.g., EDT enabled, proper codec for task/knowledge worker). Citrix uberAgent Gateway path health (Citrix Gateway / CGS) Correlate gateway VIP health, auth latency, and throughput with session UX. Identify overloaded gateways or GEO issues. Ties the access tier to user experience and explains intermittent launch/performance issues. Citrix uberAgent Teams &amp; collaboration experience Track call join times, jitter, packet loss, device redirection state, and optimization mode. Compare optimized vs. fallback sessions. Collaboration quality is a top UX driver for hybrid workers. Citrix uberAgent Cloud PC resource saturation (CPU, RAM, disk, GPU) Trend per-session and per-SKU utilization; watch for CPU-ready, RAM pressure, disk queue spikes, and GPU headroom (if present) Right-sizes Windows 365 SKUs and prevents chronic contention. Citrix uberAgent App performance &amp; hangs Monitor app start times, responsiveness, crashes, and slow I/O paths. Tie app events to the affected sessions/users. Moves troubleshooting from “the desktop is slow” to the exact app/process. Citrix DaaS Monitor, Citrix uberAgent Usage &amp; capacity trends (concurrency, duration) Review daily/weekly concurrency, session duration, and license consumption. Identify peak windows and idle time. Informs scaling policies and cost optimization for Cloud PCs + HDX. Citrix DaaS Monitor, Citrix uberAgent ReferencesTech Brief: Citrix for Windows 365 POC Guide: Citrix for Windows 365 POC Guide:  Integrating Windows 365 with Citrix Session Recording Microsoft Windows 365 Architecture]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2025_10/image.png.8ad82a77038e6c3bb0997ad515c010a4.png" length="75460" type="image/png"/><pubDate>Fri, 31 Oct 2025 04:04:00 +0000</pubDate></item><item><title>Tech Brief: Citrix Secure Access with Chrome Enterprise</title><link>https://community.stage.citrix.com/tech-zone/learn/tech-briefs/csa-ce/</link><description><![CDATA[Overview Citrix Secure Private Access (SPA) offers a powerful and adaptive Zero Trust Network Access (ZTNA) solution for SaaS and private applications. Building on this foundation, organizations can integrate with Chrome Enterprise to create Citrix Secure Access with Chrome Enterprise (CSA/CE). This expanded architecture delivers comprehensive security services—like Advanced Threat Protection (ATP) and Data Loss Prevention (DLP)—directly in the browser, enabling a wider range of critical enterprise use cases.   This integrated solution addresses several key enterprise initiatives and challenges:  Addressing the "Last Mile" Security Gap: Extends consolidated security policies directly to the endpoint browser, complementing cloud-delivered SSE services by providing granular control and visibility for SaaS, web, and private on-premises applications that may otherwise introduce access complexities or latency.  VDI Cost Optimization: Reduces reliance on expensive Virtual Desktop Infrastructure (VDI) for delivering web and SaaS applications, leading to significant infrastructure and operational cost savings.  VPN Replacement: Provides a modern, application-centric alternative to traditional network-level VPNs, enhancing security, user experience, and management simplicity.  Secure Third-Party Access: Enables highly secure, granular access for contractors, temporary workers, and partners to specific applications without granting broad network access.  Secure Remote and Hybrid Workforce: Facilitates seamless and secure access for a distributed workforce, ensuring productivity and compliance from any location.  By delivering Zero Trust access to private, web, and SaaS applications at the application layer via the familiar Chrome browser, the combined power of Citrix Secure Access and Chrome Enterprise offers a tailored, comprehensive solution for these use cases.    This architecture is engineered to deliver advanced threat protection against malware and phishing, and enforce Data Loss Prevention (DLP) policies for confidential data. Furthermore, Chrome Enterprise provides IT with a robust set of browser-level security and management controls that work seamlessly with Citrix Secure Access, enabling employees to securely access IT sanctioned applications, while maintaining granular control and optimized performance for direct access to internal applications.  Watch this video to learn more: Conceptual Architecture The high-level conceptual architecture for securely delivering applications using Citrix Secure Access with Chrome Enterprise supports both corporate-managed and unmanaged (BYOD) endpoints. While the core security framework remains consistent across device types—leveraging Zero Trust principles, adaptive access controls, and centralized policy enforcement—the method of endpoint integration and the scope of accessible resources differ.  Subject: The foundation of the zero trust model begins with the subject. Who or what is trying to access resources. The user: identity seeking access The device: the endpoint being used to access a resource. For managed devices, Citrix Secure Access leverages a local agent that provides deep OS integration. This enables access to both web applications and TCP/UDP-based private applications, along with enhanced posture checks and continuous monitoring. For unmanaged devices, the solution uses a browser extension to deliver lightweight, privacy-respecting access. While limited to web and SaaS applications, it still enforces robust security controls through Chrome Enterprise Premium policies. The browser: the interface being used to access a resource Zero Trust Decision Point: When the user tries to access Citrix Workspace, the zero trust decision point (Policy service engine), needs to determine if they get access. Its first job is to determine the full context of the request by querying the Information Points Zero Trust Information Points: The decision to grant, or deny access leverages the information gathered by the corresponding services Device Posture: Compliance status of the device. The endpoint protection agent scans the endpoint verifying if it adheres to requirements for the operating system, antivirus, firewall, certificates, etc. Adaptive Authentication: Utilizes a customer provided Identity Provider (IdP) to determine if the user's identity is valid Network Location: Allows you to define your network topology to better determine if the user is external or internal (and which physical location). Geolocation: Leverages DNS to determine broad geo location for the user. Zero Trust Enforcement Points: These are strategic control locations within the solution where access decisions are enforced based on contextual policies. Once the Policy Engine evaluates the context—such as user identity, device posture, and location—the enforcement point carries out the decision, allowing or denying the connection accordingly. Browser Security Controls: The secure browser acts as a Zero Trust enforcement point by applying granular session-level controls. Leveraging Chrome Enterprise Premium policies, it enforces security measures such as blocking copy/paste, downloads, or printing, ensuring sensitive data remains protected even on unmanaged or BYO devices. If you are applying DLP Rules to inspect content, the browser sends the content to Chrome Enterprise for machine learning inspection. Browser URL Filtering: When users attempt to access public websites, the browser acts as a Zero Trust enforcement point by applying URL filtering and session controls. Local browser policies restrict access to risky or non-compliant sites, while Chrome Enterprise Premium enhances enforcement with advanced controls—such as blocking access to specific categories, enforcing safe browsing, and applying data protection policies in real time. Google Security Gateway: The Security Gateway acts as a Zero Trust enforcement point when users access SaaS applications. Operating as a forward proxy, it does not inspect traffic but enforces access control by routing requests through customer-specific egress IP addresses. These unique IPs can be allowlisted by SaaS providers, ensuring that only authorized traffic reaches the application. Secure Private Access Service: For private resources, Secure Private Access establishes a micro-tunnel within the on-prem connector’s outbound tunnel. The micro-tunnel limits which resource can be accessed (North-South connection). Because BYO devices leverage a browser plugin, access to private resources is restricted to authorized web-based apps. Managed devices that leverage the local agent are able to access authorized web apps as well as TCP/UDP apps. In addition, SaaS app access can also be tunneled through the local connector. This approach allows organizations to perform traffic inspection with their on-prem security controls. Deployment Models The Deployment Models for Citrix Secure Access with Chrome Enterprise include Cloud-delivered and Hybrid-delivered options. These delivery models allow you to align the solution with your organizations' existing infrastructure, security requirements, and operational preferences, ensuring seamless integration and optimal performance.  Cloud-Delivered Model The Cloud-delivered model represents a modern, agile approach to Zero Trust Network Access, leveraging the full power of cloud-native services for simplified deployment, enhanced scalability, and reduced operational overhead. In this architecture, the majority of the Citrix Secure Private Access components and Chrome Enterprise Premium management reside in the cloud, providing a streamlined and globally accessible solution.   Architecture Overview:  Cloud-Native Infrastructure: All core Citrix Secure Private Access components, including the ZTNA service, policy engine, and authentication services, are hosted and managed within Citrix's cloud infrastructure.  Identity Provider Integration: Users authenticate against their existing Identity Provider (IdP), which can be cloud-based (e.g., Entra ID, Okta, Google Workspace Identity) or on-premises (e.g., Active Directory), and which integrates directly with the Citrix cloud service.  Chrome Enterprise Premium Cloud Management: Chrome browsers on endpoints are centrally managed through the Google Admin console in the cloud. This allows for seamless policy distribution, extension management, and security updates without requiring on-premises infrastructure.  Direct-to-Cloud Access: SaaS applications and public web resources are accessed directly through the Chrome browser, with security policies and DLP enforced at the browser edge.  Private App Connectors: For on-premises private applications, lightweight connectors are deployed within the corporate network. These connectors establish secure, outbound-only connections to the Citrix cloud service, enabling Zero Trust access to internal resources without opening inbound firewall ports.  Scalability and Resilience: The cloud-native architecture inherently provides high availability, global reach, and elastic scalability, adapting to fluctuating user demands and geographic distribution.  Benefits:  Simplified Deployment: Minimal on-premises infrastructure requirements, accelerating time to value.  Global Accessibility: Consistent security and performance for a distributed workforce, regardless of location.  Reduced TCO: Eliminates the need for significant hardware investments and ongoing maintenance of on-premises components.  Automatic Updates: Cloud services are continuously updated, ensuring access to the latest features and security patches.  Hybrid-Delivered Model The Hybrid-delivered model offers organizations the flexibility to integrate existing on-premises infrastructure with cloud-delivered Zero Trust capabilities. This approach is particularly beneficial for enterprises with significant investments in on-premises identity management, legacy applications, or specific compliance requirements that necessitate keeping certain components within the data centers.   Architecture Overview:  Cloud-Managed ZTNA with On-Prem Components: While the core ZTNA policy enforcement and Chrome Enterprise management remain cloud-based, this model incorporates specific on-premises components of Secure Private Access to extend granular control and integrate with existing infrastructure.  On-Premises Identity Provider Integration: Organizations can leverage their existing on-premises Identity Providers (e.g., Active Directory Federation Services (ADFS), traditional Active Directory) for user authentication, with Citrix Gateway acting as a secure proxy or IdP for these on-premises directories.  NetScaler Gateway (On-Prem): An on-premises NetScaler Gateway instance can be utilized for initial authentication, advanced policy evaluation, and integration with on-premises identity stores or multi-factor authentication solutions. It can also serve as a traditional VPN for specific legacy use cases while the ZTNA model handles modern application access.  Citrix StoreFront (On-Prem): For seamless user experience and aggregation of both cloud-delivered and on-premises applications, an on-premises Citrix StoreFront can be deployed. This acts as a unified portal where users can discover and launch all their sanctioned applications, regardless of their hosting location.  Secure Private Access Connectors: Similar to the cloud model, lightweight connectors are deployed on-premises to establish secure, outbound connections to the Citrix cloud service for ZTNA access to private applications.  Chrome Enterprise Premium Cloud Management: Chrome browsers are still managed centrally via the Google Admin console in the cloud, ensuring consistent browser security and policy enforcement across all endpoints.  Benefits:  Leverage Existing Investments: Maximizes the value of current on-premises infrastructure and identity solutions.  Phased Migration: Allows for a gradual transition to a full cloud-delivered model, reducing disruption.  Compliance Flexibility: Addresses specific regulatory or data residency requirements by keeping certain components on-premises.  Complex Environment Support: Ideal for organizations with a mix of modern cloud applications and traditional on-premises client/server applications requiring integrated access.  Adaptive Security The combined solution of Citrix Secure Access and Chrome Enterprise Premium provides a powerful adaptive security framework designed to protect corporate resources based on real-time context. This goes beyond static access rules, enabling dynamic policy enforcement that responds to changes in user, device, and environmental conditions.  Adaptive Authentication Adaptive authentication determines the right authentication flow for the current request. It can identify the device posture, geographical location, network segment, and user organization/department membership. Based on the information obtained, an administrator can define how they want to authenticate users to their IT sanctioned apps. This allows organizations to implement the same authentication policy framework across every resource including public SaaS apps, private web apps, and Desktops as a Service (DaaS).  Adaptive Authentication policies can easily incorporate business logic to create a stronger authentication solution. This could include scenarios like:  Internal vs External: Users who connect from internal locations from managed devices could authenticate with a username and password. External users on BYO devices would require multi-factor authentication.  Employee vs Contractor vs Partner: Organizations will often require different authentication flows based on the user identity. If an employee, authenticate against Active Directory. If the user is a contractor, utilize a personal Gmail account. If the user is a partner, utilize a managed Azure Active Directory account.  Mergers and Acquisitions: Adaptive Authentication can help organizations overcome the authentication challenges associated with a merger or acquisition. Adaptive Authentication lets users authenticate against their pre-merger identity provider. The pre-merger identity can then be used to provide ZTNA access to authorized resources from either organization.  Watch this video to learn more:   Context-Aware Policy Engine:  Policies are not just based on user identity, but also incorporate a rich set of contextual signals. With adaptive access, organizations can provide users with different levels of access to SaaS, private web, and private client/server apps.  When a user tries to access an authorized resource, adaptive access determines how the current request aligns with the defined conditions. The conditions can include a combination of:  User or group membership  Device type  GEO Location  Network Location  Device posture  Workspace URL   Based on the results of these conditions, access to the resource is either allowed or denied.   Real-time Threat Detection &amp; Remediation Leveraging the advanced threat protection capabilities, the Citrix Secure Access with Chrome Enterprise can detect and mitigate threats in real-time, providing a robust first line of defense directly at the browser level. The solution's deep integration with Google's extensive threat intelligence and security infrastructure is fundamental to these capabilities, ensuring proactive defense against evolving cyber threats.  Key aspects of this real-time protection include:  Google Safe Browsing: This is a primary defense wall, using Google's continuously updated lists of known malware, phishing sites, and unwanted software. If a user attempts to navigate to a dangerous site or a download originates from a known malicious source, Chrome will proactively block it or display a warning.  Real-time File Scanning: For downloaded files, Chrome Enterprise Premium performs real-time scanning using Google's cloud-based analysis. This includes advanced AI and machine learning techniques to detect and block unknown or zero-day malware before it can reach the endpoint device.  Proactive Phishing Protection: Beyond Safe Browsing lists, Chrome Enterprise utilizes Google's broader threat intelligence to identify and block sophisticated phishing attempts, safeguarding user credentials and sensitive information in real-time.  Harmful Site Isolation: Potentially dangerous or compromised websites are isolated within the browser's sandboxed environment, preventing them from interacting with other browser tabs or the underlying operating system, thus containing any potential threats.  Integration with Threat Intelligence: Chrome Enterprise continuously integrates with Google's vast threat intelligence feeds, providing an up-to-the-minute view of global threats and enabling proactive policy adjustments directly within the browser.  Automated Remediation: Upon detection of a threat or policy violation, Chrome Enterprise can initiate automated actions, such as blocking access to a malicious site, preventing a download, or triggering alerts for administrators.  Forensic Logging and Reporting: Chrome Enterprise provides detailed logs of security events and threat detections, offering valuable data for security investigations, compliance auditing, and continuous improvement of security policies.  This comprehensive approach ensures that web-based threats are identified and addressed immediately at the browser level, significantly reducing the risk of data breaches and system compromises.  Application Access Citrix Secure Access with Chrome Enterprise fundamentally transforms how users access applications, moving from a traditional network-centric model to a highly secure and efficient application-centric approach. This enhances security, simplifies the user experience, and optimizes performance across the entire application portfolio.  Unified Access to All Application Types: The solution provides a single, consistent access point for a diverse application portfolio, including SaaS applications, internal web applications, and even traditional client-server applications. Users can access all necessary tools directly through their managed Chrome Enterprise Premium browser or, for specific use cases, via the Citrix Workspace app, depending on the application type and defined security policies.  Zero Trust Network Access (ZTNA) for Private Applications: For private applications, ZTNA ensures that micro-segmented tunnels are established only for the specific application being accessed. This eliminates broad network access, significantly reducing the attack surface and preventing lateral movement within the network, even if an endpoint is compromised. This granular approach is a cornerstone of Zero Trust security.    Optimized Performance for SaaS and Web: By intelligently routing traffic and applying security policies at the browser edge, the solution ensures low-latency and high-performance access to SaaS and public web applications. This direct, secure access leads to a superior user experience by minimizing the need to backhaul traffic through a corporate data center or through forward security proxies, common with Security Service Edge (SSE) type solutions.    Enterprise Browser Chrome Enterprise Premium serves as the cornerstone of the endpoint security and access strategy, providing a highly secure, manageable, and user-friendly browser environment that is integral to the Zero Trust architecture.    Centralized Management and Policy Enforcement: IT administrators gain comprehensive control over the browser environment through Google's Chrome Enterprise administration console. This powerful platform allows for the centralized deployment and enforcement of security policies, granular browser configurations, efficient extension management, and scheduled updates across the entire fleet of managed devices. This ensures consistency and simplifies administration.  Built-in security features: Chrome Enterprise Premium leverages Chrome's robust, built-in security features, providing a strong foundation for endpoint protection:  Google safe browsing: Actively protects users from phishing attempts, malware, and dangerous websites in real-time by warning them about suspicious sites.  Browser isolation and sandboxing: Isolates web processes to prevent malicious code from impacting the underlying operating system or other applications, containing potential threats.  Site isolation: Provides an additional, critical layer of security by isolating websites from each other, preventing cross-site scripting (XSS) and other web-based attacks from compromising data across different sites.  Automatic updates: Ensures that browsers are always running the latest security patches and feature enhancements, minimizing vulnerability windows and keeping the environment protected against emerging threats.  User familiarity and productivity: By utilizing the widely adopted Chrome browser, the solution minimizes the learning curve for end-users. This familiarity promotes rapid adoption and maintains high levels of productivity, all while ensuring enterprise-grade security is seamlessly integrated into their daily workflow.  Data Loss PreventionData Loss Prevention (DLP) is a critical component of the Citrix Secure Access with Chrome Enterprise solution, designed to prevent sensitive information from leaving the organization's control. The solution implements multi-layered DLP capabilities across various points of interaction.  The benefit of data protection at the browser level, particularly with Chrome Enterprise, lies in its ability to enforce highly granular Data Loss Prevention (DLP) controls directly at the point of user interaction with an application or website.  For example, many SSE solutions excel at granular DLP for a limited number of popular SaaS applications where they have built deep integrations. However, for the vast majority of other SaaS applications, and certainly for general web sites or private web applications, their DLP capabilities are often restricted to simply preventing or allowing full file uploads and downloads. Organizations frequently utilize a much larger portfolio of SaaS applications than any single SSE solution can provide deep, application-specific DLP for.  By contrast, Chrome Enterprise, when integrated with Citrix Secure Private Access, allows for application-aware DLP directly at the browser. This means it can understand and control data interactions within the application itself, such as:    Preventing copying and pasting of sensitive text from a web form field, a document viewer, or a specific element on a private application page.  Disabling printing of content from sensitive web pages or within a SaaS application.  Blocking screen captures of confidential information displayed in the browser.  This provides a critical layer of contextual DLP that complements network-edge security, extending robust data protection across a far broader range of SaaS, web, and private applications.  The multi-layered DLP capabilities include:  Endpoint DLP: Policies are enforced directly on the endpoint device to prevent unauthorized data exfiltration originating from the device itself. This includes crucial protections against malicious software such as keyloggers, which attempt to capture sensitive keystrokes, and screen capture malware, which tries to steal visual data from the screen. By operating at this foundational level, Endpoint DLP ensures that data remains secure even before it interacts with specific applications or browsers.    Browser DLP: Policies are applied directly within the browser to control data movement between applications. This allows administrators to prevent sensitive data from being copied or pasted between specific applications, such as from a corporate web app to a personal web app or a desktop app like Notepad.     Watermarking: Dynamically overlays a watermark on the application screen, displaying user and session information. This acts as a visual deterrent against unauthorized sharing and provides traceability if data is exfiltrated via screen capture.  Visual DLP: Within application sessions, particularly for private web and SaaS applications, visual DLP controls are enforced. Visual DLP automatically masks or redacts sensitive data fields within the application interface itself, ensuring that confidential information is not displayed or accessible to unauthorized individuals, even if they have legitimate access to other parts of the application.     Deep Inspection DLP: For files, images, and content transmitted through Citrix Secure Access, deep content inspection is performed by Google's advanced scanning capabilities. This allows the solution to identify and block sensitive data patterns (e.g., credit card numbers, social security numbers, intellectual property) embedded within various file types, images, and fields, regardless of the application or protocol being used. This provides an additional, powerful layer of protection at the network edge, ensuring comprehensive data security for both structured and unstructured data.]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2025_09/ConceptualArchitecture-CSAwithCE-CloudDelivered.jpg.aae8568b1e315ca0981afe54a0d5b70d.jpg" length="66640" type="image/jpeg"/><pubDate>Wed, 17 Sep 2025 17:01:00 +0000</pubDate></item><item><title>Citrix Secure Access with Chrome Enterprise</title><link>https://community.stage.citrix.com/tech-zone/learn/poc-guides/csa-ce/</link><description><![CDATA[Overview The Citrix Secure Access (CSA) with Chrome Enterprise (CE) solution enables secure, adaptive, and high-performance Zero Trust Network Access (ZTNA) to all applications from Chrome Enterprise-managed browsers. Designed as a cutting-edge, cloud-delivered platform, it leverages the robust security and management capabilities of Chrome Enterprise directly at the endpoint, providing a comprehensive solution for modern application access. By leveraging Citrix Secure Private Access, it adheres strictly to Zero Trust principles, ensuring that access is continuously verified based on user identity, device posture, and application context, rather than implicit trust.    This deployment guide provides you with the essential steps to install and configure Citrix Secure Private Access in conjunction with Chrome Enterprise. It focuses on establishing a foundation for secure application delivery, allowing you to leverage your existing knowledge of Citrix policy and access management in this modern context.  The following key steps are covered in this guide:  Prepare the Citrix Cloud Environment for Secure Private Access  Configure Chrome Enterprise for managed browser and policy integration  Deploy and Configure Citrix Secure Access connectors for private application access  Define and Apply Adaptive Access Policies for SaaS, private web, and private TCP/UDP applications  Enable User Access and validate the secure application delivery flow from Chrome Enterprise endpoints   For detailed information regarding advanced Chrome Enterprise policies (beyond basic integration), specific application-level security controls, or in-depth troubleshooting, please refer to the respective Citrix Secure Private Access product documentation and Google Chrome Enterprise documentation.  Secure Access Delivery Options Modern secure access solutions offer flexibility in deployment, catering to various organizational needs and infrastructure setups. The two primary models are cloud-native and hybrid.   This proof-of-concept (PoC) guide will focus exclusively on the steps for a cloud-native deployment, as it represents a streamlined and increasingly popular approach for modern secure access delivery.   Prerequisites Chrome Enterprise Premium subscription Citrix Workspace subscription configured with an identity provider (IdP)  Citrix Secure Private Access subscription  Admin access to Google Workspace Admin Console Google Customer ID  Create a Resource Location A Resource Location is a logically defined grouping within your network (on-premises or cloud) where your private applications and resources reside. It is connected to the Citrix Cloud service via a connector appliance for Zero Trust access. It serves as the bridge between your private app infrastructure and the cloud-delivered access control plane.  Within Citrix Cloud, select the menu button in the upper left hand corner and launch Resource Locations  If no resource locations exist, go ahead and create one by selecting “+ Resource Locations”  Once a Resource Location has been defined, add a Connector Appliance  Select your preferred hypervisor and download the corresponding file  Once downloaded, import it into your hypervisor. Once the appliance starts, you will see the access IP Address to complete configuration of the appliance.    The first time you connect to the appliance, you have to configure the logon password. Once complete, you will see the appliance dashboard.    If you want to reconfigure the network settings, do it now by selecting the Edit Network Settings button. Once updated, the appliance will automatically reboot.  Register the connector by selecting the appropriate button.  Give the appliance a name  When you get the registration code, copy the value    Go back to the Citrix Cloud web page and paste this code into the Add a Connector Appliance dialog box    Once the details have been confirmed, you can register the appliance. Once complete, you will see the new connector appliance in your resource location  Note: If this is the first appliance, you will receive a warning message indicating that you should have at least two connectors per resource location to provide high availability. As this is just a test environment, you can ignore it.   Onboarding WorkflowOnce your Resource Locations are established, the next step is to define the specific applications that your users will access through Citrix Secure Access. An app, in this context, refers to a discreet service or resource (such as a SaaS application, an internal web application, or a private TCP/UDP application) that requires secure, policy-driven access. Each defined app represents an entry point for secure access, allowing the Citrix Cloud service to apply granular Zero Trust policies and deliver the application securely to the user's endpoint.    In this example, an internal web application will get added.   Within Citrix Cloud, select the menu button in the upper left hand corner and launch Secure Private Access  If this is your first time building Secure Private Access, you will be guided through an initial setup flow. Select Fully Cloud-delivered Service architecture   Google Chrome IntegrationTo integrate with Google Chrome, copy the Citrix service account address and select the Google Workspace admin roles link.    In the Google Chrome – Admin Roles console, select Create New Role  Select Create new role and give it a name like Chrome Enterprise Premium Admin   Add the following privileges to this role.   When the privileges are added, select Continue and Create Role  With the role created, select Assign Service Accounts  Paste the Citrix Service Account Address you copied earlier. Select Add and Assign Role  Back in the Citrix console, use the workflow and link to find and add your Google customer ID  With the information provided, select "Run CEP Verification”. Select Next. If verification was not successful, verify the privileges are assigned correctly.  Device PostureYou can obtain device posture status from third party services, but in this example, simply skip this option  Citrix also provides a device posture service integrated with Secure Private Access. For this example, skip this process   Add a Private Web App The next part of the process is to define an application. Select Add an app   The first part of your application definition is to define the initial details for the app. In this example, the app is  Inside your corporate network  App Type: HTTP/HTTPS  App Name: This is the name users will see  App Icon: This will be the icon users will see  The second part of the application definition is to specify the URL for the app.   Agentless Access: Disabled. Citrix Secure Private Access provides the ability to access private web apps without requiring an agent. However, this configuration is out-of-scope for this guide.  URL: Provide the internal HTTPS address for the private web application  Routing Type: Select Internal via Connector. This routes the connection through the resource location and connector created earlier.   Resource Location: Select the resource location created earlier.   A related domain entry was created. Edit this to select the correct resource location   Select Finish to end the Add an app workflow  You now have a list of applications you are creating with the initial workflow. Select Next to define the access policies  Define Adaptive Access PolicyBuilding upon the foundation of defined Resource Locations and Applications, the next critical phase in securing your digital workspace involves Creating Adaptive Access Policies. These policies are the intelligence layer of Citrix Secure Private Access, allowing you to move beyond static, binary access decisions to a dynamic, context-aware security model. By leveraging real-time information about the user, their device posture, location, and the specific application being accessed, Adaptive Access Policies enable granular control and significantly enhance your Zero Trust security posture.   This section will guide you through the process of designing and implementing these powerful policies, ensuring that the right users receive the right level of access to the right applications, under the right conditions.  Note You would typically have one Policy per application (or group of similar applications), and that Policy would contain multiple Rules to handle different scenarios for accessing that specific app.     Continuing from the initial app setup workflow, select Create policy to start defining your access policy    Policy Definition  Give the policy a name. This is the overarching name for a set of conditions that determine access to a specific application or group of applications  Select the applications associated with this policy. So far, you should only have one app  Inside each Policy, you define one or more Rules. Each rule specifies a particular set of conditions (e.g., device posture, user group, network location) and the actions to take (e.g., allow access, deny access, apply watermarking, force browser isolation) if those conditions are met. Rules are evaluated in order of precedence. Go ahead and Create a Rule  Provide a name for the rule. A good rule name should quickly convey who it applies to, what conditions trigger it, and what action or outcome it produces    For conditions of the policy, this will be a very simple policy applied to all domain users    The action is to allow access to the app    Once defined, complete the workflow by selecting Finish Select Save to complete the policy workflow  This is the final step for the initial app creation wizard. Go ahead and complete the workflow  Verify Google Chrome OnboardingOnce the workflow is complete, backend processes will integrate your Google Chrome tenant with Citrix Services. To monitor the status of the onboarding process, in the Citrix Cloud console, navigate to: Secure Private Access – Browser Settings – Browser.    Do not continue until integration is complete.  Create Google Chrome Users There are a few ways to define users in Google's directory, and the best method depends on the size of your organization and whether you have an existing directory like Active Directory. For Chrome Enterprise Premium, users must exist in Cloud Identity or a Google Workspace account.  Manual User CreationFor small organizations or for a few specific accounts, you can create users one-by-one directly in the Google Admin console.  Sign in to your Google Admin console with a super administrator account.  Navigate to Directory &gt; Users.  Click the "Add new user" button.  Fill in the user's details, including first name, last name, and primary email address.  Set a temporary password and choose whether to force the user to change it on their first login.  Bulk User Creation with a CSV If you have a larger number of users but don't use an on-premises directory, you can create them in bulk by uploading a CSV file.  From the Users page in the Admin console, click the "Bulk update users" icon.  Download the provided CSV template.  Fill in the template with your users' information (name, email, password, etc.).  Upload the completed CSV file to create all the user accounts at once.  Automated Sync with Google Cloud Directory Sync (GCDS) This is the most common and recommended method for organizations with an existing on-premises LDAP directory, such as Microsoft Active Directory. GCDS is a one-way synchronization tool that keeps your Google directory up to date.  Install GCDS: Install the GCDS application on a server in your environment.  Configure the Connection: Use the GCDS Configuration Manager to connect to your LDAP server and your Google account.  Create Sync Rules: Define which users, groups, and organizational units (OUs) you want to sync from your LDAP server to Google using LDAP queries. You can also set exclusion rules to prevent certain accounts from syncing.  Simulate and Sync: Run a simulation to preview the changes. Once you are satisfied, run a full sync to apply the changes to your Google directory.  Schedule Syncs: Set up a scheduled task to run GCDS regularly, ensuring your Google directory stays synchronized.  Use the Google Cloud Directory Sync guide to provide details on how to setup and configure Third-Party Identity Provider (IdP) Provisioning If you use a cloud-based IdP like Microsoft Entra ID (formerly Azure AD), you can set up automatic provisioning using the SCIM protocol. This allows user and group changes made in your IdP to automatically sync to your Google directory, streamlining the user lifecycle management process.  Configure Chrome Policies Policies control how your users interact with Chrome. By configuring these policies, you can enforce a wide range of settings—from managing privacy and security features to controlling app and extension behavior—to ensure a consistent and secure Browse environment.  In the Google Admin console (admin.google.com), navigate to Chrome Browser - Settings  Search for the setting “Custom theme color”  Select the setting  Modify the Hex Color to be #003893. This will change the browser’s color when it is being managed by the policies.   Save the configuration. Validate Chrome Configuration Start Chrome and Sign in to Chrome   Once you successfully authenticate to Chrome, the browser’s color should change. You will also notice that three Chrome browser extensions were also automatically added. These extensions are used to provide agentless access to your private apps.  Validate Private App AccessWith Chrome settings verified, the final step is private app access validation. This ensures that your newly defined application is accessible as intended, and that your granular policies are being enforced correctly.   Within your managed Chrome browser profile, try to access the private web app you defined earlier. If this doesn't succeed, verify the app is configured correctly, the cloud connector appliance is functional, that the cloud connector can communicate with the web app, and that the web app is online. ,  Once you connect to the private web app, verify session details within the Citrix Cloud admin console. On your admin desktop, log into the Citrix Cloud console.    Select Monitor from the menu bar in the top left corner.  You should see an active session within the Web/SaaS/TCP/UDP-Active Sessions graph. Select the number to get more details  Select the active session from the list  To get more details, select the Resource Name link from the session   This dashboard provides extensive information about the current session, including topology, app, policy, and more.     Summary This deployment guide walked you through the end-to-end process of setting up secure access to private web applications using Citrix Secure Access with Chrome Enterprise. By following these steps, you have successfully:  Established a secure foundation by creating a resource location and defining your private web applications.  Implemented a robust security model by configuring adaptive access policies to control who can access your applications and under what conditions.  Provisioned user accounts in your Google directory, ensuring a seamless user experience.  Enforced a secure browser environment by configuring policies across your organization.  With this setup complete, you have a powerful and secure framework for delivering applications to your users, fully leveraging the Zero Trust capabilities of Citrix Secure Access with Chrome Enterprise.]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2025_09/PoCGuide-CSAwithCE-CloudDeliveredcopy.jpg.61249a4fb1c079227edc906030a304f8.jpg" length="66640" type="image/jpeg"/><pubDate>Wed, 17 Sep 2025 16:29:00 +0000</pubDate></item><item><title>Integrating Scout Board with Okta SAML 2.0 for Single Sign-On</title><link>https://community.stage.citrix.com/tech-zone/build/deployment-guides/unicon-scout-sso/</link><description><![CDATA[OverviewThis guide describes how to integrate Scout Board with Okta using the SAML 2.0 protocol to enable Single Sign-On (SSO) for end users. By leveraging Okta’s identity platform, organizations can centralize authentication, improve security, and streamline the login experience for Scout Board users.  PrerequisitesBefore starting, ensure the following requirements are met: Scout Board 15 2507 or above. Administrative access to both the Okta Console and the Scout Board server. An SSL/TLS certificate (public/private key pair) for signing SAML requests. A designated Scout Board URL that is accessible by Administrators. This article guides on configuring the integration when installing Scout Board or when switching from another authentication method to Okta SAML 2.0 during an existing installation. Note: For testing purposes, you can create a self-signed certificate for signing the SAML requests using OpenSSL using the following command: openssl req -x509 -newkey rsa:4096 -keyout private_key.pem -out public_cert.pem -days 365 -nodes –subj "/C=US/ST=California/L=San Francisco/O=MyOrg/CN=my-test-domain.com" Create the Okta SAML IntegrationFrom the Okta Admin Console, create a new App integration: Navigate to Applications → Create App Integration. Select Sign-in method: SAML 2.0 Provide an App Name (i.e. Scout Board SAML app)  Configure Okta SAML settingsConfigure the SAMLsettings as described below, replacing &lt;HOSTNAME&gt; with the Scout Board URL.  If your Scout Board uses a different port than 22160, update the setting as needed. General Settings:Attribute Value Single sign-on URL https://&lt;HOSTNAME&gt;:22160/api/v1/auth/login-saml/callback Audience URI (SP Entity ID) https://scoutboard.com/47d899fb-3002-4692-b741-8068b8f7fbb4 Name ID format Email Application username Email  Advanced Settings:Attribute Value Enable Single Logout Yes Signature Certificate Upload the public key certificate to verify the signature Single Logout URL https://&lt;HOSTNAME&gt;:22160/api/v1/auth/logout-saml/callback SP Issuer https://scoutboard.com/47d899fb-3002-4692-b741-8068b8f7fbb4 *** Only replace if you are not using the Scout Board issuer Signed Requests Yes  Attribute Statements:Name Name Format Value firstName Basic user.firstName lastName Basic user.lastName email Basic user.email  Group Attribute Statement:Name Name Format Filter Value Groups Basic Matches regex .*  Note: Use the filter expression required by your environment. The .* sends all group memberships. After completing the configuration, download the SAML Signing Certificate (okta.cert).  Assign Users and GroupsUnder the Assignments tab in Okta, add the users or groups that should have access to Scout Board.  Retrieve required Okta InformationUsing the newly created Okta SAML integration, retrieve the values for the following parameters: ●        Sign On URL ●        Single Logout URL ●        SAML Signing Certificate (okta.cert) At this point, ensure that you already have the private key associated with the signing certificate.  Configure Scout Board for Okta SAMLOkta SAML authentication for Scout Board can be enabled either during the initial installation or after the system is already deployed. This flexibility allows you to start with one authentication method (such as Active Directory) and later transition to Okta if needed. During Installation – If you are enabling Okta SAML as part of the Scout Board installation, provide the required parameter values below during the setup of the Scout Enterprise Management Suite.  The initial user parameter must be an existing user in Okta with access to the Scout Board application. The installer will then add the user as an administrator for Scout Board.  Post-Installation – If Scout Board is already installed and you are switching to Okta SAML, locate the .env configuration file at C:\Program Files\Unicon\Scout\ScoutBoard and update the relevant parameters with the corresponding values listed below.  Parameters Value AUTH_TYPE OKTA_SAML SAML_SSO_ENTRY Sign on URL SAML_LOGOUT_URL Single Logout URL SAML_SERVICE_PROVIDER_ISSUER https://scoutboard.com/47d899fb-3002-4692-b741-8068b8f7fbb4 *** Only replace if you are not using the Scout Board issuer SAML_CERT_PATH Okta app integration certificate okta.cert (i.e: C:\Program Files\Unicon\Scout\ScoutBoard\okta.cert)  Upload the okta.cert to the Scout Board machine  SAML_PRIVATE_KEY_PATH private.key used to sign the requests (i.e: C:\Program Files\Unicon\Scout\ScoutBoard\okta_private_key.pem)  Upload the private key to the Scout Board machine   Template for the .env file: AUTH_TYPE=OKTA_SAML
SAML_SSO_ENTRY=
SAML_LOGOUT_URL=
SAML_SERVICE_PROVIDER_ISSUER=https://scoutboard.com/47d899fb-3002-4692-b741-8068b8f7fbb4
SAML_CERT_PATH=
SAML_PRIVATE_KEY_PATH= Note: If you enable Okta SAML authentication after Scout Board has been installed, ensure that at least one user with permissions for the Okta SAML integration app is added to the Scout database. ●        Using SQL Server Management Studio, open the dbo.Administrator table. ●        Insert a new record by copying all values from AdministratorID = 1, but update the Name column with the user’s email address. This is required because the Okta initial user does not yet exist in the Scout Board. Restart the Scout Board service for the changes to take effect.  ValidationThe configured integration supports both Service Provider (SP)-initiated and Identity Provider (IdP)-initiated flows.  It’s recommended to validate both: SP-Initiated Flow – From a browser, navigate to the Scout Board URL: You should be redirected to the Okta login page. After entering your credentials, you should be automatically signed in to Scout Board. IdP-Initiated Flow – If you make the Scout Board app available in the Okta portal: Sign in to the Okta portal. Launch the Scout Board app, and confirm you are automatically signed in without being prompted for credentials. Finally, test Single Logout (SLO) by signing out of Scout Board and verifying that you are also logged out of your Okta session.  SummaryIntegrating Scout Board and Okta for SAML 2.0 authentication enables seamless, secure authentication using your organization’s existing identity infrastructure.  With SSO in place, users benefit from a simplified login process, and administrators gain centralized control over access policies. In addition to SAML 2.0, administrators can configure Okta OpenID Connect (OICD) for authentication with Scout Board. For configuration details, refer to the Scout Board Installation Guide. For further guidance, reach out to your Citrix representative or consult the official documentation at https://docs.citrix.com/en-us/unicon-elux-scout.]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2025_08/scout.png.8c56e26cbceaccc9ba96e24f9dd1ffd9.png" length="162110" type="image/png"/><pubDate>Thu, 14 Aug 2025 13:49:06 +0000</pubDate></item><item><title>Deployment Guide: Citrix MCS for Amazon WorkSpaces Core Managed Instances</title><link>https://community.stage.citrix.com/tech-zone/build/deployment-guides/mcs-for-amazon-wsc/</link><description>OverviewIn this Deployment Guide, we will walk you through the process of deploying Citrix MCS for Amazon WorkSpaces Core Managed Instances environments. The new integration introduces an Amazon WorkSpaces Core Host Connection type, which allows Citrix MCS to manage the power and lifecycle of VDAs across Amazon WorkSpaces Core environments.   What&#xB4;s NewWith this new product, we&#x2019;re expanding Citrix MCS non-persistent and persistent provisioning capabilities to work with Amazon WorkSpaces Core Managed Instances environments. Here&#x2019;s what&#x2019;s now possible: Non-Persistent and persistent Workload Management: power management and provisioning of non-persistent and persistent WorkSpaces instances VM and Image lifecycle management (create, view, update, delete) Bring existing AWS EC2 AMIs to use as images for WorkSpaces BYOL Windows 11/10 Desktop, Windows Server 2025/2022/2019/2016, Ubuntu Server 24.04 LTS, and Debian 12 support Flexibility to bring Microsoft 365 Apps for enterprise licenses to run on WorkSpaces Core or purchase Microsoft Office bundles (https://aws.amazon.com/workspaces-family/core/faqs/) Global WorkSpaces Core regional support Customer-controlled account flexibility Identity management flexibility: Active Directory and Entra Hybrid support, Citrix FAS for SSO to VDAs A similar resource location setup, host connection configuration, and catalog creation process as existing MCS workflows (making the experience familiar for current MCS users) Full suite of DaaS capabilities beyond MCS: Studio management console, session brokering/management, HDX, Workspace app, Director monitoring, WEM, deviceTRUST  PrerequisitesFollow our Product Docs to set up an AWS virtualization environment (Amazon WorkSpaces Core environments share similarities with AWS EC2 environments) Install VDAs on the Master VMs (for creating Master Image AMIs, which will be used to prepare Images for MCS Machine Catalogs) Create a service-linked role   Creating a Host ConnectionThe Host Connection creation process for Amazon WorkSpaces Core will be similar to the existing MCS connection to AWS EC2, except for IAM permissions policy. Please see the &#x201C;Minimal IAM permissions&#x201D; in the &#x201C;Appendix&#x201D; section of this guide to properly define IAM permissions for your IAM user or role&#x2019;s policy for Citrix to manage resources in your AWS account. If you&#x2019;d like to create a host connection configured with a proxy, please see the section &#x201C;Create a secure environment for AWS-managed traffic&#x201D; section of our product docs. Create a new connection. Go to the Hosting tab on the Studio console and click Add Connection and Resources.  Use the Resource Location (e.g., WSC) you set up for your AWS environment (see prerequisites) and select &#x201C;Amazon WorkSpaces Core&#x201D; as the Connection Type.   Next, you can either use: an IAM user access key, or an IAM role.  For the IAM user access key approach, please provide your &#x201C;API key&#x201D; and &#x201C;Secret key&#x201D; for the IAM user that has the proper IAM permissions policy for Citrix to manage resources in your AWS account. For the IAM role approach, select &#x201C;Use IAM role&#x201D;:  Ensure that you've assigned an IAM role to the Citrix Cloud Connector (or Delivery Controller) instance with the proper IAM permissions policy, allowing Citrix to manage resources in your AWS account.  Refer to our Role-based Authentication guide for more information.  Finally, give the connection a name.    Specify the location where your VMs will be provisioned. Select the Cloud region, VPC, and Availability Zone for creating new VMs.    Name the resources in the previously selected Availability Zone and choose one or more subnets in the VPC that you configured in the previous menu.  Click through the remaining pages until the Summary page.    Click Finish to create the Host Connection to Amazon WorkSpaces Core.    Create a Prepared ImageGo to the Images tab on the Studio console and click Create Image Definition.       Select an OS type and Session type for the prepared Image. Provide the Host Connection that you created in the Create a Host Connection section of this guide.    Select the resources you created in the Create a Host Connection section of this guide.  Select a Master Image as the template for creating a prepared Image version in this Image Definition. The Master Image should be an AMI created from a Master VM with VDA installed (see the prerequisites section of this guide).       Select a Machine Profile that will serve as a hardware template to use when provisioning machines in the Create a Machine Catalog section of this guide. The Machine Profile can be an EC2 instance or Launch Template Version in your resources with hardware properties (e.g., Instance type, Tenancy type, Network mappings, Security Groups, Volume properties) that will be captured for provisioning new VMs.          Select a machine specification that the provisioned VMs will take on during Machine Catalog creation. By default, the Machine Profile&#x2019;s instance type is selected.    Select one or more network interfaces that the provisioned VMs will use during Machine Catalog creation.    You can provide an optional description to highlight information about this initial version of the Image Definition.    Name the definition and click Finish to create the Image Definition and an initial Image version.    To create another Image version (beyond the initial Image version), navigate to the Image Definition you just created, and click Create Image version.    Select Resources, a Master Image, and/or a Machine Profile for the new Image version. The initial Image version&#x2019;s properties are used by default and can be modified.          Create a Machine CatalogGo to the Machine Catalogs tab on the Studio console and click Create Machine Catalog. Select a Machine Type for this catalog (e.g., Multi-session OS or Single-session OS, which is required for persistent machines).          Use the dropdown menu under Resources to select the Resources (Availability Zone) you configured in the Create a Host Connection section of this guide.   To create a machine catalog containing persistent machines, select the option for a &#x201C;static desktop&#x201D; experience that &#x201C;creates a dedicated VM and saves changes on the local disk.&#x201D;  For non-persistent machines, select a &#x201C;random desktop&#x201D; or &#x201C;static desktop&#x201D; that discards all changes.   Click Select an Image to select a Prepared Image for the Machine Catalog.    Select the Prepared Image version that you would like to use, created in the Create a Prepared Image section of this guide, and click Done.    The Machine Profile associated with the Prepared Image will appear, and its hardware properties (e.g., Instance type, Tenancy type, Network mappings, Security groups, Volume properties) will be used to create machines in the catalogs. You can change the Machine Profile source to another VM or launch template version by clicking the Edit button.    Define how many VMs you want to create in the Machine Catalog. The default Machine Specification is based on the Machine Profile. To change it, click Select a Machine Specification and pick the Instance type you&#x2019;d like to use for the VMs.       Configure on-prem AD (or Hybrid Azure AD) for the machines in the catalog by selecting the domain and creating new AD accounts for the VMs that will be created in this Machine Catalog.  The provisioned VMs will be joined to the selected Domain.       After configuring the Domain account options for the VMs in the catalog, you&#x2019;ll have to provide the credentials for the selected domain by clicking Enter credentials, where you&#x2019;ll be prompted to enter an admin-level Username and Password. You can also use a Service Account if you already have Domain credentials saved, as outlined in our Product Docs.       Click through the remaining pages until the Summary page.  Give the Machine Catalog a name and select Finish to create the Machine Catalog.       AppendixMinimal IAM Permissions{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "ec2:AttachVolume",
                "ec2:AssociateIamInstanceProfile",
                "ec2:AuthorizeSecurityGroupEgress",
                "ec2:RevokeSecurityGroupEgress",
                "ec2:AuthorizeSecurityGroupIngress",
                "ec2:CreateImage",
                "ec2:CreateLaunchTemplate",
                "ec2:CreateNetworkInterface",
                "ec2:CreateTags",
                "ec2:CreateVolume",
                "ec2:DeleteLaunchTemplate",
                "ec2:DeleteNetworkInterface",
                "ec2:DeleteSecurityGroup",
                "ec2:DeleteSnapshot",
                "ec2:DeleteTags",
                "ec2:DeleteVolume",
                "ec2:DeregisterImage",
                "ec2:DescribeAccountAttributes",
                "ec2:DescribeAvailabilityZones",
                "ec2:DescribeIamInstanceProfileAssociations",
                "ec2:DescribeImages",
                "ec2:DescribeInstances",
                "ec2:DescribeInstanceTypes",
                "ec2:DescribeInstanceStatus",
                "ec2:DescribeLaunchTemplates",
                "ec2:DescribeLaunchTemplateVersions",
                "ec2:DescribeNetworkInterfaces",
                "ec2:DescribeRegions",
                "ec2:DescribeSecurityGroups",
                "ec2:DescribeSnapshots",
                "ec2:DescribeSubnets",
                "ec2:DescribeTags",
                "ec2:DescribeSpotInstanceRequests",
                "ec2:DescribeInstanceCreditSpecifications",
                "ec2:DescribeInstanceAttribute",
                "ec2:GetLaunchTemplateData",
                "ec2:DescribeVolumes",
                "ec2:DescribeVpcs",
                "ec2:DetachVolume",
                "ec2:DisassociateIamInstanceProfile",
                "ec2:RebootInstances",
                "ec2:RunInstances",
                "ec2:StartInstances",
                "ec2:StopInstances",
                "ec2:TerminateInstances"
            ],
            "Effect": "Allow",
            "Resource": "*"
        },
        {
            "Action": [
                "ec2:CreateSecurityGroup",
                "ec2:DeleteSecurityGroup"
            ],
            "Effect": "Allow",
            "Resource": "*"
        },
        {
            "Action": [
                "ebs:StartSnapshot",
                "ebs:GetSnapshotBlock",
                "ebs:PutSnapshotBlock",
                "ebs:CompleteSnapshot",
                "ebs:ListSnapshotBlocks",
                "ebs:ListChangedBlocks",
                "ec2:CreateSnapshot"
            ],
            "Effect": "Allow",
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                 "kms:CreateGrant",
                 "kms:Decrypt",
                 "kms:DescribeKey",
                 "kms:GenerateDataKeyWithoutPlainText",
                 "kms:GenerateDataKey",
                 "kms:ReEncryptTo",
                 "kms:ReEncryptFrom"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": "iam:PassRole",
            "Resource": "arn:aws:iam::*:role/*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "workspaces-instances:*"
            ],
            "Resource": "*"
        }

    ]
} Confidentiality and DisclaimerConfidentiality and Disclaimer The information in this document is confidential information of Cloud Software Group, Inc. and/or its affiliates. Use, duplication, transmission, or republication for any purpose without the prior written consent of Cloud Software Group, Inc. is expressly prohibited. This document (including, without limitation, any product roadmap or statement of direction data) illustrates the planned testing, release and availability dates for Cloud Software Group, Inc. products and services. This document is provided for informational purposes only and its contents are subject to change without notice. Cloud Software Group, Inc. makes no warranties, express or implied, in or relating to this document or any information in it, including, without limitation, that this document, or any information in it, is error-free or meets any conditions of merchantability or fitness for a particular purpose. The material provided is for informational purposes only, and should not be relied on in making a purchasing decision. The information is not a commitment, promise or legal obligation to deliver any material, code, or functionality. The development, release, and timing of any features or functionality described for our products remains at our sole discretion. During the course of this presentation, Cloud Software Group, Inc. or its representatives may make forward-looking statements regarding future events Cloud Software Group, Inc.'s future results or our future financial performance. These statements are based on management's current expectations. Although we believe that the expectations reflected in the forward-looking statements contained in this presentation are reasonable, these expectations or any such forward-looking statements could prove to be incorrect and actual results or financial performance could differ materially from those stated herein. Cloud Software Group, Inc. does not undertake to update any forward-looking statement that may be made from time to time or on its behalf.</description><enclosure url="https://media.invisioncic.com/r328636/monthly_2025_06/1.png.1c3d3559d0f288fe5898cb6bca7409f6.png" length="103465" type="image/png"/><pubDate>Tue, 22 Jul 2025 10:00:00 +0000</pubDate></item><item><title>AWS Cross-Account Provisioning</title><link>https://community.stage.citrix.com/tech-zone/build/deployment-guides/aws-cross-account-provisioning/</link><description><![CDATA[OverviewAdmins using Citrix Machine Creation Services (MCS) technology have use-cases where Cloud Connectors (or Delivery Controllers) would like to be placed in a primary AWS account with IAM roles that have cross-account resource access to MCS-provisioned machine catalogs in separate secondary accounts. No additional Cloud Connectors should be needed in the secondary accounts. Such a deployment model is not currently supported by MCS, which requires cloud connectors to be placed in every AWS account, each with its own IAM role or IAM user access, including API/secret keys. The current methods are thus not scalable and costly (e.g., resource and management costs of multiple cloud connectors and/or API/secret keys) To address such challenges and more, Citrix MCS is introducing a new capability known as "AWS Cross-Account Provisioning," which enables the use of cross-account IAM roles to provision machine catalogs in one or more different AWS accounts from where the Cloud Connectors (or Delivery Controllers) reside. What is Cross-Account Provisioning?MCS Cross-Account Provisioning for AWS allows admins to provision machine catalogs across multiple AWS accounts using cross-account IAM roles. The Cloud Connector(s) (or Delivery Controller) only needs to reside in a single account with a cross-account IAM role. At the same time, provisioned VMs can span multiple accounts, providing cross-account resource access through IAM. Prerequisites and requirements to enable the feature: Ability for the Domain Controller in the "primary" account to talk to provisioned VMs in other accounts. One option is VPC peering (https://docs.aws.amazon.com/vpc/latest/peering/what-is-vpc-peering.html) Domain Controller in one account can talk to the Cloud connectors in the same account Provisioned VMs in other accounts Cloud connector can talk to the provisioned VMs in separate accounts Cross-account resource access in IAM (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies-cross-account-resource-access.html) The destination/target account (where machine catalogs of provisioned VMs will reside) needs to provide (1) trust policy and (2) permissions policy to assume role for the originating account (CC/DDC and DC) The originating account must have a permissions policy in place to pass the role from the destination account to the cloud connector. The cloud connector will then assume the passed-on role and be able to provision VMs in the destination account.  Why Use Cross-Account Provisioning?Administrators will benefit from: Saving compute and storage costs since cloud connector(s) only need to be created in one account (rather than multiple, which can cause significant cost increases as a function of the number of accounts) Being able to create and manage machine catalogs in an account separate from that of their CC, DDC, and/or DC Provisioning capabilities similar to Azure with multi-subscription or multi-tenancy A scalable solution for (1) large enterprises that have many subdivisions managed in separate AWS accounts, and (2) big orgs that act like or as partners for smaller orgs and manage AWS accounts in a hierarchical fashion Only needing role-based authentication and not creating multiple API or secret keys for every account and host connection. Much more scalable and manageable, since API keys/secrets have to be rotated on a regular/periodic basis Role-based authentication is also the AWS best practice for leveraging the API/SDK, making Citrix environments much more secure and compliant. How To Enable Cross-Account Provisioning?Using Web StudioLet's assume that Account "A" is a primary or originating account where shared infrastructure or service components reside. At the same time, Account "B" is a destination/target account where we would like to provision VMs/machine catalogs. Set up VPC peering between Account A and Account B. See the following guides to do so: Citrix product documentation https://docs.aws.amazon.com/vpc/latest/peering/create-vpc-peering-connection.html Delegate cross-account access using IAM roles. See the following guides to do so: Citrix product documentation https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies-cross-account-resource-access.html Under the Citrix Web Studio hosting page, select "Add Connection and Resources." Select "Create a new connection" and then select "Use cross-account Access to provision resources." Provide the ARN of the IAM role in Account B that the Cloud Connectors (or Delivery Controllers) in Account A will assume and need to perform provisioning operations in. Also, give the connection a name.  Complete host connection creation. You can now provision VMs in Account B associated with this newly created host connection. Using PowershellFollow steps 1-2 of the "Using Web Studio" section to configure IAM roles for VPC peering and cross-account access. Use PowerShell commands to create the host connection and add two custom properties: CrossAccountRoleArn: If you don’t provide the CrossAccountRoleArn property, then the regular Host Connection is created. In this case, MaximumAssumeRoleDurationInSeconds is ignored even if it is provided. MaximumAssumeRoleDurationInSeconds: DurationInSeconds must be between 900 seconds to 3600 seconds. The default is 900 seconds. If you provide a value of more than 3600, then DurationInSeconds is set to 3600. Example: $connectionName = "cross-account-conn"
$cloudRegion = "us-east-1"
$apiKey = "role_based_auth"
$secretKey = "role_based_auth"
$zoneUid = "xxxxxx"
$secureKey = (ConvertTo-SecureString -String $secretKey -AsPlainText -Force)
$connectionPath = "XDHyp:\Connections\" + $connectionName
$customProperties = '&lt;CustomProperties xmlns="http://schemas.citrix.com/2014/xd/machinecreation" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;
&lt;Property xsi:type="StringProperty" Name="CrossAccountRoleArn" Value="arn:aws:iam::5678:role/citrix-role" /&gt;&lt;Property xsi:type="StringProperty" Name="MaximumAssumeRoleDurationInSeconds" Value="3600" /&gt;
"&lt;/CustomProperties&gt;'
 
$connection = New-Item -Path $connectionPath -ConnectionType "AWS" -HypervisorAddress "https://ec2.$($cloudRegion).amazonaws.com" -Persist -Scope @() -UserName $apiKey -SecurePassword $secureKey -ZoneUid $zoneUid -CustomProperties $customProperties
New-BrokerHypervisorConnection -HypHypervisorConnectionUid $connection.HypervisorConnectionUid]]></description><pubDate>Thu, 03 Jul 2025 16:01:40 +0000</pubDate></item><item><title>Deployment Guide: Using HashiCorp Packer to Automate the Creation of Master Images</title><link>https://community.stage.citrix.com/tech-zone/build/deployment-guides/master-images-with-packer/</link><description><![CDATA[OverviewWhat is HashiCorp Packer?HashiCorp Packer is an open-source tool designed to automate the creation of machine images for multiple platforms from a single source configuration. It enables developers and DevOps teams to define Image Creation workflows in code, allowing for consistent, repeatable, and version-controlled Image builds. Packer supports a wide range of platforms, including: AWS AMIs (Amazon Machine Images) Azure Managed Images Azure Compute Galleries Google Cloud Images Docker containers vSphere, VirtualBox, and other Hypervisors  Core ConceptsImportant: Packer does not replace configuration management like Chef or Puppet.   Packer can use tools like Chef or Puppet to install software onto the Image. Immutable InfrastructurePacker promotes the concept of immutable infrastructure, where Images are not modified after deployment. Instead, new Images are built and deployed. This approach ensures zero Configuration drift as the Image contains all configurations. The reproducibility is also 100% - all Image builds are the same.  Common Use CasesGolden Image Creation Create pre-configured, secure, and tested Base Images for cloud or on-prem environments. CI/CD Integration Integrate with CI/CD pipelines to automatically build and test Images during deployment. Multi-Cloud/Hybrid-Cloud Deployments Build identical Images for multiple cloud providers or even hybrid cloud deployments to ensure consistency across environments. Container Image Creation Use Packer to build Docker Images with consistent provisioning steps. Disaster Recovery Maintain up-to-date Images that can be quickly deployed in the event of a failure.  Benefits of using PackerBenefit Description Consistency Ensures all environments use the same base image, reducing "it works on my machine" issues. Automation Fully automates image creation, reducing manual errors and saving time. Speed Speeds up instance provisioning by using pre-baked images. Version Control Templates can be stored, for example, in Git, enabling change tracking and collaboration. Multi-Platform Support Build images for multiple platforms from a single template. Security Embed security patches and configurations into images before deployment.  Installing PackerThe installation of Packer is straightforward. There are many ways to install Packer – you can find more information about Installation on the Packer homepage. Note: In our case, we use a dedicated Ubuntu-based VM, where all automation-related frameworks are installed. You can find more information about creating and installing such a dedicated machine on Citrix TechZone.  Example of installing Packer on Ubuntu: azadmin@tacg-cicd:~$curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add -
OK

azadmin@tacg-cicd:~$echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(grep -oP '(?&lt;=UBUNTU_CODENAME=).*' /etc/os-release || lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
deb [arch=amd64 signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com noble main

azadmin@tacg-cicd:~$ sudo apt-get update &amp;&amp; sudo apt-get install packer
Hit:1 http://at.archive.ubuntu.com/ubuntu noble InRelease
Get:2 http://at.archive.ubuntu.com/ubuntu noble-updates InRelease [126 kB]
Get:3 https://apt.releases.hashicorp.com noble InRelease [12.9 kB]
Ign:4 https://pkg.jenkins.io/debian binary/ InRelease
Get:5 http://at.archive.ubuntu.com/ubuntu noble-backports InRelease [126 kB]
Hit:6 https://pkg.jenkins.io/debian binary/ Release
Get:7 http://at.archive.ubuntu.com/ubuntu noble-updates/main amd64 Packages [1,110 kB]
Hit:9 https://ppa.launchpadcontent.net/ansible/ansible/ubuntu noble InRelease
Get:10 http://security.ubuntu.com/ubuntu noble-security InRelease [126 kB]
Get:11 http://at.archive.ubuntu.com/ubuntu noble-updates/main amd64 Components [161 kB]
Get:12 http://at.archive.ubuntu.com/ubuntu noble-updates/restricted amd64 Components [212 B]
Get:13 http://at.archive.ubuntu.com/ubuntu noble-updates/universe amd64 Packages [1,068 kB]
Get:14 http://at.archive.ubuntu.com/ubuntu noble-updates/universe amd64 Components [376 kB]
Get:15 http://security.ubuntu.com/ubuntu noble-security/main amd64 Components [21.6 kB]
Get:16 http://at.archive.ubuntu.com/ubuntu noble-updates/multiverse amd64 Components [940 B]
Get:17 http://at.archive.ubuntu.com/ubuntu noble-backports/main amd64 Components [7,072 B]
Get:18 http://at.archive.ubuntu.com/ubuntu noble-backports/restricted amd64 Components [216 B]
Get:19 http://at.archive.ubuntu.com/ubuntu noble-backports/universe amd64 Components [16.4 kB]
Get:20 http://at.archive.ubuntu.com/ubuntu noble-backports/multiverse amd64 Components [212 B]
Get:21 http://security.ubuntu.com/ubuntu noble-security/restricted amd64 Components [212 B]
Get:22 http://security.ubuntu.com/ubuntu noble-security/universe amd64 Components [52.2 kB]
Get:23 http://security.ubuntu.com/ubuntu noble-security/multiverse amd64 Components [212 B]
Fetched 3,206 kB in 1s (3,247 kB/s)
Reading package lists... Done
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following NEW packages will be installed:
  packer
0 upgraded, 1 newly installed, 0 to remove and 20 not upgraded.
Need to get 14.4 MB of archives.
After this operation, 42.0 MB of additional disk space will be used.
Get:1 https://apt.releases.hashicorp.com noble/main amd64 packer amd64 1.13.0-1 [14.4 MB]
Fetched 14.4 MB in 0s (32.6 MB/s)
Selecting previously unselected package packer.
(Reading database ... 223616 files and directories currently installed.)
Preparing to unpack .../packer_1.13.0-1_amd64.deb ...
Unpacking packer (1.13.0-1) ...
Setting up packer (1.13.0-1) ...
azadmin@tacg-cicd:~$
azadmin@tacg-cicd:~$ packer --version
Packer v1.13.0
azadmin@tacg-cicd:~$ Packer CommandsPacker is controlled using its command-line interface (CLI). Calling packer without any subcommand shows all currently implemented commands: azadmin@tacg-cicd:~$ packer
Usage: packer [--version] [--help] &lt;command&gt; [&lt;args&gt;]

Available commands are:
    build           build image(s) from template
    console         creates a console for testing variable interpolation
    fix             fixes templates from old versions of packer
    fmt             Rewrites HCL2 config files to canonical format
    hcl2_upgrade    transform a JSON template into an HCL2 configuration
    init            Install missing plugins or upgrade plugins
    inspect         see components of a template
    plugins         Interact with Packer plugins and catalog
    validate        check that a template is valid
    version         Prints the Packer version

azadmin@tacg-cicd:~$ Note: You can find a detailed description of the commands on the Packer homepage. For this guide, we will use only 3 commands – these are making up the core workflow: packer init -upgrade path_to_template.hcl: This command downloads and installs all needed plugins according to the template file. azadmin@tacg-cicd:~/packer-templates/winsrv2025-ssh$ packer init -upgrade win2025-std-config.pkr.hcl
Installed plugin github.com/hashicorp/vsphere v1.4.2 in "/home/azadmin/.config/packer/plugins/github.com/hashicorp/vsphere/packer-plugin-vsphere_v1.4.2_x5.0_linux_amd64"
Installed plugin github.com/rgl/windows-update v0.16.10 in "/home/azadmin/.config/packer/plugins/github.com/rgl/windows-update/packer-plugin-windows-update_v0.16.10_x5.0_linux_amd64"
azadmin@tacg-cicd:~/packer-templates/winsrv2025-ssh$ packer validate -var-file=path_to_varfile.hcl path_to_template.hcl:  This command is used to validate the syntax and configuration of a template file.  It returns a specific exit status, depending on whether the operation was successful or not. An error message will be shown if a template cannot be validated successfully. azadmin@tacg-cicd:~/packer-templates/winsrv2025-ssh$ packer validate -var-file win2025-std.auto.pkrvars.hcl win2025-std-
config.pkr.hcl
The configuration is valid.
azadmin@tacg-cicd:~/packer-templates/winsrv2025-ssh$ packer build -var-file=path_to_varfile.hcl -force -on-error=abort path_to_template.hcl: This command is used to build an Image based on the template file and the variables for the template. The -force parameter instructs Packer to overwrite existing artifacts. If a previous build has created an artifact (such as an AMI or Vagrant box) with the same name or ID, Packer will delete or overwrite it without prompting.  It also instructs Packer to skip confirmation prompts, thereby bypassing any interactive confirmation that might otherwise halt the build. In case of an error during the building process, the -on-error=abort parameter instructs Packer to exit without removing any created stuff, which is helpful during debugging.  Example of the output of a successful Image Build: azadmin@tacg-cicd:~/packer-templates/winsrv2025-ssh$ packer build -var-file win2025-std.auto.pkrvars.hcl -force -on-error=abort win2025-std-config.pkr.hcl
The configuration is valid.
vsphere-iso.winsrv2025: output will be in this color.

==&gt; vsphere-iso.winsrv2025: Creating virtual machine...
==&gt; vsphere-iso.winsrv2025: Customizing hardware...
==&gt; vsphere-iso.winsrv2025: Adding virtual machine flags...
==&gt; vsphere-iso.winsrv2025: Mounting ISO images...
==&gt; vsphere-iso.winsrv2025: Adding configuration parameters...
==&gt; vsphere-iso.winsrv2025: Creating floppy disk...
    vsphere-iso.winsrv2025: Copying files flatly from floppy_files
...
vsphere-iso.winsrv2025: Successfully installed
    vsphere-iso.winsrv2025:
==&gt; vsphere-iso.winsrv2025: Provisioning with Powershell...
==&gt; vsphere-iso.winsrv2025: Provisioning with powershell script: ./setup/post-setup.ps1
    vsphere-iso.winsrv2025: Disable SSH
==&gt; vsphere-iso.winsrv2025: Provisioning with powershell script: ./setup/disable-ssh.ps1
==&gt; vsphere-iso.winsrv2025: Running shutdown command...
==&gt; vsphere-iso.winsrv2025: Deleting floppy drives...
==&gt; vsphere-iso.winsrv2025: Deleting floppy image...
==&gt; vsphere-iso.winsrv2025: Ejecting CD-ROM media...
==&gt; vsphere-iso.winsrv2025: Removing CD-ROM devices...
    vsphere-iso.winsrv2025: Closing sessions ....
Build 'vsphere-iso.winsrv2025' finished after 57 minutes 36 seconds.

==&gt; Wait completed after 57 minutes 36 seconds

==&gt; Builds finished. The artifacts of successful builds are:
--&gt; vsphere-iso.winsrv2025: winsrv2025-01
azadmin@tacg-cicd:~/packer-templates/winsrv2025-ssh$ Packer TerminologyA short overview of terms related to Packer: Artifacts Each Builder creates a single Artifact during the building process. For example, the vSphere Builder creates a directory containing all files of the created VM. Builds A Build is a task that produces an Artifact based on the instructions set in the Template. Builder A Builder is specific to each platform and is the process that creates the Virtual Machine. Each Builder has different capabilities and needs depending on the underlying platform. For example, the azure-arm builder allows you to build VHDs and Managed Images in ARM. The vsphere-ISO builder uses an OS ISO file and builds a VM Image on vSphere.  Provisioner A Provisioner allows Packer to install Software before the Build finishes. Provisioners are the main component that adds all the desired elements to the underlying OS. Provisioners can also call other IaC Frameworks, such as Ansible, or run scripts like Shell Scripts or PowerShell snippets, or upload files. It is imperative that communication between the Packer Machine and the Windows Machine, using WinRM and/or SSH, can be established without error. Post-Processor A Post-Processor takes a successful build and can be used, e.g., to upload an Artifact or package the files. For example, the vsphere Post-Processor uploads a successfully created Artifact to vSphere. Template A JSON- or HCL-based human-readable file which defines the build process. It holds the configuration and tells the Builder what to do. Data Source A Data Source fetches Data from the outer World and makes it accessible to Packer.  Important: Communication between the Packer Machine and the Windows Machine must use WinRM and/or SSH . Please check the communication flow for errors.  Note: You can assure that WinRM is configured correctly by adding the configuration script to the unattend.xml or autounattend.xml file, which Packer will use.  Packer StructurePacker uses Templates written in HCL as the receipt for creating an Image. You must follow a simple structure in the Templates (see example of a Windows Server 2025 Template below): # Windows Server 2025 deployment using Hashicorp Packer

locals {
  SSHUser          = "XxXxXxXxXxXxX"
  SSHPass          = "XxXxXxXxXxXxX"
  WinRMUser        = "administrator"
  WinRMPass        = "XxXxXxXxXxXxX"
  vcenter_admin    = "admin@vspXxX.tmmXxXxX"
  vcenter_password = "XxXxXxXxXxXxX"
  timestamp        = regex_replace(timestamp(), "[- TZ:01]", "")
  buildtime        = formatdate("DD-MM-YYYY hh:mm ZZZ", timestamp())
  manifest_date    = formatdate("DD-MM-YYYY hh:mm:ss", timestamp())
}

packer {
  required_version = "&gt;= 1.12.0"
  required_plugins {
    vsphere = {
      version = "&gt;= v1.4.2"
      source  = "github.com/hashicorp/vsphere"
    }
  }
  required_plugins {
    windows-update = {
      version = "&gt;= 0.16.8"
      source  = "github.com/rgl/windows-update"
    }
  }
}

variable "host" {
  type        = string
  description = "Name of the Host to use"
}

variable "name" {
  type        = string
  description = "Name of the Image"
}
...
variable "vvtd_enabled" {
  type = string
}

source "vsphere-iso" "winsrv2025" {
  CPUs     = var.vm_cpus
  notes    = "TMM - Built by HashiCorp Packer on ${local.buildtime}."
  RAM      = var.vm_memory
  firmware = var.vm_firmware
  ...
  network_adapters {
    network      = var.vcenter_network
    network_card = var.vm_network_card
  }
  ...
  storage {
    disk_size             = var.vm_disk_size
    disk_thin_provisioned = var.vm_disk_thin
  }
  ...
  boot_order       = var.vm_boot_order
  boot_wait        = var.vm_boot_wait
  boot_command     = var.vm_boot_command
  shutdown_command = var.vm_shutdown_command
}

build {
  sources = ["source.vsphere-iso.winsrv2025"]

  provisioner "windows-update" {
    pause_before    = "30s"
    search_criteria = "IsInstalled=0"
    filters = [
      "exclude:$_.Title -like '*Preview*'",
      "exclude:$_.InstallationBehavior.CanRequestUserInput",
      "include:$true"
    ]
    restart_timeout = "120m"
  }

  provisioner "windows-restart" {
    restart_check_command = "powershell -command \"&amp; {Write-Output 'restarted.'}\""
    restart_timeout       = "20m"
  }

  provisioner "powershell" {
    elevated_user     = local.SSHUser
    elevated_password = local.SSHPass
    scripts = [
      "./setup/disable-autolog.ps1",
      "./setup/disable-ssh.ps1"
    ]
  }
}The locals {} block defines variables used locally in this particular Template. The packer {} block is the root block of this Template and contains all settings. It specifies a required version – in this case, at least Packer version 1.12 or higher. The required_plugins {} block defines all plugins needed for a successful build. The variable {} blocks define all the variable names, the types, and, if needed, default values. Packer looks for the values in the variable file mentioned in the CLI command you used to start the build (remember packer build -var-file=path_to_varfile.hcl -force -on-error=abort path_to_template.hcl). The source {} block configures a plugin for building an Image on a specific platform and defines all needed settings. Important: This block differs based on the underlying platform and its needs. It can be reused whenever you need to build an Image on the same platform. The build {} block defines all necessary steps that Packer should do sequentially after the complete source {} block. Now, as all needed blocks are defined, you can build your Image. If you are unsure whether your Template is valid, you can run 'packer validate -var-file=path_to_varfile.hcl path_to_template. hcl' to check and identify any errors.  Creating Master Images with PackerIn this guide, we will look at two ways to automate the creation of Master Images using Packer: Creating a Master Image on vSphere Creating a Master Image on Azure The HCL-based Packer templates are primarily built using variables for maximum flexibility and reusability. There are different ways of modifying the Master Image during the creation process – in this guide, we look at the following steps: Packer initially creates the Base Images and sets the initial Software configurations on the Master Image using PowerShell scripts embedded in the autounattend.xml file and using different provisioners. After creating the Master Image initially, we use Ansible to automate further software installations and configurations. Using this approach, you can maximize the flexibility during the automated creation of your Master Images. Modifying the Master Image using Autounattend.xmlThe autounattend.xml file is an unattended answer file used during the Windows setup process. It contains configuration settings and instructions that Windows Setup uses to perform an automated installation without requiring user interaction. Important: The autounattend.xml file is only needed for Hypervisor-based Images. It is not required for all Hyperscaler-/Cloud-based deployments. Of course, you could convert an upload  Hypervisor-based Image to the Hyperscaler…  Caution: The syntax and structure of the autounattend.xml file are very complex and differ between the Operating Systems. Please be aware of the correct structure and syntax, as an erroneous autounattend.xml file can break the deployment of the Windows Master Image. Some benefits of using autounattend.xml files for modifying the Windows Master Image are: Fully Automated Installation: It eliminates the need for manual input during installation (e.g., language selection, disk partitioning, product key entry)  Consistency Across Deployments:  Ensures every machine is set up precisely the same way — ideal for enterprise environments or mass deployments  Time-Saving:  Speeds up the deployment process, especially when installing Windows on many machines.  Customization:  You can predefine: Computer name User accounts Network settings Domain-join settings Application installations Post-install scripts  Hands-Free Setup:  Useful for remote or unattended installations where no technician is present.  Note: You can decide when to use the autounattend.xml method for initial configuration and when to use Ansible after the initial deployment. For demonstration purposes, we use both ways sequentially in this guide.  Example of an autounattend.xml file for initial configuration: ...
&lt;settings pass="oobeSystem"&gt;
        &lt;component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;
            &lt;AutoLogon&gt;
                &lt;Enabled&gt;true&lt;/Enabled&gt;
                &lt;Username&gt;administrator&lt;/Username&gt;
                &lt;Password&gt;
                    &lt;Value&gt;XxXxXxXxXxXxXxX&lt;/Value&gt;
                    &lt;PlainText&gt;true&lt;/PlainText&gt;
                &lt;/Password&gt;
                &lt;LogonCount&gt;5&lt;/LogonCount&gt;
            &lt;/AutoLogon&gt;
            &lt;FirstLogonCommands&gt;
                &lt;SynchronousCommand wcm:action="add"&gt;
                    &lt;Order&gt;1&lt;/Order&gt;
                    &lt;CommandLine&gt;powershell -ExecutionPolicy Bypass -File a:\install-vmwtools.ps1&lt;/CommandLine&gt;
                    &lt;Description&gt;VMware Tools Installation&lt;/Description&gt;
                &lt;/SynchronousCommand&gt;
                &lt;SynchronousCommand wcm:action="add"&gt;
                    &lt;Order&gt;2&lt;/Order&gt;
                    &lt;CommandLine&gt;powershell -ExecutionPolicy Bypass -File a:\enable-ssh.ps1&lt;/CommandLine&gt;
                    &lt;Description&gt;Enable SSH service&lt;/Description&gt;
                    &lt;RequiresUserInput&gt;true&lt;/RequiresUserInput&gt;
                &lt;/SynchronousCommand&gt;
                &lt;SynchronousCommand wcm:action="add"&gt;
                    &lt;Order&gt;3&lt;/Order&gt;
                    &lt;CommandLine&gt;cmd.exe /c a:\enable-rdp.cmd&lt;/CommandLine&gt;
                    &lt;Description&gt;Enable RDP&lt;/Description&gt;
                &lt;/SynchronousCommand&gt;
                &lt;SynchronousCommand wcm:action="add"&gt;
                    &lt;Order&gt;4&lt;/Order&gt;
                    &lt;CommandLine&gt;powershell -ExecutionPolicy Bypass -File a:\configure-firewall.ps1&lt;/CommandLine&gt;
                    &lt;Description&gt;Configure Windows Firewall&lt;/Description&gt;
                &lt;/SynchronousCommand&gt;
                &lt;SynchronousCommand wcm:action="add"&gt;
                    &lt;Order&gt;5&lt;/Order&gt;
                    &lt;CommandLine&gt;powershell -ExecutionPolicy Bypass -File a:\enable-winrm.ps1&lt;/CommandLine&gt;
                    &lt;Description&gt;Enable WinRM service&lt;/Description&gt;
                    &lt;RequiresUserInput&gt;true&lt;/RequiresUserInput&gt;
                &lt;/SynchronousCommand&gt;
            &lt;/FirstLogonCommands&gt;
            &lt;OOBE&gt;
                &lt;HideEULAPage&gt;true&lt;/HideEULAPage&gt;
                &lt;HideLocalAccountScreen&gt;true&lt;/HideLocalAccountScreen&gt;
                &lt;HideOEMRegistrationScreen&gt;true&lt;/HideOEMRegistrationScreen&gt;
                &lt;HideOnlineAccountScreens&gt;true&lt;/HideOnlineAccountScreens&gt;
                &lt;HideWirelessSetupInOOBE&gt;true&lt;/HideWirelessSetupInOOBE&gt;
                &lt;NetworkLocation&gt;Home&lt;/NetworkLocation&gt;
                &lt;ProtectYourPC&gt;1&lt;/ProtectYourPC&gt;
            &lt;/OOBE&gt;
            &lt;UserAccounts&gt;
                &lt;AdministratorPassword&gt;
                    &lt;Value&gt;XxXxXxXxXxXxXValue&gt;
                    &lt;PlainText&gt;true&lt;/PlainText&gt;
                &lt;/AdministratorPassword&gt;
            &lt;/UserAccounts&gt;
        &lt;/component&gt;
    &lt;/settings&gt;In this example, autounattend.xml sequentially starts five scripts for further configuration: install-vmwtools.ps1 Installs the actual version of the VMware Tools enable-ssh.ps1  Enables and configures SSH communication for Ansible and Terraform enable-rdp.cmd Enables RDP Access configure-firewall.ps1 Configures the Windows Firewall adjacent with the further needs enable-winrm.ps1 Enables and configures WinRM communication for Ansible and Terraform  Note: You can reuse or modify the working ones if you need different autounattend.xml files. Therefore, we created a web-based application with a graphical interface to configure and structure a corresponding autounattend.xml file.  Important: To begin, you can find many examples of valid autounattend.xml files on the Internet.  Example screenshot of the PACKERGEN web application:   Using this application and its framework simplifies the creation of a valid autounattend.xml file. That concludes the part about modifying the Master Image using autounattend.xml. A valid and well-structured autounattend.xml lays the foundation for successfully deploying a Windows-based Master Image.  Modifying the Master Image using AnsibleAnsible is an open-source automation tool used primarily for: Configuration management Application deployment Task automation IT orchestration Ansible describes automation jobs using YAML (in the form of Ansible Playbooks), a simple, human-readable language. It connects to your nodes (Servers, Devices, etc.) over SSH (or WinRM for Windows) and doesn’t require Agent software installed on the target machines. Important: The Ansible Playbooks depend entirely on the correct syntax and structure of the corresponding YAML file. Please be aware that writing YAML using the correct structure and syntax is crucial, as an erroneous Playbook can break the deployment of the Windows Master Image. Some benefits of using Ansible Playbooks for modifying the Windows Master Image, especially when you're aiming for consistency, scalability, and automation in your infrastructure, are: Consistency and Repeatability:  Ansible Playbooks ensure that every Master Image is built the same way, every time. Reduces human error and configuration drift.  Modular and Reusable:  Ansible roles and tasks can be reused across different images or environments. Makes it easy to maintain and update configurations.  Testable and Version-Controlled: Ansible Playbooks can be stored in Git, enabling version control, collaboration, and code reviews. You can test changes in a staging environment before applying them to production images.  Agentless and Cross-Platform: No need to install agents on the Image. Works across Linux, Windows, and Cloud platforms (AWS, Azure, GCP, etc.). Using Ansible to modify your Windows Master Images is a powerful way to ensure consistency and repeatability in your infrastructure. Note: You can reuse or modify working Ansible Playbooks again if you need different Ansible Playbooks . Therefore, we also created a web-based application with a graphical interface to configure and structure the corresponding Ansible Playbooks .  As you can see from this example screenshot of the application, you can choose different System settings and Software Packages - Ansible will install and configure all your desired items. Therefore, the application and its framework directly edit the underlying Ansible Playbooks and Configuration files. Example of an Ansible Playbook showcasing the flexibility – the Web application lets you choose an existing Ansible Playbook and modifies it automatically according to your desired settings (shortened due to the original length of the code/decision logic) : ---
# Install WWCO Golden Master
# Version 100625v2GK
# Windows 11 23H2
- name: Install WWCO Master Image 100625v2GK
  hosts: all
  gather_facts: true

  vars:
    vm_join_domain: true
    vm_enable_rdp: true
    vm_install_windows_updates: false
    vm_join_domain_config: "wwco.net.json"
    vm_optimize_image: true
    vm_install_vda: true
    vm_install_vda_mcsio: false
    vm_install_vda_firewall: false
    vm_install_vda_rtt: false
    vm_install_vda_pm: false
    vm_install_vda_pm_wmi: true
    vm_install_vda_upgr: true
    vm_install_wem: false
    vm_install_pvs: false
    vm_install_uber: false
    vm_install_wapp: false
    vm_install_o365: true
    vm_install_python: false
    vm_install_ps7: false
    vm_install_crm: false
    vm_install_chrome: true
    vm_install_firefox: true
    vm_install_slack: false
    
  roles:
    - role: join_domain
      when: vm_join_domain == true
    - role: enable_rdp
      when: vm_enable_rdp == true  
    - role: install_vda_std
      when: vm_install_vda == true and vm_install_vda_mcsio == false and vm_install_vda_firewall == false and vm_install_vda_rtt == false and vm_install_vda_pm == false and vm_install_vda_pm_wmi == false and vm_install_vda_upgr == false
    - role: install_vda_std_mcsio
      when: vm_install_vda == true and vm_install_vda_mcsio == true and vm_install_vda_firewall == false and vm_install_vda_rtt == false and vm_install_vda_pm == false and vm_install_vda_pm_wmi == false and vm_install_vda_upgr == false
    - role: install_vda_std_fw
      when: vm_install_vda == true and vm_install_vda_mcsio == false and vm_install_vda_firewall == true and vm_install_vda_rtt == false and vm_install_vda_pm == false and vm_install_vda_pm_wmi == false and vm_install_vda_upgr == false
    - role: install_vda_std_rtt
      when: vm_install_vda == true and vm_install_vda_mcsio == false and vm_install_vda_firewall == false and vm_install_vda_rtt == true and vm_install_vda_pm == false and vm_install_vda_pm_wmi == false and vm_install_vda_upgr == false
    - role: install_vda_std_pm
      when: vm_install_vda == true and vm_install_vda_mcsio == false and vm_install_vda_firewall == false and vm_install_vda_rtt == false and vm_install_vda_pm == true and vm_install_vda_upgr == false
    - role: install_vda_std_pm_wmi
      when: vm_install_vda == true and vm_install_vda_mcsio == false and vm_install_vda_firewall == false and vm_install_vda_rtt == false and vm_install_vda_pm_wmi == true and vm_install_vda_upgr == false
    - role: install_vda_std_upgr
      when: vm_install_vda == true and vm_install_vda_mcsio == false and vm_install_vda_firewall == false and vm_install_vda_rtt == false and vm_install_vda_pm == false and vm_install_vda_pm_wmi == true and vm_install_vda_upgr == true
...
    - role: install_wem
      when: vm_install_wem == true
    - role: install_pvs
      when: vm_install_pvs == true
    - role: install_uber
      when: vm_install_uber == true
    - role: install_wapp
      when: vm_install_wapp == true
    - role: install_o365
      when: vm_install_o365 == true
    - role: install_python
      when: vm_install_python == true    
    - role: install_ps7
      when: vm_install_ps7 == true   
    - role: install_crm
      when: vm_install_crm == true   
    - role: install_chrome
      when: vm_install_chrome == true
    - role: install_firefox
      when: vm_install_firefox == true
    - role: install_slack
      when: vm_install_slack == true
    - role: install_windows_updates
      when: vm_install_windows_updates == true
    - role: optimize_image
      when: vm_optimize_image == true
...Important: The vars: part is the most essential part, as the variables decide whether a specific package or configuration will be deployed.  For example, checking “Install VDA” sets the corresponding variable to true. vm_install_vda: trueAnsible will now run the corresponding role install_vda_std: - role: install_vda_std
      when: vm_install_vda == true and vm_install_vda_mcsio == false and vm_install_vda_firewall == false and vm_install_vda_rtt == false and vm_install_vda_pm == false and vm_install_vda_pm_wmi == false and vm_install_vda_upgr == false The Ansible roles contain all needed Tasks for deploying the corresponding functionality – for example, these are the Tasks for the Ansible role “Join-Domain”: ---
# Ansible Role Join-Domain
## Generic Tasks file for Join-Domain Role
- name: Create the DNS Server List
  ansible.builtin.set_fact:
    dns_servers_list: "{{ dns_servers  | split(',') }}"
  when: dns_servers is search(",")

- name: Set DNS to DCs
  ansible.windows.win_dns_client:
    adapter_names: "*"
    dns_servers: "{{ dns_servers_list }}"
  when: dns_servers and  dns_servers is search(",")

- name: Change Computer Name
  ansible.windows.win_hostname:
    name: "{{ vm_hostname }}"
  register: hostname_state

- name: Reboot after Computer Name was changed
  ansible.windows.win_reboot:
    post_reboot_delay: 30
  when: hostname_state.reboot_required

- name: Join to Domain - Variables are set by PACKERGEN-Tool according to chosen Domain-Join configuration
  microsoft.ad.membership:
    dns_domain_name: "{{ dns_domain_name }}"
    domain_admin_user: "{{ domain_join_user }}"
    domain_admin_password: "{{ domain_join_pw }}"
    domain_ou_path: "{{ domain_join_ou_path }}"
    hostname: "{{ vm_hostname }}"
    state: domain
  ignore_unreachable: true
  register: domain_state

- name: Reboot after Domain Join
  ansible.windows.win_reboot:
...
Most of the Ansible Tasks are built on Variables modified and read by the Web application. The Domain configuration is stored as a JSON-based file on an Azure Vault and loaded by the Web application. {
    "dns_domain_name":"WWCO.NET",
    "domain_admin_user":"XxXxXxX@wwco.net",
    "domain_admin_password":"SWFtU29ycnlCdXRUaGF0SXNPZkNvdXJzZU5vdFRoZVJpZ2h0UGFzc3dvcmQ=",
    "domain_ou_path":"OU=VDA,OU=XxXxXxXxX,OU=XxXxXxXxX,OU=XxXxXxXxX,OU=XxXxXxXxX,DC=wwco,DC=net",
    "hostname": "GENMI100625V2",
    "dns1":"10.53.XXX.XXX",
    "dns2":"10.53.XXX.XXX",
    "state": "domain"
}It is written in the corresponding Ansible YAML file: ---
# Variable files for Domain-Join Role
## DNS
dns_servers: "10.53.XXX.XXX,10.53.XXX.XXX"
dns_domain_name: "WWCO.NET"

# Domain
domain_join_user: "XxXxXxX@wwco.net"
domain_join_pw: "SWFtU29ycnlCdXRUaGF0SXNPZkNvdXJzZU5vdFRoZVJpZ2h0UGFzc3dvcmQ="
domain_join_ou_path: "OU=VDA,OU=XxXxXxXxX,OU=XxXxXxXxX,OU=XxXxXxXxX,OU=XxXxXxXxX,DC=wwco,DC=net"
vm_hostname: "GENMI100625V2"That concludes the Ansible section on modifying the Master Image – the possibilities are endless if Ansible is part of the Master Image Creation.  Example Workflow for Packer creating a Master Image on AzureThe workflow below is an example of automating the creation of a Windows Server 2025-based Master Image using all parts mentioned in this guide.  As the environment is fully automated, we use Terraform to create all necessary Azure prerequisites. Terraform output for creation of the needed prerequisites: azadmin@tacg-cicd:~$ terraform init
Initializing the backend...
Initializing provider plugins...
- Finding hashicorp/azurerm versions matching "4.6.0"...
- Installing hashicorp/azurerm v4.6.0...
- Installed hashicorp/azurerm v4.6.0 (signed by HashiCorp)
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.
azadmin@tacg-cicd:~$ terraform plan

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:

  # azurerm_resource_group.WWCONET-TF-PCKR-RG will be created
  + resource "azurerm_resource_group" "WWCONET-TF-PCKR-RG" {
      + id       = (known after apply)
      + location = "westeurope"
      + name     = "PACKR_WEUR"
      + tags     = {
          + "Environment" = "WWCONET_TMM"
          + "Tech"        = "WWCONET_TMM_Tech"
        }
    }

  # azurerm_shared_image.WWCONET-TF-PCKR-SIG-IDW11M will be created
  + resource "azurerm_shared_image" "WWCONET-TF-PCKR-SIG-IDW11M" {
      + accelerated_network_support_enabled = true
      + architecture                        = "x64"
      + gallery_name                        = "PACKR_SIG"
      + hibernation_enabled                 = true
      + hyper_v_generation                  = "V2"
      + id                                  = (known after apply)
      + location                            = "westeurope"
      + name                                = "PACKR_W2K25_MI"
      + os_type                             = "Windows"
      + resource_group_name                 = "PACKR_WEUR"
      + tags                                = {
          + "Environment" = "WWCONET_TMM"
          + "Tech"        = "WWCONET_TMM_Tech"
        }

      + identifier {
          + offer     = "windowsserver"
          + publisher = "microsoftwindowsserver"
          + sku       = "2025-datacenter-azure-edition"
        }
    }

  # azurerm_shared_image_gallery.WWCONET-TF-PCKR-SIG will be created
  + resource "azurerm_shared_image_gallery" "WWCONET-TF-PCKR-SIG" {
      + description         = "Shared Image Gallery for Packer Demo"
      + id                  = (known after apply)
      + location            = "westeurope"
      + name                = "PACKR_SIG"
      + resource_group_name = "PACKR_WEUR"
      + tags                = {
          + "Environment" = "WWCONET_TMM"
          + "Tech"        = "WWCONET_TMM_Tech"
        }
      + unique_name         = (known after apply)
    }

  # azurerm_storage_account.WWCONET-TF-PCKR-STAC will be created
  + resource "azurerm_storage_account" "WWCONET-TF-PCKR-STAC" {
      + access_tier                        = (known after apply)
      + account_kind                       = "StorageV2"
      + account_replication_type           = "LRS"
...
      + sftp_enabled                       = false
      + shared_access_key_enabled          = true
      + table_encryption_key_type          = "Service"

      + blob_properties (known after apply)

      + network_rules (known after apply)

      + queue_properties (known after apply)

      + routing (known after apply)

      + share_properties (known after apply)
    }

Plan: 4 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.
azadmin@tacg-cicd:~$ terraform apply

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:

  # azurerm_resource_group.WWCONET-TF-PCKR-RG will be created
  + resource "azurerm_resource_group" "WWCONET-TF-PCKR-RG" {
      + id       = (known after apply)
      + location = "westeurope"
      + name     = "PACKR_WEUR"
      + tags     = {
          + "Environment" = "WWCONET_TMM"
          + "Tech"        = "WWCONET_TMM_Tech"
        }
    }

  # azurerm_shared_image.WWCONET-TF-PCKR-SIG-IDW11M will be created
  + resource "azurerm_shared_image" "WWCONET-TF-PCKR-SIG-IDW11M" {
      + accelerated_network_support_enabled = true
      + architecture                        = "x64"
      + gallery_name                        = "PACKR_SIG"
      + hibernation_enabled                 = true
      + hyper_v_generation                  = "V2"
      + id                                  = (known after apply)
      + location                            = "westeurope"
      + name                                = "PACKR_W2K25_MI"
      + os_type                             = "Windows"
      + resource_group_name                 = "PACKR_WEUR"
      + tags                                = {
          + "Environment" = "WWCONET_TMM"
          + "Tech"        = "WWCONET_TMM_Tech"
        }

      + identifier {
          + offer     = "windowsserver"
          + publisher = "microsoftwindowsserver"
          + sku       = "2025-datacenter-azure-edition"
        }
    }

  # azurerm_shared_image_gallery.WWCONET-TF-PCKR-SIG will be created
  + resource "azurerm_shared_image_gallery" "WWCONET-TF-PCKR-SIG" {
      + description         = "Shared Image Gallery for Packer Demo"
      + id                  = (known after apply)
      + location            = "westeurope"
      + name                = "PACKR_SIG"
      + resource_group_name = "PACKR_WEUR"
      + tags                = {
          + "Environment" = "WWCONET_TMM"
          + "Tech"        = "WWCONET_TMM_Tech"
        }
      + unique_name         = (known after apply)
    }

  # azurerm_storage_account.WWCONET-TF-PCKR-STAC will be created
  + resource "azurerm_storage_account" "WWCONET-TF-PCKR-STAC" {
      + access_tier                        = (known after apply)
      + account_kind                       = "StorageV2"
      + account_replication_type           = "LRS"
...
      + sftp_enabled                       = false
      + shared_access_key_enabled          = true
      + table_encryption_key_type          = "Service"

      + blob_properties (known after apply)

      + network_rules (known after apply)

      + queue_properties (known after apply)

      + routing (known after apply)

      + share_properties (known after apply)
    }

Plan: 4 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

azurerm_resource_group.WWCONET-TF-PCKR-RG: Creating...
azurerm_resource_group.WWCONET-TF-PCKR-RG: Still creating... [00m10s elapsed]
azurerm_resource_group.WWCONET-TF-PCKR-RG: Creation complete after 11s [id=/subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/PACKR_WEUR]
azurerm_shared_image_gallery.WWCONET-TF-PCKR-SIG: Creating...
azurerm_storage_account.WWCONET-TF-PCKR-STAC: Creating...
azurerm_shared_image_gallery.WWCONET-TF-PCKR-SIG: Still creating... [00m10s elapsed]
azurerm_storage_account.WWCONET-TF-PCKR-STAC: Still creating... [00m10s elapsed]
azurerm_shared_image_gallery.WWCONET-TF-PCKR-SIG: Creation complete after 13s [id=/subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/PACKR_WEUR/providers/Microsoft.Compute/galleries/PACKR_SIG]
azurerm_shared_image.WWCONET-TF-PCKR-SIG-IDW11M: Creating...
azurerm_storage_account.WWCONET-TF-PCKR-STAC: Still creating... [00m20s elapsed]
azurerm_shared_image.WWCONET-TF-PCKR-SIG-IDW11M: Still creating... [00m10s elapsed]
azurerm_shared_image.WWCONET-TF-PCKR-SIG-IDW11M: Creation complete after 12s [id=/subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/PACKR_WEUR/providers/Microsoft.Compute/galleries/PACKR_SIG/images/PACKR_W2K25_MI]
azurerm_storage_account.WWCONET-TF-PCKR-STAC: Still creating... [00m30s elapsed]
azurerm_storage_account.WWCONET-TF-PCKR-STAC: Still creating... [00m40s elapsed]
azurerm_storage_account.WWCONET-TF-PCKR-STAC: Still creating... [00m50s elapsed]
azurerm_storage_account.WWCONET-TF-PCKR-STAC: Still creating... [01m00s elapsed]
azurerm_storage_account.WWCONET-TF-PCKR-STAC: Creation complete after 1m6s [id=/subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/PACKR_WEUR/providers/Microsoft.Storage/storageAccounts/wwcoXxXxXxXxX]

Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
azadmin@tacg-cicd:~$All necessary prerequisites were successfully created. Important: Please ensure that you upload all necessary scripts, Software components, etc., to the newly created Azure Storage Vault. You can also leverage Terraform or the Azure Storage Explorer. Example using Terraform (output shortened due to length): azadmin@tacg-cicd:~$ terraform apply
data.azurerm_resource_group.WWCONET-TF-PCKR-RG: Reading...
data.azurerm_resource_group.WWCONET-TF-PCKR-RG: Read complete after 1s [id=/subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/PACKR_WEUR]
data.azurerm_storage_account.WWCONET-TF-PCKR-STAC: Reading...
data.azurerm_storage_account.WWCONET-TF-PCKR-STAC: Read complete after 1s [id=/subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/PACKR_WEUR/providers/Microsoft.Storage/storageAccounts/wwcoXxXxXxXxX]

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:
...
  # azurerm_storage_blob.VDASRV will be created
  + resource "azurerm_storage_blob" "VDASRV" {
      + access_tier            = (known after apply)
      + content_type           = "application/octet-stream"
      + id                     = (known after apply)
      + metadata               = (known after apply)
      + name                   = "VDAServerSetup_2503.exe"
      + parallelism            = 8
      + size                   = 0
      + source                 = "SRC/VDAServerSetup_2503.exe"
      + storage_account_name   = "wwcoXxXxXxXxX"
      + storage_container_name = "packer"
      + type                   = "Block"
      + url                    = (known after apply)
    }

  # azurerm_storage_blob.VDAWK will be created
  + resource "azurerm_storage_blob" "VDAWK" {
      + access_tier            = (known after apply)
      + content_type           = "application/octet-stream"
      + id                     = (known after apply)
      + metadata               = (known after apply)
      + name                   = "VDAWorkstationSetup_2503.exe"
      + parallelism            = 8
      + size                   = 0
      + source                 = "SRC/VDAWorkstationSetup_2503.exe"
      + storage_account_name   = "wwcoXxXxXxXxX"
      + storage_container_name = "packer"
      + type                   = "Block"
      + url                    = (known after apply)
    }

...

  # azurerm_storage_blob.WAPP will be created
  + resource "azurerm_storage_blob" "WAPP" {
      + access_tier            = (known after apply)
      + content_type           = "application/octet-stream"
      + id                     = (known after apply)
      + metadata               = (known after apply)
      + name                   = "CitrixWorkspaceApp2503.exe"
      + parallelism            = 8
      + size                   = 0
      + source                 = "SRC/CitrixWorkspaceApp2503.exe"
      + storage_account_name   = "wwcoXxXxXxXxX"
      + storage_container_name = "packer"
      + type                   = "Block"
      + url                    = (known after apply)
    }

  # azurerm_storage_container.WWCONET-TF-PCKR-STAC-STCO will be created
  + resource "azurerm_storage_container" "WWCONET-TF-PCKR-STAC-STCO" {
      + container_access_type             = "private"
      + default_encryption_scope          = (known after apply)
      + encryption_scope_override_enabled = true
      + has_immutability_policy           = (known after apply)
      + has_legal_hold                    = (known after apply)
      + id                                = (known after apply)
      + metadata                          = (known after apply)
      + name                              = "packer"
      + resource_manager_id               = (known after apply)
      + storage_account_id                = "/subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/PACKR_WEUR/providers/Microsoft.Storage/storageAccounts/wwcoXxXxXxXxX"
    }

Plan: 29 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

azurerm_storage_container.WWCONET-TF-PCKR-STAC-STCO: Creating...
azurerm_storage_container.WWCONET-TF-PCKR-STAC-STCO: Creation complete after 2s [id=/subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/PACKR_WEUR/providers/Microsoft.Storage/storageAccounts/wwcoXxXxXxXxX/blobServices/default/containers/packer]
azurerm_storage_blob.WAPP: Creating...
azurerm_storage_blob.VDAWK: Creating...
...
azurerm_storage_blob.VDASRV: Creating...
...
azurerm_storage_blob.WAPP: Still creating... [00m10s elapsed]
azurerm_storage_blob.VDASRV: Still creating... [00m10s elapsed]
...
azurerm_storage_blob.VDAWK: Still creating... [00m10s elapsed]
azurerm_storage_blob.VDASRV: Still creating... [00m10s elapsed]
...
azurerm_storage_blob.WAPP: Creation complete after 2m41s [id=https://wwcoXxXxXxXxX.blob.core.windows.net/packer/CitrixWorkspaceApp2503.exe]
...
azurerm_storage_blob.VDAWK: Creation complete after 7m13s [id=https://wwcoXxXxXxXxX.blob.core.windows.net/packer/VDAWorkstationSetup_2503.exe]
...
azurerm_storage_blob.VDASRV: Creation complete after 8m8s [id=https://wwcoXxXxXxXxX.blob.core.windows.net/packer/VDAServerSetup_2503.exe]
...
	
Apply complete! Resources: 29 added, 0 changed, 0 destroyed.

azadmin@tacg-cicd:~$ Now that all prerequisites are complete, the building process using Packer can be initiated. Example: Creating a Windows Server 2025 Master Image on Azure using an existing custom Base ImageIn this example, we use Packer and Ansible to create a tailored Master Image on Azure. We defined the Software packages and needed configurations in Ansible using the Web application. Packer will create the Master Image on Azure based on a custom Base Image and then use Ansible for further configuration and Software deployment. Note: In this example, we use an Ansible playbook to install the Citrix Virtual Delivery Agent (VDA) on the Master Image.  Later in this guide, we will explore an alternative method for deploying the VDA if Ansible is not an option.  Important: Please ensure that you upload all necessary scripts, Software components, etc., to the Azure Storage Vault created above. You can also leverage Terraform or the Azure Storage Explorer as mentioned above. After completion, the Master Image will be put into the Shared Image Gallery. Example Packer HCL code: # Packer File for Creation of a Windows Server 2025-based Master Image based on an existing custom Base Image using Packer and Ansible
## Version 06.2025
### Modified by PACKERGEN
packer {
  required_plugins {
    azure = {
      source  = "github.com/hashicorp/azure"
      version = "&gt;= 2.3.3"
    }
    ansible = {
      source  = "github.com/hashicorp/ansible"
      version = "&gt;= 1.1.3"
    }
  }
}

source "azure-arm" "windows" {
  async_resourcegroup_delete             = true
  client_id                              = var.client_id
  client_secret                          = var.client_secret
  communicator                           = "winrm"
  location                               = var.location
  build_resource_group_name              = var.build_resource_group_name

  # Security settings
  security_type                          = var.security_type
  vtpm_enabled                           = var.vtpm
  secure_boot_enabled                    = var.secure_boot

  # Temp Image Storage Type
  managed_image_storage_account_type     = var.managed_image_storage_account_type
  os_type                                = var.os_type

  shared_image_gallery {
    subscription                         = var.subscription_id
    resource_group                       = var.resource_group_name
    gallery_name                         = var.shared_gallery_name
    image_name                           = var.image_name
    image_version                        = var.image_version
  }
  shared_image_gallery_destination {
    gallery_name                         = var.shared_gallery_name
    image_name                           = var.dest_image_name
    image_version                        = var.dest_image_version
    replication_regions                  = ["${var.image_replication_regions}"]
    resource_group                       = var.resource_group_name
    storage_account_type                 = var.sig_storage_type
  }
  azure_tags = {
    build_number                         = var.build_number
    build_id                             = var.build_id
  }

  private_virtual_network_with_public_ip = "false"
  subscription_id                        = var.subscription_id
  tenant_id                              = var.tenant_id
  vm_size                                = var.vm_size
  virtual_network_name                   = var.virtual_network_name
  virtual_network_resource_group_name    = var.virtual_network_resource_group_name
  virtual_network_subnet_name            = var.virtual_network_subnet_name
  winrm_insecure                         = "true"
  winrm_timeout                          = var.winrm_timeout
  winrm_use_ssl                          = "true"
  winrm_username                         = var.winrm_username
}

build {
  name = "WWCOW2K25MIFromARMTemplate"
  sources = ["source.azure-arm.windows"]

  # Enable WinRM
  provisioner "powershell" {
    script = "/scripts/enable-winrm.ps1"
  }

  # Provisioning of Configuration and Packages using Ansible 
  ## Modified by PACKERGEN
  provisioner "ansible" {
    playbook_file = "${var.ansible_playbook_generic}"
    user          = "packer"
    use_proxy     = false
      extra_arguments = [
      "--extra-vars", "@${var.ansible_config}",
      "-e",
      "ansible_winrm_server_cert_validation=ignore",
      "-e",
      "vm_hostname=${var.vm_name}",
      "${var.ansible_debug}"
    ]
  }

   ## Modified by PACKERGEN
   provisioner "ansible" {
    playbook_file = "${var.ansible_playbook_CTX}"
    user          = "packer"
    use_proxy     = false
      extra_arguments = [
      "--extra-vars", "@${var.ansible_config}",
      "-e",
      "ansible_winrm_server_cert_validation=ignore",
      "-e",
      "vm_hostname=${var.vm_name}",
      "${var.ansible_debug}"
    ]
  }

  provisioner "powershell" {
  script = "/scripts/sysprep.ps1"
  }

  post-processor "manifest" {
   output = "manifest.json"
   strip_path = true
   custom_data = {
        source_image_name = "${build.SourceImageName}"
        tenant_id = "${build.TenantID}"
        subscription_id = "${build.SubscriptionID}"
        temp_deployment_name = "${build.TempDeploymentName}"
        temp_compute_name = "${build.TempComputeName}"
        temp_nic_name = "${build.TempNicName}"
        temp_os_disk_name = "${build.TempOSDiskName}"
        temp_data_disk_name = "${build.TempDataDiskName}"
        temp_resource_group_name = "${build.TempResourceGroupName}"
        temp_nsg_name = "${build.TempNsgName}"
        temp_key_vault_name = "${build.TempKeyVaultName}"
        temp_subnet_name = "${build.TempSubnetName}"
        temp_virtual_network_name = "${build.TempVirtualNetworkName}"
        temp_public_ip_address_name = "${build.TempPublicIPAddressName}"
  }
 }
}
...Important: Please do not forget to follow the correct sequence of the packer commands – e.g.: packer init -upgrade WWCO-W2K25ARMWithAnsible.pkr.hcl packer validate -var-file=WWCO-W2K25ARMWithAnsible.variables.pkr.hcl WWCO-W2K25ARMWithAnsible.pkr.hcl packer build -on-error=abort -force -var-file=WWCO-W2K25ARMWithAnsible.variables.pkr.hcl WWCO-W2K25ARMWithAnsible.pkr.hcl  Example: Creating a Windows Server 2025 Master Image on vSphereIn this example, we use Packer and Ansible to create a customized Master Image on vSphere. We defined the Software packages and needed configurations in Ansible using the Web application. Note: In this example, we use an Ansible Playbook to install the Citrix Virtual Delivery Agent (VDA) on the Master Image. Later in this guide, we will explore an alternative method for deploying the VDA if Ansible is not an option.  Important: Please ensure that you upload all necessary scripts, Software components, etc., to the Azure Storage Vault created above. You can also leverage Terraform or the Azure Storage Explorer as mentioned above. Packer will create the Master Image on Azure based on an official Windows Server 2025 ISO file and then use Ansible for further configuration and Software deployment. Example Packer HCL code: # Packer File for Creation of a Windows Server 2025-based Master Image based on an official Windows-ISO file using Packer and Ansible
## Version 06.2025
### Modified by PACKERGEN
packer {
  required_plugins {
    azure = {
      source  = "github.com/hashicorp/vsphere"
      version = "&gt;= 1.4.2"
    }
    ansible = {
      source  = "github.com/hashicorp/ansible"
      version = "&gt;= 1.1.3"
    }
    windows-update = {
      version = "&gt;= 0.16.8"
      source  = "github.com/rgl/windows-update"
    }
  }
}

source "vsphere-iso" "windows" {
    vcenter_server              = var.vcenter_server
    username                    = var.vcenter_username
    password                    = var.vcenter_password
    insecure_connection         = true

    cluster                     = var.vsphere_cluster
    host                        = var.vsphere_host
    datacenter                  = var.vsphere_datacenter
    datastore                   = var.vsphere_datastore
    folder                      = var.vsphere_folder

    vm_name                     = var.vm_name
    notes                       = "Created by Packer and PACKERGEN."
    guest_os_type               = "windows9Server64Guest"
    firmware                    = var.firmware
    vTPM                        = var.vtpm
    CPUs                        = var.cpu
    cpu_cores                   = var.cpu
    RAM                         = var.memory
    RAM_reserve_all             = true
    remove_cdrom                = true
    cdrom_type                  = "sata"
    
    vm_version                  = var.vm_version

    iso_paths                   = [
      "${var.os_iso_path}"
    ]

    cd_files                  = [
      "/autounattend/Autounattend.xml",
      "/scripts/enable-winrm.ps1",
      "/scripts/install-vmwaretools.ps1",
      "/drivers/"
    ]

    disk_controller_type        = ["pvscsi"]
    storage {
        disk_size                 = var.disksize
        disk_thin_provisioned     = true
    }

    network_adapters {
        network                   = var.vsphere_network
        network_card              = "vmxnet3"
    }

    boot_order              = "disk,cdrom"
    boot_command = [
        "&lt;spacebar&gt;&lt;wait2&gt;&lt;spacebar&gt;"
    ]
    boot_wait                   = "1s"

    communicator                = "winrm"
    winrm_insecure              = true
    winrm_password              = var.connection_password
    winrm_timeout               = "20m"
    winrm_use_ssl               = true
    winrm_username              = var.connection_username
}

build {
    name = "WWCOW2K25MIFromISOvSphere"
    source "vsphere-iso.windows" {
        convert_to_template         = false
        create_snapshot             = var.create_snapshot
        snapshot_name               = "Created by Packer and PACKERGEN"
    }

  # Provisioning of Configuration and Packages using Ansible 
  ## Modified by PACKERGEN
  provisioner "ansible" {
    playbook_file = "${var.ansible_playbook_generic}"
    user          = "packer"
    use_proxy     = false
      extra_arguments = [
      "--extra-vars", "@${var.ansible_config}",
      "-e",
      "ansible_winrm_server_cert_validation=ignore",
      "-e",
      "vm_hostname=${var.vm_name}",
      "${var.ansible_debug}"
    ]
  }

  ## Modified by PACKERGEN
  provisioner "ansible" {
    playbook_file = "${var.ansible_playbook_CTX}"
    user          = "packer"
    use_proxy     = false
      extra_arguments = [
      "--extra-vars", "@${var.ansible_config}",
      "-e",
      "ansible_winrm_server_cert_validation=ignore",
      "-e",
      "vm_hostname=${var.vm_name}",
      "${var.ansible_debug}"
    ]
  }

  post-processor "manifest" {
        output = "manifest.json"
        strip_path = true
        custom_data = {
          source_image_name = "${build.SourceImageName}"
          tenant_id = "${build.TenantID}"
          subscription_id = "${build.SubscriptionID}"
          temp_deployment_name = "${build.TempDeploymentName}"
          temp_compute_name = "${build.TempComputeName}"
          temp_nic_name = "${build.TempNicName}"
          temp_os_disk_name = "${build.TempOSDiskName}"
          temp_data_disk_name = "${build.TempDataDiskName}"
          temp_resource_group_name = "${build.TempResourceGroupName}"
          temp_nsg_name = "${build.TempNsgName}"
          temp_key_vault_name = "${build.TempKeyVaultName}"
          temp_subnet_name = "${build.TempSubnetName}"
          temp_virtual_network_name = "${build.TempVirtualNetworkName}"
          temp_public_ip_address_name = "${build.TempPublicIPAddressName}"
        }
  }  
} Example: Packer Image Management Module for Citrix® Virtual Apps and DesktopsAs mentioned above, there are different ways to deploy the Citrix® Virtual Delivery Agent (VDA) on the Master Image: using the autounattend.xml file, a Packer Provisioner, or Ansible. The VDA is a crucial component for a successful Citrix infrastructure.  The Packer Image Management Module for Citrix® Virtual Apps and Desktops aims to streamline image management by creating a custom Golden Image with the VDA.  It runs the Citrix® Optimizer to improve image efficiency by removing unnecessary bloatware, resulting in optimized performance and resource utilization.  A single command can deploy an Image by creating it using Packer. Note: Please note that this module is still in Tech Preview. You can find the actual version here: https://github.com/citrix/citrix-packer-tools  Important: Please ensure that you upload all necessary scripts, Software components, etc., to the Azure Storage Vault created above. You can also leverage Terraform or the Azure Storage Explorer as mentioned above. Example HCL code for creating a Windows 11-based Image: packer {
  required_plugins {
    azure = {
      source  = "github.com/hashicorp/azure"
      version = "~&gt; 1"
    }
  }
}

variable "client_id" {
  type    = string
  default = "ff1bXXXX-XXXX-XXXX-XXXX-XXXXXXXX53b6"
}

variable "client_object_id" {
  type    = string
  default = "69bdXXXX-XXXX-XXXX-XXXX-XXXXXXXX4244"
}

variable "client_secret" {
  type    = string
  default = "XxXxXxXxXxXxXxXxX"
}

variable "client_subscription_id" {
  type    = string
  default = "de13XXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13"
}

variable "client_tenant_id" {
  type    = string
  default = "1974XXXX-XXXX-XXXX-XXXX-XXXXXXXX474b"
}

variable "image_name" {
  type    = string
  default = "win_office_11_22h2"
}

variable "image_resource_group" {
  type    = string
  default = "TMM-XxXxXxXxX-WEUR"
}

variable "install_browser_chrome_flag" {
  type    = string
  default = "true"
}

variable "install_browser_firefox_flag" {
  type    = string
  default = "true"
}

variable "location_setup" {
  type    = string
  default = "c:\\setup"
}

variable "optimizer_location" {
  type    = string
  default = "https://XxXxXxX.blob.core.windows.net/packer/VDAWorkstationSetup_2503.exe"
}

variable "optimizer_template" {
  type    = string
  default = "Citrix_Windows_11_2009.xml"
}

variable "script_cleanup" {
  type    = string
  default = "run-cleanup.ps1"
}

variable "script_installchrome" {
  type    = string
  default = "install-browser-chrome.ps1"
}

variable "script_installfirefox" {
  type    = string
  default = "install-browser-firefox.ps1"
}

variable "script_installvda" {
  type    = string
  default = "install-vda.ps1"
}

variable "script_optimizer" {
  type    = string
  default = "run-optimizer.ps1"
}

variable "username" {
  type    = string
  default = "XxXxXxXxXxXxXxXxX"
}

variable "userpass" {
  type    = string
  default = "XxXxXxXxXxXxXxXxX"
}

variable "vda_location" {
  type    = string
  default = "https://XxXxXxX.blob.core.windows.net/packer/CitrixOptimizerTool.zip"
}

variable "winrm_insecure" {
  type    = string
  default = "true"
}
# The "legacy_isotime" function has been provided for backwards compatability, but we recommend switching to the timestamp and formatdate functions.

source "azure-arm" "autogenerated_1" {
  azure_tags = {
    Image = "Packer Generated"
  }
  client_id                           = "${var.client_id}"
  client_secret                       = "${var.client_secret}"
  communicator                        = "winrm"
  image_offer                         = "office-365"
  image_publisher                     = "MicrosoftWindowsDesktop"
  image_sku                           = "WIN11-22H2-AVD-M365"
  image_version                       = "latest"
  location                            = "East US"
  managed_image_name                  = "${var.image_name}_${legacy_isotime("200601020304")}"
  managed_image_os_disk_snapshot_name = "${var.image_name}_${legacy_isotime("200601020304")}"
  managed_image_resource_group_name   = "${var.image_resource_group}"
  object_id                           = "${var.client_object_id}"
  os_type                             = "Windows"
  subscription_id                     = "${var.client_subscription_id}"
  tenant_id                           = "${var.client_tenant_id}"
  vm_size                             = "Standard_D2s_v3"
  winrm_insecure                      = "${var.winrm_insecure}"
  winrm_password                      = "${var.userpass}"
  winrm_timeout                       = "20m"
  winrm_use_ssl                       = false
  winrm_username                      = "${var.username}"
}

build {
  sources = ["source.azure-arm.autogenerated_1"]
...
  provisioner "powershell" {
    inline = ["New-Item -Path ${var.location_setup} -ItemType \"directory\" | Out-Null"]
  }

  provisioner "file" {
    destination = "${var.location_setup}\\${var.script_installvda}"
    source      = ".\\scripts\\${var.script_installvda}"
  }

  provisioner "file" {
    destination = "${var.location_setup}\\${var.script_optimizer}"
    source      = ".\\scripts\\${var.script_optimizer}"
  }

  provisioner "file" {
    destination = "${var.location_setup}\\${var.script_installchrome}"
    source      = ".\\scripts\\${var.script_installchrome}"
  }

  provisioner "file" {
    destination = "${var.location_setup}\\${var.script_installfirefox}"
    source      = ".\\scripts\\${var.script_installfirefox}"
  }

  provisioner "file" {
    destination = "${var.location_setup}\\${var.script_cleanup}"
    source      = ".\\scripts\\${var.script_cleanup}"
  }

  provisioner "powershell" {
    inline = ["${var.location_setup}\\${var.script_installvda} -VdaSetupDownloadUri ${var.vda_location}"]
  }

  provisioner "windows-restart" {
    restart_check_command = "powershell -command \"&amp; {Write-Host 'Script: computer restarted.'}\""
    restart_timeout       = "30m"
  }

  provisioner "powershell" {
    inline = ["${var.location_setup}\\${var.script_installvda} -VdaSetupDownloadUri ${var.vda_location}"]
  }

  provisioner "powershell" {
    inline = ["${var.location_setup}\\${var.script_optimizer} -OptimizerDownloadUri ${var.optimizer_location} -Template ${var.optimizer_template}"]
  }

  provisioner "powershell" {
    inline = ["${var.location_setup}\\${var.script_installchrome} -InstallSoftware ${var.install_browser_chrome_flag}"]
  }

  provisioner "powershell" {
    inline = ["${var.location_setup}\\${var.script_installfirefox} -InstallSoftware ${var.install_browser_firefox_flag}"]
  }

  provisioner "powershell" {
    inline = ["${var.location_setup}\\${var.script_cleanup} -Username ${var.username} -LocationPath ${var.location_setup}"]
  }
...
}Our Packer Image Management Module is a prime example of a comprehensive, purpose-built set of HCL commands for deploying a Master Image using Packer.  ConclusionIn today's fast-paced DevOps landscape, automating infrastructure and application deployment is essential for efficiency and scalability. Image creation with Ansible and Packer provides a powerful, flexible, and repeatable approach to building standardized images across various platforms. Here's why this combination is the best: Consistency Across Environments:  Using Packer with Ansible, you can define a single source of truth for your infrastructure. The exact configuration can be applied to different environments, ensuring that your development, testing, and production systems are identical. This reduces discrepancies and environment-specific bugs, providing more reliable deployments.  Automation and Efficiency:  Packer automates the process of building machine images, and when combined with Ansible’s powerful configuration management capabilities, the entire pipeline becomes seamless. This results in faster provisioning, less manual intervention, and reduced human error, a key benefit for teams aiming to speed up delivery cycles.  Reusability and Modularity:  Ansible’s playbooks are reusable and modular, allowing you to create images that are easily adjusted to suit different applications or environments. With Packer, you can create reusable templates for multiple platforms (AWS, Azure, GCP, VMware, etc.), ensuring a wide range of use cases are covered with minimal changes to your configuration.  Scalability:  As organizations grow, so do their infrastructure needs. The combination of Ansible and Packer scales well, enabling you to create images for thousands of virtual machines or instances across a range of platforms without manually configuring each one. This scalability is a key advantage for larger enterprises or cloud-native applications.  Cost-Effective:  Automating image creation reduces the overhead of manual configuration, testing, and re-deployment. With more predictable and stable environments, you can minimize downtime and reduce operational costs. Additionally, since Packer allows you to build and manage images for multiple providers, it can help you choose the most cost-efficient infrastructure options.  Version Control and Auditing:  Packer and Ansible encourage best practices around infrastructure as code (IaC). Integrating these tools into your CI/CD pipeline allows you to version control your image templates and configurations, making it easier to audit, roll back, and trace changes over time. This leads to more transparent, accountable, and manageable operations. Using Ansible and Packer together for image creation combines the best of both worlds—Packer’s ability to build and version machine images across multiple platforms, and Ansible’s power for configuration management. The result is an automated, scalable, and consistent process that enhances operational efficiency, fosters collaboration, mitigates risk, and ultimately ensures a smoother journey toward reliable and repeatable infrastructure.  This approach is the best for teams seeking to streamline their workflows, enhance consistency, and maintain control over their environments. Our following guides will discuss implementing the automated Image Creation in CI/CD-based deployments using Jenkins and Azure Pipelines.  AcknowledgementsThank you to my colleagues Rody Kossen and Alan Goldman for providing valuable information and granting me access to their repositories.  DisclaimerDisclaimer: EXCEPT WHERE EXPRESSLY PROVIDED OTHERWISE BY CLOUD SOFTWARE GROUP, THE CONTENT IN THIS DOCUMENT IS PROVIDED “AS IS” AND CLOUD SOFTWARE GROUP HEREBY DISCLAIMS ALL EXPRESS OR IMPLIED REPRESENTATIONS, WARRANTIES, GUARANTIES, AND CONDITIONS, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES OR CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD TO BE LEGALLY INVALID. CLOUD SOFTWARE GROUP MAKES NO REPRESENTATIONS, WARRANTIES, GUARANTIES, OR CONDITIONS AS TO THE QUALITY, SUITABILITY, TRUTH, ACCURACY, OR COMPLETENESS OF ANY OF THE CONTENT CONTAINED ON THE WEBSITE. The above disclaimer applies to any damages, liability, or injuries caused by any failure of performance, error, omission, interruption, deletion, defect, delay in operation or transmission, computer virus, communication line failure, theft or destruction of or unauthorized access to, alteration of, or use, whether for breach of contract, tort, negligence or any other cause of action.]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2025_06/Packer-Ansible.gif.7aa239faf1facd640cf591c79144162f.gif" length="2494258" type="image/gif"/><pubDate>Mon, 30 Jun 2025 09:05:57 +0000</pubDate></item><item><title>Deploying Citrix Secure Developer Spaces on OpenShift</title><link>https://community.stage.citrix.com/tech-zone/build/deployment-guides/deploying-citrix-secure-developer-spaces-on-openshift/</link><description><![CDATA[OverviewThis guide explains in detail how to deploy Citrix Secure Developer Spaces, formerly known as Strong Network proof of concept platform, to an existing OpenShift cluster. General prerequisitesContainer registry to which you have push permissions An up-and-running OpenShift cluster Basic tools for working with that OpenShift cluster: oc and kubectl You should already be authenticated in both oc and kubectl with your OpenShift cluster. Deployment stepsObtain a Citrix Secure Developer Spaces platform licenseYou need a valid license to use the Citrix Secure Developer Spaces platform. Please contact your ATS or Solution Architect to request that one be issued to you. To issue a license, we only need to know which URL will be used to access the Citrix Secure Developer Spaces platform. In case of OpenShift, we need to find out the cluster domain first, which we can easily do in two ways: From the web console URL: The cluster domain is part of the OpenShift web console address. If you access the console at https://console-OpenShift-console.apps.cluster.example.com, then your cluster domain is apps.cluster.example.com Using the OpenShift CLI (oc): oc get ingresses.config/cluster -o jsonpath='{.spec.domain}' You will get an output like: apps.sm8kda46je6e9a9c7b.swedencentral.aroapp.io Once you have the cluster domain, the final Citrix Secure Developer Spaces platform domain will be in the form of: sn.&lt;cluster-domain&gt; In our case, it will look like the following: sn.apps.sm8kda46je6e9a9c7b.swedencentral.aroapp.io  Now that we have the domain, we will register it with the Citrix Secure Developer Spaces License server.  Check the Wildcard policy of your OpenShift clusterTo be able to create and use workspaces on the Citrix Secure Developer Spaces platform, which have dynamic routes, you need to make sure that the wildcard policy of your OpenShift cluster routing is set to WildcardsAllowed.  To do that, you can follow these steps: Find out the name of your ingress controller by running: oc get ingresscontroller -n OpenShift-ingress-operator -o jsonpath='{.items[*].metadata.name}' If you made a default installation of an OpenShift cluster and haven’t changed any of the settings, the name will most likely be default. Check if your ingress controller has a WildcardsAllowed policy already set, using the name of the controller obtained from the previous step. In our case, it is default, so the command looks like: oc get ingresscontroller default -n OpenShift-ingress-operator -o jsonpath='{.spec.routeAdmission.wildcardPolicy}' If the output is WildcardsAllowed, then everything is set, and you can go to the next step of the guide. If the output is empty, it means we need to set the policy manually. This can be done by editing the configuration of that particular ingress controller (named default) by executing: oc patch ingresscontroller default -n OpenShift-ingress-operator --type=merge -p '{"spec":{"routeAdmission":{"wildcardPolicy":"WildcardsAllowed"}}}' You can now check that the previous command was successful by executing: oc get ingresscontroller default -n OpenShift-ingress-operator -o jsonpath='{.spec.routeAdmission.wildcardPolicy}' and getting WildcardsAllowed as a response. If you want to see the complete ingress controller configuration, you can execute: oc get ingresscontroller default -n OpenShift-ingress-operator -o yaml More info on the subject of Ingress controllers and wildcard policies in particular can be found at: https://docs.redhat.com/en/documentation/OpenShift_container_platform/4.18/html/operator_apis/ingresscontroller-operator-OpenShift-io-v1#spec-routeadmission  Create a Kubernetes namespace for your deploymentSelect the name of the namespace that you want to use for Citrix Secure Developer Spaces deployment. It is advised not to reuse existing namespaces for deploying our platform. For this guide, we will use the name Strong Network. First, you need to check if that namespace already exists by running: kubectl get namespaces | grep strong-network If it does not exist, you can create it by running: kubectl create namespace strong-network  Run the Citrix Secure Developer Spaces installerYou can start the installation process by running the following command from the console of your device: docker run -it --rm -v ${PWD}:/strong-network/shared strongnetwork/strong_installer:2025.5.2 After this is executed and the installer image is pulled and run, you can run the installer. The installer will create a configuration file that should be used to upgrade the product in the future. It will also download the Helm chart to the shared host directory and push Citrix Secure Developer Spaces images to your (client’s) container registry. Since we provide a large number of pre-built workspace images by default, which occupy a significant amount of space, you can reduce the number of images that we pull or push by editing the list of workspace images that we will pull or push, or by not downloading them at all. This could also be useful if you (the client) plan to use your workspace images, so you do not need Citrix Secure Developer Spaces' prebuilt ones. In case you need only specific workspace images but do not want to download the rest, you can edit the workspace_images.txt file and remove the ones that you do not need. Then you can run: ./strong-cli install You will have to go through a wizard that helps you create the YAML configuration file. Enter the main domain of the platform (e.g, strong-network.example.com): sn.apps.sm8kda46je6e9a9c7b.swedencentral.aroapp.io                                                                                        The default domain used to access workspaces is: proxy.sn.apps.sm8kda46je6e9a9c7b.swedencentral.aroapp.io Do you want to change it? (y/n): n Set the email of the platform admin: admin:admin@admin.com Set password for admin (leave empty to autogenerate): Define the base64 database encryption key (leave empty to autogenerate): Define the JWT secret (leave empty to autogenerate): Do you want to enable node affinities for workspaces and strong network services? (y/n): n Use two-factor authentication for users? (y/n): n Installing version 2025.5.2... In case you get an error in the terminal like the following: Failed to fetch access token with error: permission denied, try using a service account.., This means that your domain is not registered correctly on the Citrix Secure Developer Spaces License server, and therefore, we cannot automatically generate the access token for our container registry. If you did not encounter any problems during the wizard, your whole wizard will look something like the output below. root@0f7ca7f89e78:/strong-network# ./strong-cli install Enter the main domain of the platform (e.g, strong-network.example.com): sn.apps.sm8kda46je6e9a9c7b.swedencentral.aroapp.io The default domain used to access workspaces is: proxy.sn.apps.sm8kda46je6e9a9c7b.swedencentral.aroapp.io Do you want to change it? (y/n): n Set the email of the platform admin: admin:admin@admin.com Set password for admin (leave empty to autogenerate): Define the base64 database encryption key (leave empty to autogenerate): Define the JWT secret (leave empty to autogenerate): Do you want to enable node affinities for workspaces and strong network services? (y/n): n Use two-factor authentication for users? (y/n): n Installing version 2025.5.2... ⢿ Authenticating... Done. ⣟ Finding best source registry... Selected EU region. Pulling helm chart version 2025.5.2.. File ninjahchart-2025.5.2.tgz downloaded to /strong-network/shared/ninjahchart-2025.5.2.tgz Destination Registry URL:your-docker-registry.azurecr.io Detected Azure Registry Type.. Select Type For Authentication:         [1] User Account         [2] Service Principal Please Enter Your Numeric Choice: 2 Enter service principal ID: your-docker-registry-token Enter service principal password: Authentication successful. src: europe-docker.pkg.dev/strong-network-release/images/frontend:2025.5.2 dst: http://your-docker-registry.azurecr.io/frontend:2025.5.2  Getting image source signatures Copying blob fb6ab0edbcbd done   | Copying blob ab9385d5595c done   | Copying blob 0c1c1add5fb1 done   |   … Copying blob 4f4fb700ef54 skipped: already exists  Copying config 3564c26c52 done   | Writing manifest to image destination ⢿ Finishing up... Done. Run your deployment like this:     helm upgrade --install \\             --namespace default \\             --create-namespace \\             -f "./config_2025.5.2.yaml" \\             release ./ninjahchart-2025.5.2.tgz root@0f7ca7f89e78:/strong-network# Add Docker registry secrets to KubernetesDocker images for the Citrix Secure Developer Spaces platform itself and later for workspace images need to be hosted in a suitable location. This is why you (customer) need to provide your Container registry (or Artifactory), because the Citrix Secure Developer Spaces installer is going to pull the images from Citrix Secure Developer Spaces private GCP Artifactory and push them to your registry. Citrix Secure Developer Spaces container registry is likely not going to be public, and this is why you need to authenticate with it. The credentials for this authentication must be present during the Citrix Secure Developer Spaces platform installation so that images can be pulled for each of the Citrix Secure Developer Spaces service pods. Once you have the credentials ready, you can add them by running: kubectl create secret docker-registry acr-secret   --namespace=strong-network   --docker-server=your-docker-registry.azurecr.io    --docker-username=your-docker-registry-token   --docker-password=10E+xxvXEF/CwkRa25fg/i7ZLOYAwHuX42u2DKsDu7+ACRCDjoDL This creates a new Docker registry secret named 'acr-secret' in the 'strong-network' namespace. You can verify that the secret is successfully added by executing: kubectl get secrets | grep acr-secret Now you need to manually edit the YAML configuration that you got as a result of running the Strong installer and add the following line in the platform section: imagePullSecrets: acr-secret Set proper Security Context ConstraintsSince the OpenShift security model differs from the standard Kubernetes security model, you must apply additional policies; otherwise, Citrix Secure Developer Spaces services or workspaces will not start properly. You need to set policies for both service deployments and workspaces that will be made on the platform. Set policies for the service accounts of platform servicesExecute the instructions below: oc adm policy -n strong-network add-scc-to-user privileged -z release-central-service oc adm policy -n strong-network add-scc-to-user privileged -z release-coordinator oc adm policy -n strong-network add-scc-to-user privileged -z release-proxy oc adm policy -n strong-network add-scc-to-user privileged -z release-workspace-api oc adm policy -n strong-network add-scc-to-user privileged -z release-mongodb oc adm policy -n strong-network add-scc-to-user privileged -z release-frontend oc adm policy -n strong-network add-scc-to-user privileged -z release-cron-service-account This will add the elevated permissions to all platform services’ service accounts that Citrix Secure Developer Spaces uses. The -z flag indicates that what follows is a service account name, for example, release-proxy. Create a new service account, its policy, and assign itCreate a file named workspaces-policy.yaml: apiVersion: security.OpenShift.io/v1 kind: SecurityContextConstraints metadata:   annotations:     kubernetes.io/description: anyuid provides all features of the restricted SCC       but allows users to run with any UID and any GID.   creationTimestamp: "2024-08-07T12:25:28Z"   generation: 1   name: strongnetwork-scc   resourceVersion: "998061"   uid: 434809ab-e25a-41eb-aa4b-bd918513ac1f allowHostDirVolumePlugin: false allowHostIPC: false allowHostNetwork: false allowHostPID: false allowHostPorts: false allowPrivilegeEscalation: true allowPrivilegedContainer: true allowedCapabilities: - NET_ADMIN - NET_RAW defaultAddCapabilities: null fsGroup:   type: RunAsAny groups: - system:cluster-admins priority: 10 readOnlyRootFilesystem: false requiredDropCapabilities: - MKNOD runAsUser:   type: RunAsAny seLinuxContext:   type: MustRunAs supplementalGroups:   type: RunAsAny users: [] volumes: - configMap - downwardAPI - emptyDir - persistentVolumeClaim - projected - secret Apply it by running: kubectl apply -f workspaces-policy.yaml Now you need to check if a service account named statefulset-sa already exists: oc get serviceaccounts | grep statefulset-sa Service account already existsIf the service account already exists, you can check if it is already assigned the strongnetwork-scc SecurityContextConstraints. You can do so by running: oc get rolebindings,clusterrolebindings --all-namespaces -o wide | grep statefulset-sa You can expect to get an output like the one below, which would mean that it is using a strongnetwork-scc:  If this is the case, you do not need to do any extra steps regarding the service account and its policies.  Service account does not existIf the service account does not exist, you need to create one and assign the strongnetwork-scc policy to it: oc create serviceaccount statefulset-sa -n strong-network oc adm policy add-scc-to-user strongnetwork-scc -z statefulset-sa Editing the YAML configurationFinally, you need to manually edit the YAML configuration that you got as a result of running the Strong installer. You need to add the following lines in the platform section: useOpenShift: true You need to add the following lines in the region section: clusterConfig:         privilegedWorkspaces: true         customServiceAccount: statefulset-sa Final YAMLAfter all the edits made in previous steps, the YAML configuration should look like this: mongodb:     auth:         replicaSetKey: aEKuc2XTY6MEvnW7f0avejLXsElnuTVb         password: ayYeIGVyIi1WGkhVIOoKMntXz1lHRlq4         rootPassword: a6aiZ0qW8uxh9koOammtSCt0nvU7Ofh8 platform:     imageTag: 2025.5.2     dockerRegistry: your-docker-registry.azurecr.io/     workspaceRegistry: your-docker-registry.azurecr.io/ws-images/     publicRegistryLatestTag: 2.2.5     secureBrowserTag: 2025.5.2     sidecarTag: 2025.5.2     hostName: sn.apps.sm8kda46je6e9a9c7b.swedencentral.aroapp.io     centralProxyHostname: proxy.sn.apps.sm8kda46je6e9a9c7b.swedencentral.aroapp.io     userAdminEmail: admin@admin.com     userAdminPassword: 53Vr8KEROiF!Jl&amp;E8F9I&amp;@rg5sF*G5xU     secretKeyReposB64: x94X0jttiAw2Tl74eeS3Aw==     jwtSecret: 935HcvhL3Ja8jHKUbCG2dzH355pIXyQp     sslCertificateSecret: ""     sslCertificateSecretProxy: ""     twoFaDisabled: true     useOpenShift: true     imagePullSecrets: acr-secret region:     clusterConfig:         privilegedWorkspaces: true         customServiceAccount: statefulset-sa DeploymentNow what’s left is to run the helm upgrade and deploy the application: helm upgrade --install \\             --namespace strong-network \\             -f "./config_2025.5.2.yaml" \\             release ./ninjahchart-2025.5.2.tgz The terminal will freeze for a few seconds while deployment is in progress. Now you can go to the OpenShift console and Workloads -&gt; Pods and verify that all pods are running correctly:  If there is a problem with a particular pod, click on that pod and have a look at its Logs/Events. When everything is ok with all the pods, you can proceed to log in to the platform on the URL that is defined by the hostName value in the YAML, using the userAdminEmail and userAdminPassword from the YAML. After creating the initial organization and project, you can try creating a workspace and see if it deploys correctly. If the workspace does not deploy correctly, examine the logs of the relevant Pod/StatefulSet, as explained above.]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2025_06/image.png.5fc73e4750a1bec460baf4e3e678012d.png" length="22253" type="image/png"/><pubDate>Tue, 10 Jun 2025 09:04:00 +0000</pubDate></item><item><title>Citrix&#xAE; Hypervisor 8.2 to XenServer&#xAE; 8.4 Migration</title><link>https://community.stage.citrix.com/tech-zone/build/deployment-guides/citrix-hypervisor-to-xenserver-migration/</link><description><![CDATA[OverviewCitrix Hypervisor 8.2 CU1 will reach its end of life on June 25, 2025. It was initially released in June 2020 and was the first Long-Term Service Release (LTSR) version released under the Citrix Hypervisor name. With its end of life near, migrating your Citrix Hypervisor 8.2 servers to XenServer 8.4 is necessary.  This guide provides detailed steps to ensure that Citrix Virtual Apps and Desktops™ infrastructure servers and Virtual Delivery Agent hosts running on Citrix Hypervisor 8.2 CU1 are upgraded and migrated to the XenServer 8.4 release. PrerequisitesThe following prerequisites must be met to upgrade and migrate your host servers to XenServer 8.4: All host servers must be running Citrix Hypervisor 8.2 Cumulative Update 1. All hosts should have installed the recommended Citrix Hypervisor 8.2 Cumulative 1 Update Hotfixes. Citrix Virtual Apps and Desktops v2203 LTSR or later is supported on XenServer 8.4 Partitions must use a supported partition layout. Legacy partitions are no longer supported and cannot be upgraded directly to XenServer 8.4. A virtual Windows-based Citrix License Server Resize Heartbeat SR to a minimum of 4GB Validate that the server host hardware is compatible with XenServer 8.4 XenServer Premium Edition license applied to the Citrix Hypervisor 8.2 hosts. XenServer 8.4 ISO and the latest version of XenCenter® downloaded Network location where you can access the installation ISO through HTTP, FTP, or NFS.  Note: In this guide, we will use NFS. Ensure that the NFS client is installed on the machine you are running XenCenter, and that you have the correct permissions to access the share if you use NFS.  XenCenter 2502.2.0 or later is required. XenServer VM tools on each virtual machine have been updated. Snapshot of all virtual machines on the host to be migrated to XenServer 8.4 For vGPU-enabled virtual machines, ensure the GPU is supported and the NVIDIA GRID drivers are upgraded to the latest version. Ensure all virtual machine DVD drives are empty Perform the upgrade during planned maintenance windows Please refer to the official XenServer product documentation for up-to-date requirements.  Upgrade Using XenCenterOur migration will begin by upgrading our first Citrix Hypervisor 8.2 CU1 server that hosts Citrix Virtual Apps and Desktops infrastructure components.  (Reference: Rolling Pool upgrade with XenCenter) Connect to XenCenter and select the pool master.  Select Help &gt; Rolling Pool Upgrade…  Click Next.  Select the first Citrix Hypervisor 8.2 CU1 server (pool master) to be upgraded. Click Next.  Select the upgrade mode, either Automatic (HTTP, FTP, NFS) or Manual (USB, DVD, PXE). In our case, we are hosting our ISO file on an NFS share. We select Automatic Mode and NFS from the drop-down menu, enter the NFS share path, and then click Next.  If you have any supplemental packs to install, enter the file name/location. Otherwise, click Run Pre-checks. Note: If your hosts use NVIDIA vGPU you will need to provide the NVIDIA supplemental pack at this point.  Select Resolve All to resolve any issues the pre-check has found. Once completed, click Check Again.  Review and, if satisfied, accept the existing EUA to proceed.   Click Start Upgrade.  XenCenter provides an update status as we go through the installation process on our host.  Upon completion of the upgrade process, click Finish.  The host has been upgraded to XenServer 8.4.  In addition to upgrading XenServer hosts using XenCenter, the XenServer CLI is also available for use. For the step-by-step process, please refer to the XenServer product documentation. Install XenServer UpdatesWith XenServer 8.4 now installed, you can install any updates that have been released. In XenCenter, click Tools &gt; Configure Updates.  Select the host just upgraded, the appropriate update channel, and the synchronization schedule. Click OK.  Click Yes, Synchronize.  Right-click on the host, select Updates &gt;Synchronize with update channel.  In XenCenter, click Tools &gt; Install Updates.  Select XenServer 8.4 or later. Click Next.  Select Apply updates and click Next.  Select the upgraded host and click Next.  Review the required tasks and click Next.  Review the pre-checks and click Install updates.  Updates are installed.  When updates have been installed, click Finish.]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2025_06/image.png.240f513fa2e0a0a5789ac1e6c0e449c5.png" length="89840" type="image/png"/><pubDate>Fri, 06 Jun 2025 16:45:00 +0000</pubDate></item><item><title>Deploying Secure Developer Spaces in Azure Kubernetes</title><link>https://community.stage.citrix.com/tech-zone/build/deployment-guides/deploying-citrix-secure-developer-spaces-in-azure-kubernetes/</link><description><![CDATA[OverviewThe Citrix Secure Developer Spaces™ platform provides a secure and productive cloud development environment that can be deployed on public, private clouds, and self-hosted servers. It also works in full air-gapped modes, such as high-security settings.  The primary purpose of the Secure Developer Spaces platform is to provide secure, cloud development environments (CDEs) that boost developer productivity while ensuring enterprise-level security. It allows organizations to streamline the provisioning and management of coding environments, improving efficiency and collaboration among internal and external teams. By centralizing development resources and integrating automated security features, the platform reduces the risk of data leaks and intellectual property theft, enabling safe remote work and supporting DevSecOps practices. Specifically, typical usage scenarios encompass IT efficiency, developer productivity, and governance of the development process. This deployment guide provides the steps to deploy the Secure Developer Spaces secure CDE using Azure Managed Kubernetes Service (AKS) and MongoDB Atlas. The CDEs can be accessed through cloud or local integrated development environments (IDEs), offering a secure and isolated environment for developers. This guide helps the user perform the following tasks:  Preparing for the installation Installing and configuring the Secure Developer Spaces platform Accessing and upgrading the Secure Developer Spaces workspace Onboarding end users For more information, see the Secure Developer Spaces documentation website. Additionally, the Secure Developer Spaces website’s Resources and Knowledge Center offers a variety of insightful resources on CDEs, including topics, such as DevOps integration, secure coding practices, and comparisons with other development tools.  Architecture Diagram The architectural diagram of a CDE has the following components: One Kubernetes cluster with auto-scaling node and storage Container Storage Interface(CSI) driver capacity to host the Secure Developer Spaces platform and workspace A container registry MongoDB database Code repositories, for example, Bitbucket or GitHub Optional: Additional Kubernetes clusters set up in different regions to ensure global access with optimized network latency Optional: An identity provider (SAML), such as Okta Optional: Observability Optional: Private access using Citrix® Workspace The key components of Secure Developer Spaces - the Cloud Development Environment (CDE) Platform include Kubernetes clusters, a container registry, and a MongoDB database. You can leverage resources from any cloud service provider, use your hardware in a data center, or even use a hybrid.  The following hardware specifications are required for Azure deployment: Azure Kubernetes Services for platform and regions Service node pool with two Standard_D8as_v5 VMs Workspace node pool with Standard_D16as_v5 VMs with auto-scaling Azure Container Registry Premium Tier Geo Replication peers MongoDB Atlas cluster M10 (2 GB RAM, 8 GB Storage) with auto-scaling Read-only nodes for regions If you are not using Azure, you can choose from the following alternatives: Kubernetes Cluster: Amazon Elastic Kubernetes Service Google Kubernetes Engine Container Registry Amazon Elastic Container Registry Google Container Registry PrerequisitesThe Secure Developer Spaces CDE platform deployment has the following prerequisites:  An Azure subscription for cloud infrastructure A MongoDB Atlas subscription for database management Two domain names: Regular, such as sample.com A wildcard subdomain for proxy support, such as *.proxy.example.com SSL certificates for secure communications An identity provider (SAML), such as Okta, for identity and access management Preparing for the InstallationYou can prepare for the installation by setting up the prerequisites listed in the previous section. In addition, you must perform the procedures given in this section. Creating a Managed Kubernetes ServiceAzure Kubernetes Service (AKS) is a managed Kubernetes service that you can use to deploy and manage containerized applications. Before starting, download and install the following tools: az command-line tool kubectl command-line tool Procedure To create a resource group, run the following command: az group create --name $MY_RESOURCE_GROUP_NAME --location $REGION An Azure resource group is a logical container for deploying and managing Azure resources. When creating a resource group, you are prompted to specify a location. This location is the storage site for your resource group metadata. It determines where your resources run if the Azure location is not specified during resource creation. To create an AKS cluster, use the az aks create command. The following example creates a cluster with one node that enables a system-assigned managed identity: az aks create \     --resource-group $MY_RESOURCE_GROUP_NAME \     --name $MY_AKS_CLUSTER_NAME \     --node-count 1 \     --generate-ssh-keys To manage a Kubernetes cluster, use the kubectl command to download the credentials and configure the Kubernetes CLI to use them. az aks get-credentials --resource-group $MY_RESOURCE_GROUP_NAME --name $MY_AKS_CLUSTER_NAME To verify the connection to your cluster and get a list of cluster nodes, run the following command: kubectl get nodes Creating an Azure Container RegistryAzure Container Registry (ACR) is a managed registry service based on open-source Docker Registry 2.0. You can store and manage your container images and related artifacts by creating and maintaining Azure container registries. Secure Developer Spaces fetches service images and workspace images that you uploaded from Azure Container Registry. Procedure Create an ACR instance using the az acr create command. Ensure that the registry name is unique within Azure and contains 5 to 50 lowercase alphanumeric characters. The name in the following example is mycontainerregistry. az acr create --resource-group myResourceGroup --name mycontainerregistry --sku Basic Before pushing and pulling container images, you must log in to the registry using a service principal. Run the following command to create a service principal and assign the AcrOwner role:  acr_resource_id=$(az acr show --name &lt;acr-name&gt; --query id --output tsv) az ad sp create-for-rbac --name &lt;service-principal-name&gt; --role AcrOwner --scopes ${acr_resource_id} --sdk-auth This command generates the details of the service principal, including the appId, displayName, password, and tenant. To verify the service principal, run the following command to log in to ACR: docker login &lt;acr-name&gt;.azurecr.io --username &lt;service-principal-id&gt; Password: Login Succeeded Establish an authentication mechanism between ACR and AKS using the following command: az aks update --name myAKSCluster --resource-group myResourceGroup --attach-acr &lt;acr-name&gt; You can configure the required permissions between Azure Container Registry and Azure Kubernetes Service. Creating a MongoDB ServiceThis procedure guides you through creating a MongoDB database and configuring it for use with your Kubernetes cluster. Procedure 1.        Open the database page and click Create.  2.       Configure your settings and click Create Deployment.  3.      Go to the Database Access page and add a user.  4.      Go to the Network Access page and add your Kubernetes cluster IP.   Downloading Container Images and Helm ChartThis procedure guides you in downloading container images and the Helm chart necessary for deploying the Secure Developer Spaces platform. Procedure To download container images and Helm chart, run the Docker container from Docker Hub: docker run -it --rm -v ${PWD}:/strong-network/shared  strongnetwork/strong_installer:${VERSION} Creating ConfigIf you are installing Secure Developer Spaces for the first time, you must create the config.yml file for deployment. Procedure Run the following command: ./strong-cli create-config Set up the platform by responding to the prompts based on your preferences and requirements.  The following example shows a generated config.yml file: ninja: image_tag: "latest" docker_registry: "&lt;acr-name&gt;" workspace_registry: "&lt;acr-name&gt;/ws-images/" user_admin_email: "" # Default admin@strong-network.com user_admin_password: "" # command: openssl rand -base64 16 | tr '/+' '_-' | tr -d '=' secret_key_repos_b64: "" # 16bytes base64 encoded; command: openssl rand -base64 16 proxy_host_name: "" # example: proxy.example.com host_name: "" # example: example.com ssl_certificate_secret: "" # secret for ssl certs; created using: kubectl create -n release-name secret tls helloworld-credential --key=private.key --cert=public.crt externalMongodb: hostName: "" # MongoDB Atlas host name auth: username: "" # set MongoDB Atlas user password: "" # set MongoDB Atlas password Where: image_tag: The Docker image tag of services. docker_registry: The URL of the Docker registry where services are hosted. workspace_registry: The URL of the Docker registry where workspace images are hosted. user_admin_email: The email of the administrator. user_admin_password: The password for the administrator of the platform. secret_key_repos_b64: The secret to encrypt the private keys to access the repositories. host_name: The URL of deployment (for example, company.strong-network.com). proxy_host_name: The proxy URL used to access the workspace application. ssl_certificate_secret: The Kubernetes TLS secret that contains a private key and a chain of SSL certificates.  Fetching Helm Chart and Container ImagesThis section outlines the steps to fetch the Helm chart and container images required for deploying the Secure Developer Spaces platform. Procedure Go to the /strong-network directory and run the following strong-cli Binary command: ./strong-cli install \         --source-region eu \         --domain your-domain.com \         --dst-reg-url &lt;acr-name&gt; \         --azure-service-principal-id &lt;service-principal-id&gt; \         --azure-service-principal-password &lt;service-principal-password&gt;      Where: --source-region: It is the closest region to you (us, eu, asia). If this flag is not provided, the program auto-detects the best region. --domain: The host domain. If this flag is not provided, the domain is extracted from the ./shared/config.yml file. --src-reg-key-file: The path to the Service Account Key file of the Secure Developer Spaces registry. If this flag is not provided, the program tries to auto-authenticate to the source registry. --dst-reg-url: Your destination registry URL to which you want to push images. Based on the destination registry URL, you must provide the following inputs for Azure: --azure-service-principal-id: The service principal ID of your Azure registry. --azure-service-principal-password: The service principal password of your Azure registry.  The CLI downloads the Helm chart and starts pushing images to your registry. Preinstalling NGINX Ingress ControllerEnsure you have a pre-installed NGINX Ingress controller on the Kubernetes cluster. If not, then go to the installation guide and see the installation per Cloud Provider. If the Cloud Provider is not on the list, perform the following steps. Procedure Run the following helm commands: helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx helm repo update helm install --namespace nginx --create-namespace ingress-nginx ingress-nginx/ingress-nginx It is best practice to set up this NGINX configuration to optimize the platform's performance in customer deployments.  A default ConfigMap exists in the ingress-nginx namespace, usually named ingress-nginx-controller.  kubectl edit configmap ingress-nginx-controller Add keep-alive-requests: “10000” to the ConfigMap as shown in the following command:  apiVersion: v1 data:   allow-snippet-annotations: "true"   enable-brotli: "true"   keep-alive: 120s   keep-alive-requests: "10000"   use-gzip: "true"   use-http2: "true" kind: ConfigMap Uploading the SSL SecretThe SSL secret is used in the Secure Developer Spaces ingress configuration. Perform the following step to upload your SSL secret. Procedure Run the kubectl create secret command to upload your SSL secret. See the command in the following example: kubectl create secret tls my-ssl-secret --cert=path/to/tls.crt --key=path/to/tls.key Installing the Secure Developer Spaces PlatformAfter you have followed the prerequisite procedures, you can install the Secure Developer Spaces platform. Procedure Configure and run the .yml file using Helm. Replace the values in the following example with the actual values:  helm upgrade --install my_release ninjahchart-${SN_VERSION}.tgz -f config.yml Once the deployment is complete, inspect the running services with the kubectl get all command to generate an output similar to the following example: &gt; kubectl get pods pod/release-central-service-7794bf97db     1/1 Running 0 3h24m pod/release-central-service-55f74bb5d      1/1 Running 0 3h24m pod/release-mongodb-869ddfb7c-7q58h        1/1 Running 0 3h24m pod/release-coordinator-7f9c4c95bd5fd      1/1 Running 0 3h24m pod/release-proxy-569859ddb9-748jj         1/1 Running 0 3h24m pod/release-frontend-7b4b8b97d4-67d2k      1/1 Running 0 3h24m pod/release-workspace-api-74cb48675c-9x4v8 1/1 Running 0 3h24m To log into the platform, enter the URL of your deployment in the Chrome browser. Use the admin credentials that you set in the Helm values.  You can configure the platform to connect to the service providers, such as GitHub, GitLab, GSuite, and Microsoft Teams. Configuring the Secure Developer Spaces PlatformAfter installing the Secure Developer Spaces platform, you must configure it for use. This section covers the configuration procedures. Configuring Okta as SAML Identity ProviderIn the Okta Admin menu, navigate to Applications &gt; Application. Click Create App Integration. In the modal for the sign-in method, select SAML 2.0. Enter the application name. Click Configure SAML and enter the following values in the dialog. Single Sign-On URL: Enter the URL, for example http(s)://yourdomain/saml/acs Audience URI: Enter the URI, for example: http(s)://yourdomain/saml/metadata Attribute Statements: email: (Mandatory) firstName: (Optional) If not set, the email is used as the username. lastName: (Optional) If not set, the email is used as the username.  Click Continue and Finish. The app is successfully created in Okta. To configure the Secure Developer Spaces platform to trust Okta, log in to the platform as the administrator and go to http(s)://yourdomain/platform/system_configuration/saml_sp or click System Configuration &gt; SAML Service Provider Configuration   Click Configure and upload the metadata of the SAML Identity Provider (Okta in this case) or enter a metadata URL. The SAML configuration is now complete and ready to use. The SAML configuration is now complete and ready to use. Configuring the Code RepositoryTo configure the code repository, you must set up the Secure Developer Spaces platform, configure the Bitbucket instance, and configure SSH Access.  Configuring the Secure Developer Spaces Platform Go to the Secure Developer Spaces platform settings. Open the Code Repository Applications menu. Click Add Bitbucket. Select the checkbox for Bitbucket Server or Data Center (self-hosted). Set the following fields: Bitbucket App Name: Users see it when using this Code Repository Provider. Custom Domain: Enter the URL where the Bitbucket instance is hosted. If none is given, HTTPS is chosen by default. Enforce Users to Connect: If selected, users must connect to Bitbucket before opening their workspaces. This can prevent misconfiguration and permission issues on the user side. Click the Create button to complete the configuration on the Secure Developer Spaces platform. Save the Bitbucket Server Public Key for later use. This can also be found in the edit menu after clicking the Create button. Configuring the Bitbucket InstanceGo to Administration &gt; Applications &gt; Application Links and click Create link. Enter the following information in the fields: Application Type: External application Direction: Incoming Click Continue. Set a unique name. Redirect URL: Set to https://example.com/oauth/v1/apps/callback, where example.com should be replaced with the proper domain name. Application Permissions: Set Account: Write, Repositories: Admin. Click Save. Enter strong_network for both the Client ID and Client Secret. SSH Access ConfigurationThe platform optionally allows users to connect to their workspaces using SSH. Developers can securely access the remote file system of the workspace and leverage remote IDE features in applications, such as Visual Studio Code and JetBrains Gateway. This section covers the following procedures: Configure the NGINX Load Balancer: Forward TCP requests on port 22 to allow SSH access. Enable SSH Access in the Admin Platform Configuration: Adjust settings on the admin platform configuration page to enable the SSH feature. Utilize SSH Access: Show developers how to use the SSH feature to connect to their workspaces. Configuring the NGINX Load BalancerTo create ConfigMap, switch to the namespace of the NGINX controller. By default, it is Nginx. Run the following command: kubectl create configmap ssh-mapping To edit the data field of the ConfigMap to include a mapping from the SSH port (default 22) to your release’s workspace API, run the following commands: kubectl edit configmap ssh-mapping apiVersion: v1 data:   "22": default/release-workspace-api:2222 kind: ConfigMap To edit the DeploymentApp of the NGINX Ingress Controller, run the following command: kubectl edit deployments.apps ingress-nginx-controller Add the following line to the Arguments of the controller (under the Args header): spec:   --tcp-services-configmap=$(POD_NAMESPACE)/ssh-mapping To expose Port 22 in the Service of the NGINX Ingress Controller, run the following commands: kubectl edit svc -n=nginx nginx-ingress-controller  name: ssh   port: 22   protocol: TCP   targetPort: 22 TCP requests to port 22 in the load balancer are set to automatically be redirected to port 22 on the Secure Developer Spaces workspace service. Enabling SSH Access on the PlatformLog in to the Secure Developer Spaces platform. You must have an administrator or security officer role. Go to Platform Overview &gt; Settings &gt; Workspace Settings. Scroll down to the Connect Via SSH section. Click the toggle button to enable the feature. Note: Ensure that the port for SSH connection matches the port exposed in the NGINX load balancer.  Creating Your First Secure Developer Spaces WorkspaceTo create your first Secure Developer Spaces workspace, you must create an organization and a project administrator. Creating an OrganizationClick Add New Organization. Enter the following information: Organization Name: Name of the organization Owner: Any user with the right permissions to own an organization Creating a Project AdminClick Add New Project. Enter the following information: Project Name: Name of the project Owner: Any existing user on the platform or a new user you want to onboard Installing the Secure Developer Spaces RegionThe following topics cover the procedures required to install the Secure Developer Spaces region. Creating MongoDB Read-Only NodesNavigate to the Database page and edit the database config as shown in the following image:  Enable Multi-Region and add a read-only node of the region you want to create. Then, save the change.  Creating the Kubernetes Cluster in the New RegionPerform the steps in the Creating a Managed Kubernetes Service topic. Configure NGINX Ingress Controller. For more information, see Preinstalling NGINX Ingress Controller. Preparing the Helm Chart and Container ImagesThe regional instance uses the same set of service container images as the primary region.  Use the same installer to pull the images. For more information, see Fetching Helm Charts and Container Images. Obtain the regional chart from Secure Developer Spaces support. Creating the Helm Chart ValuesTo create the Helm chart values, you need a config.yml file. Here is an example of the config.yml file: regional:   secretKeyReposB64: "" # Set the value same as primary region   jwtSecret: "" # Set the value same as primary region   regionalDomain: ""   centralHostname: "" # primary region proxy URL   centralService: "" # primary region URL   externalMongodb: # The same as primary region     hostname: ""     auth:       username: ""       password: "" Deploying the Secure Developer Spaces RegionConfigure and run the following .yml file using Helm. helm upgrade --install my_release regionchart-${SN_VERSION}.tgz -f config.yml Upgrading the Secure Developer Spaces PlatformThis section provides instructions for upgrading the Secure Developer Spaces platform to the latest version. Pulling the New Helm Chart and Container ImagesTo pull the new Helm chart and container images, perform the steps in the Preparing the Helm Chart and Container Images topic. To upgrade with Helm, run the following command. helm upgrade my_release ninjahchart-${SN_VERSION}.tgz  Note: The service restarts during the upgrade, resetting active connections and affecting users. Onboarding End UsersThe Strong Agent ensures seamless access control to source code across all working environments. Once end users are added to the workspace and assigned to project teams, they can use the environments for cloud-native and legacy Windows, Linux, and Mac development.  Cloud Native EnvironmentIn this section, you create and configure a workspace for cloud-native development using the Secure Developer Spaces platform. Creating a WorkspaceClick Create Workspace.  Select and enter the following information: Basic Info: Name Embedded Cloud IDE User Sharing Options Docker Image Image Version Resource Allocation (requires Workspaces::Manage Project permission)  Attach various project resources to your workspace. Resources must have been previously added to the project, and you might need to update access rights. You can add the following resources: Git Applications and Repositories: Connect the entire Git applications available from your platform or single repositories that have been previously imported to the project's or organization's resources. Specify a default folder location within your workspace where the Git files are cloned. Secrets: Import secrets to the workspace as files or environment variables. Choose from existing secrets or create one. Connected HTTP and SSH Services: Connect services as environment variables in the workspace. Supported and available services are part of the project's and organization's resources and depend on the platform's configuration. Startup Script: Set up a startup script to be run automatically at the workspace's boot time. This allows you to customize the software available in the container. Review your Workspace configuration and open it. Your workspace is automatically deployed. You can edit its configuration from the Overview or Workspaces pages. Accessing Your Workspace Through SSHThis topic explains how to access your workspace through SSH, allowing you to edit code directly using a local command-line editor. You must generate an SSH key pair for this process. To generate an SSH Key Pair on UNIX and UNIX-like systems, run the following command in your terminal: ssh-keygen The terminal suggests a default path and file name (for example, /home/user_name/.ssh/id_rsa). To accept the default path and file name, press Enter. To specify a different path and file name, enter those details and press Enter. Option: Enter a passphrase. It is best practice to set a passphrase for additional security against unauthorized use of your private key. If you set a passphrase, you are prompted to enter it again for confirmation. If you did not set a passphrase, press Enter. The generated SSH public and private keys are saved in the specified path. The public key file name is automatically created by appending .pub to the private key file name. For example, if the private key file is named id_rsa, the public key file is id_rsa.pub. Upload your public key to the SSH Keys section in your profile.  To enable the SSH connection to your workspace, drag the SSH access.  Authorize the workspace using your SSH key.  To connect to your workspace using a shell, navigate to the Running Actions List of your workspace Select the Connect With SSH option. This action displays the SSH command required to establish an SSH connection to your workspace.  Enter this command in your terminal.  Connecting to Your Workspace Through SSH Using Visual Studio CodeInstall the Secure Developer Spaces Remote SSH extension from the marketplace.  Sign in using the &lt; Secure Developer Spaces URL&gt;.  Open the assigned workspace to begin working in a secure environment.  Working from Windows/Linux/Mac VDASecure Developer Spaces supports proxy mode, using which you can install Strong Agents on existing Windows VDA, Linux VDA, and Mac VDA. In this mode, all source code access traffic is routed through the Strong Agent, which uses existing source credentials or keys. This eliminates the need for engineers to create personal SSH keys to access source code. Note: The following procedure applies to Windows, Linux, and the Citrix VDA for macOS. Setting up the ProfileIf you have not used the cloud-native environment, activate your Strong account with your company's Strong instance URL.  Contact your project admin to add you to a project.  Configure your profile to set up the source code access permission.   Installing the AgentInstall the Strong agent and run the Strong VDI library after installation.   Setting up the ConfigurationRun the binary and enter the Secure Developer Spaces workspace URL  Click Let’s Get Started and log into your account.  Authorize the connection to Secure Developer Spaces  Configure the environment and complete the setup.  You can now use any terminal to clone your source code and use a local IDE, such as Visual Studio or Xcode, to perform your daily tasks. Secure Developer Spaces securely and centrally manages credentials in the backend.]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2025_06/image.png.7197d84400e5082c792731addfff57dd.png" length="86733" type="image/png"/><pubDate>Thu, 05 Jun 2025 12:46:00 +0000</pubDate></item><item><title>POC Guide: eLux and Scout</title><link>https://community.stage.citrix.com/tech-zone/learn/poc-guides/elux-and-scout/</link><description><![CDATA[OverviewThis guide is designed to help IT administrators, solution architects, and technical decision-makers evaluate the eLux® Operating System, Scout Management Suite, and ELIAS (eLux Image Administration Service) through a structured and practical Proof of Concept (PoC). Whether you're looking to modernize endpoint management, improve security posture, or validate a Citrix-integrated thin client solution, this document walks you through each step needed to get hands-on with the Unicon™ stack in a controlled test environment. eLux and Scout represent a purpose-built solution for secure, scalable, and centrally managed endpoints—especially suited for digital workspaces, virtual desktop infrastructure (VDI), and hybrid environments. With this guide, you'll learn how to: Install and configure the core components Build and deploy customized OS images Manage endpoints using organizational units (OUs) Define applications and use cases aligned with enterprise needs Evaluate various deployment modes, including Desktop, Kiosk, Boot to VDI, and Boot to Apps. Each section provides practical insights, technical requirements, and best practices to help you quickly realize the value of eLux and Scout in your organization. Note: Ensure alignment with Citrix® support and consult relevant best practices for environment-specific tuning and security hardening. Solution ComponentsA successful proof of concept (PoC) begins with a clear understanding of the core components involved. The eLux OS, Scout Management Suite, and ELIAS each play a critical role in establishing a secure, scalable, and manageable endpoint environment. Below is an overview of these foundational elements and how they work together to support thin client deployments in Citrix-integrated infrastructures: eLux® Operating System A secure, lightweight Linux-based operating system designed for thin clients. It is optimized for VDI and DaaS environments, offering high reliability, minimal attack surface, and support for modern enterprise applications such as Citrix Workspace™. Scout Management Suite A centralized management platform that enables administrators to configure, monitor, and update all eLux-powered devices remotely. Scout simplifies endpoint management at scale with policy control, automation, and detailed reporting capabilities. ELIAS (eLux Image Administration Service) A tool for creating customized eLux OS images tailored to specific use cases. With ELIAS, administrators can include only the necessary software components and security certificates, thereby minimize the system footprint and improve endpoint security.   Key PoC Setup StepsThis guide walks through the following major steps to help you establish a fully functional PoC environment: Install Scout Management Suite, ELIAS, and eLux OS v7. Set up the infrastructure required for image creation and centralized device management. Create a Custom eLux OS Image Using ELIAS Build a tailored operating system image that includes only the applications and services required (e.g., Citrix Workspace App, Citrix Secure Private Access™, Google Chrome). If it’s not installed, it can’t be exploited. Deploy the eLux OS Image to Target Devices Use StickWizz to create a bootable USB installer. Alternatively, use the ISO image to deploy to virtual machines on platforms such as XenServer®, Hyper-V, VMware ESX, or VirtualBox (supported from eLux 7.25) Add Devices to Scout for Centralized Management. Register the devices with Scout to remotely manage them, apply policies, push configurations, and monitor device health. Configure Initial Device Settings and Best Practices Set up key configuration parameters such as desktop language, firmware settings, certificate deployment, and file transfer mechanisms. Define and Deploy Core Applications Preconfigure essential applications for Citrix environments, including Citrix Workspace App (with certificate integration), Google Chrome, Citrix Secure Private Access, deviceTRUST®, and uberAgent®. By following this guide, you will be able to rapidly set up a secure, efficient, and easily manageable endpoint solution ready for Citrix-based environments. This foundation will allow your team to evaluate the performance, integration, and scalability of eLux and Scout before full-scale deployment. ArchitectureScout architecture is a compact and user-friendly endpoint management solution tailored for devices operating on the eLux operating system. Its primary function is to manage endpoints connected to both the internal and external networks. For this proof-of-concept guide, it is assumed that all devices will be running on the internal network. While Scout can manage external devices regardless of their location, deploying the Scout Cloud Gateway is necessary to enable external management capabilities. The following diagram illustrates the overall architecture for the Scout Management Suite. For this Proof of Concept (PoC), Scout Server and ELIAS should be deployed on the same server.  RequirementsSome upfront preparation is needed. This section outlines the prerequisites and acquisition process for the necessary Unicon software, which is essential for the Proof of Concept (PoC) of eLux &amp; Scout. Hardware requirementsThis section outlines the hardware requirements necessary for a successful deployment of the eLux® operating system, including: The bootable USB stick used for installation. The endpoint devices that will run eLux. The server infrastructure hosting the Scout Management Suite and ELIAS. These requirements are critical to ensure optimal performance, stability, and manageability throughout your Proof of Concept. Bootable USB Stick for eLux InstallationeLux can be provisioned into a USB stick as eLux portable or eLux recovery stick. The eLux Portable is a lightweight version that runs directly from a bootable USB stick without requiring installation on the host device. Use this method in case you don't want to make any changes to the physical device during the PoC. The eLux Recovery Stick is a bootable USB device designed to install or restore the eLux operating system on thin clients or compatible x86 hardware. It is beneficial for scenarios requiring system recovery, reimaging, or deploying customized firmware configurations. In both cases, to install eLux OS on a device, a bootable USB stick must be prepared using the StickWizz tool. StickWizz version 3.5.3 or above – download here. Minimum capacity: 8 GB (recommend 16GB). Hardware requirements for eLux 7eLux 7 supports a wide range of hardware platforms, including thin clients, desktops, and laptops that meet the following minimum specifications: CPU: x86 architecture, 64-bit capable Memory: Minimum 4 GB RAM (8 GB or more recommended for smoother performance) Storage: Minimum 8 GB flash storage (16 GB or more recommended to support silent updates and caching)  Note: For production use, Unicon supports only devices listed in the official Hardware Compatibility List (HCL). Refer to the compatibility list for each specific eLux release at the myelux portal. For additional information regarding the system requirements for eLux 7, see the eLux 7 system requirements documentation. Scout &amp; ELIASThis guide will demonstrate the installation of Scout Management Suite and ELIAS version 18 on a single Windows-based server. The operating system version and configuration should align with Microsoft’s official recommendations. The following specifications are suggested as a baseline: Processor: x86, 64-bit capable, 2.0 GHz (Dual Core or higher). Memory: Minimum 16 GB RAM (more recommended for larger environments). Storage: Minimum 40 GB available disk space (excluding space required for the operating system). Operating System: Clean installation of Microsoft Windows Server 2022 and above Software requirementsAfter fulfilling the hardware requirements, it's essential to delve into the software prerequisites for a successful Proof of Concept (PoC) implementation. This involves identifying and procuring necessary software licenses, ensuring compatibility with existing systems, and potentially setting up a dedicated testing environment. Active DirectoryActive Directory is the default authentication method for Scout Board, and it serves as the reference for this guide. Ensure you have the following: One domain account credentials that will be used as the initial Scout administrator. Scout Server can access the Active Directory for authentication. NetworkTo ensure proper communication between eLux, Scout, and ELIAS, we have outlined a set of network communication requirements. The most up-to-date list of required IP addresses and ports can be found here: Network Requirements Guide. CertificatesCertain applications running on eLux devices—such as web browsers and Citrix Workspace App—often need to access internal web applications and services secured with certificates issued by an internal Certificate Authority (CA). To ensure these applications can establish trusted and secure connections, the corresponding root or intermediate CA certificates must be deployed to the device. This is critical because: Devices must be able to verify the authenticity of the certificates presented by internal services. Without the trusted CA certificates, the device will reject or flag the connection as untrusted, leading to security warnings or connection failures. This directly impacts the user experience and application functionality, particularly in environments that rely on internal portals, virtual apps, or secure web interfaces. Action Required: Certificate Deployment via Scout Board Obtain the required root and intermediate CA certificates used to sign internal web application certificates. Deploy these certificates to eLux devices using the Scout Board management interface. This will ensure seamless trust and secure access to internal resources, compliance with enterprise security policies, and a smoother, interruption-free experience for end users. Microsoft SQL database and SQL AccountYou can use a Microsoft SQL Server version with available product support as an SQL database. For more information, refer to the Requirements for the database system documentation for the supported version of SQL Server. We recommend that you create the required databases before installing Scout Enterprise Management Suite®. Important: Starting with Scout 15 2503, LocalDB is no longer supported. The SQL Server ODBC driver must be installed on the Scout Server, which looks for specific versions of the ODBC driver. Refer to the Minimum requirements for Scout Server and Scout Console documentation for the supported ODBC driver. Ensure the SQL account has the correct permissions to the database (SQL Server users and application roles). Scout Server service account - “Log on as a service” permissionDuring the Scout install process, you need to provide the domain credentials that will be used for the Scout Server service, ensuring this account has permission to log on as a service. Otherwise, the installation process will detect that the user lacks the correct permission and will terminate. To assign your user account log on as a service rights: Open the Local Security Policy editor using the Run dialog box, type "secpol.msc". Navigate to Local Policies &gt; User Rights Assignments. Open "Log on as a service".   4.      Add your user account that you want to use to install Scout. Click Apply, then OK.   5.  Close the Local Security Policy editor. IIS requirements for ELIASThere are specific requirements that must be addressed to ensure the successful execution of ELIAS18. Specifically, we need to install Microsoft Internet Information Services (IIS) 7.0 or a later version. To accomplish this, follow these steps: Open the Server Manager console by selecting Manage from the menu. Click on Add Roles and Features. On the Select Installation Type page, select Role-based or Feature-based installation. On the Select Destination Server page, select the server from the server pool and click Next. Check the Web Server (IIS) option, and a pop-up will appear. In the pop-up, click on the Add Features button. Press the Next button to proceed.   Check the .NET Framework 3.5 Features and click the Next button.   On the Role Services for Web Server Role (IIS), expand Application Development and select WebSocket Protocol. Click Next.   Check Restart the destination server automatically if required and click the Install button. Now the roles and features necessary are being installed. When finished, click the Close button.   Software downloadYou will need to download the following files from https://myelux.unicon.com: Scout: used to manage devices with eLux OS ELIAS version 18: used to create custom eLux images eLux USB Stick Image: used to create a USB Stick to install eLux 7 OS eLux software packages: download the bundle package that contains all modules for eLux 7 eLux portable: used to boot eLux 7 from a USB stick without installation  Navigate to https://myelux.unicon.com and log in to the Portal. If you are not registered, click the appropriate “Signup” button in the “New to myelux.com?” section, depending on whether you are a Partner or Customer. For new registration, fill in all the required fields of the registration form and don’t forget to check the “Privacy Policy” and the “Terms of Use” boxes if you agree to them. Enter the Captcha code and then finally click the “Create account now” button to register. Type your e-mail address and password to log in.  Once you are logged in, you will see the myelux.com Dashboard. First, download the eLux software packages bundle so that we can use them to create our installer image. Click on the following: https://myelux.unicon.com/downloads/elux-software-packages.   Next, obtain the eLux USB Stick Image. This image is necessary to generate an eLux recovery stick, which will be used to install eLux on the initial machine. Click on the following: https://myelux.unicon.com/downloads/elux-usb-stick-images.   The eLux 7 portable stick should also be tested in certain use cases. It can be downloaded here: https://myelux.unicon.com/downloads/elux-portable.   Download the Scout Management Suite. Click the following: https://myelux.unicon.com/downloads/scout. Scout -&gt; Scout -&gt; latest version   Lastly, download ELIAS18 for Windows. Click the following: https://myelux.unicon.com/downloads/scout/detail/scout-enterprise-elias. Scout -&gt; ELIAS -&gt; ELIAS 18 xxxx for Windows   Tip: You will receive an email when your account has been created. After receiving this e-mail, go to https://myelux.unicon.com and log in by pressing the “Click here to log on” button.  Scout Management SuiteScout is a user-friendly management suite for all eLux devices. Tailor settings and applications to specific End-User groups, ensuring a customized experience. There are two primary methods for managing eLux via the Scout platform: Scout Board (web version - recommended): This web-based application enables you to remotely control and monitor eLux from any location with an internet connection and a compatible web browser. Scout Console (desktop version): This is a legacy Windows application that provides a desktop interface for eLux management. While it offers similar functionality to Scout Board, it is limited to Windows operating systems and may not be supported in future updates. Both Scout Board and Scout Console offer a range of features for eLux management, including device provisioning, configuration, and troubleshooting. However, Scout Board is generally recommended for most users due to its greater flexibility and ease of use. Scout installationBefore installing the Scout software, it is essential to verify that all prerequisites are met. These requirements are comprehensively outlined in the designated section titled Software Requirements. Kindly refer to this resource to ascertain whether your system satisfies all the requisite conditions before proceeding with the Scout installation. The subsequent instructions outline the steps to install Scout and ELIAS on the same server. Access the Server that was provisioned to install Scout Management and ELIAS. Transfer and extract Scout installation files: a.       Ensure you have the Scout installation file (e.g., Scout-15.2503.zip) readily available. This file should have been downloaded previously. b.       Copy this Scout installation file to the server. c.       Once the file is on the server, extract its contents. This will typically create a new folder containing the installation wizard and related files. Run the Scout Installer: a.       Navigate to the extracted Scout folder. b.       Locate and run the ScoutInstaller.exe file. This will launch the installation wizard. Accept the License Agreement: a.       The installation wizard will present the Unicon End User License Agreement. b.       Carefully read the terms and conditions of the agreement. c.       To proceed with the installation, you must accept the agreement by checking the "I accept the agreement" checkbox. d.       Once you have accepted the agreement, click the "Next" button to continue with the installation process.  Note: The provided instructions outline the initial steps of the Scout installation process. Further steps, such as selecting installation options, configuring settings, and completing the installation, will likely be presented as you progress through the installation wizard.  Select the installation type “License Server” or “Operating mode Enterprise Subscription or Managed Service Provider”. a.       For Proof of Concept select “Operating mode Enterprise Subscription or Managed Service Provider”     The option "License Server" requires a Citrix License Server, which can be integrated with existing license server enabling centralized license management for all Citrix products. a.       Add the license server URL, the server must be reachable during this process. b.       Activation of the Scout Enterprise Suite licenses will be handled through the license server management console.  Note: The operating mode Citrix License Server is supported since the Scout version 15 2508 LTSR. After providing the hostname and the port (if needed), this license server will be used for receiving entitlements. You can continue without providing the Citrix License Server or by providing a not-yet reachable/configured one. In this case, Scout will go into a grace period, until a valid license server has been setup. The Citrix License Server can later be (re-) configured in the Scout Board UI at any time.    When using the “Operating mode Enterprise Subscription or Managed Service Provider”, this option requires a myelux user, which is a Technical Scout User that was activated for Enterprise Subscriptions. a.       A Technical Scout User is created for approved trials (to be requested via your Citrix ATS) and after activating your Unicon entitlements in Citrix MyAccount. For any questions regarding licensing, check the corresponding FAQs or contact your Citrix ATS or distribution partner. Next, you'll need to select the "User defined" option and then press Next.     Ensure ONLY the following features are selected: ELIAS must not be selected as this option pertains to the legacy ELIAS. A separate installer for ELIAS installation is provided and covered in a separate topic of this guide.     Enter your Microsoft SQL database information. To initiate the installation process, input your complete computer or domain name. Ensure that you are logged into an MS Windows account that has administrator privileges. Then, proceed by clicking the "Next" button. The installer will create the database in case you didn't select an existing one.      Configure the initial Scout administrator and device password. a.       Initial Scout user – the administrator user, which will act as the Scout Board administrator: i. When using AD provide the user using the format DOMAIN\username. ii.      When using Okta or Entra ID, enter the email associated with the user into the Identity Provider instead of the username. b.       Provide the device password – this password is used to perform elevated privilege actions on eLux devices    Configure Scout Board Web Service and authentication settings: a.       Number of the port on which the Scout Board service should run b.       Provide the hostname for the Scout Board Web Service c.       Computer name (FQDN) of the machine on which the database layer is to run d.       With HTTPS, a secure connection to the interface is used. e.       Select Active Directory as the authentication method.    Initially, a self-signed certificate is created. We will configure an individual SSL certificate instead later in this guide. Scout Keep Alive settings. Find more information regarding Scout Keep Alive here: Scout Keep Alive service.    Update the services ports as needed or use the default ports (recommended).    If all the inputted information is correct, the "Install" button will become available. Click on it to start the installation. Once the installation has been completed, the Close button will appear.  Click on it to finalize the process.   Configuring an individual HTTPS certificate for Scout BoardHTTPS certificates are essential for securing web traffic and ensuring data privacy. When setting up a Scout Board, configuring an individual HTTPS certificate can provide enhanced security and trust. Self-signed certificates are not accepted as secure by the browser. To provide the browser with a valid SSL certificate for Scout Board, perform the following steps: Use your wildcard or domain certificate. Rename the certificate file to certificate.crt and the private key file to private.key Copy both files into the Scout Board program directory - by default, this is: C:\Program Files\Unicon\Scout\Scout Board Still in the Scout Board program directory, edit the .env file and change the entry CERTIFICATE=SELFSIGN to CERTIFICATE=SIGN Restart the Scout Board service. More information can be found in the Citrix product documentation: Configuring an individual HTTPS certificate for Scout Board. Configuring AD domains for Scout BoardThe AD domain for the logon process is being used from the provided data during the installation process. If the domain changes or additional domains need to be added, these can be manually updated in the .env file located at C:\Program Files\Unicon\Scout\Scout Board. The defined domains will be selectable in the [Scout Board] Logon dialog. Example: One domain: DOMAINS=[“production.mydomain.com”] Multiple domains: DOMAINS=[“production.mydomain.com”, “test.mydomain.com”] Note: After modifying the .env file, the Windows service “[Scout Board]” needs to be restarted. More information can be found in the Citrix product documentation: Configuring AD domains for [Scout Board]. Scout Board supports additional identity providers like Okta and EntraID for authentication using SAML or OpenID. For more information on integrating these providers with Scout Board, refer to the following articles: Integrating Scout Board with Okta SAML 2.0 for Single Sign-On Installing Scout Board  Scout first configurationsScout’s initial configurations cover various setups that align with the use cases outlined in this PoC guide. Additionally, it introduces several capabilities essential for IT administrators managing eLux devices. Access the Scout Board at https://[scout server fqdn]:22160. All configurations and tasks throughout this PoC will be conducted within the Scout Board.  Diagnostic files &amp; Log levelsBefore we dive into configuring Scout and eLux, you must be familiar with the diagnostic files &amp; log levels. During a PoC, you might face issues with behavior and/or configurations. Therefore, you can request and download a set of log files from an eLux OS. To configure the Diagnostics scope and content, as well as the log level, please refer to the documentation.  For diagnostic files, we can provide you with a set of templates that you can copy and paste into your Scout environment. These templates can be added to your Scout environment using Scout Console, which is currently supported, and it’s planned to be supported in Scout Board in the future. Certificate-related templates Certificates /setup/cacerts/intcerts/*.pem /setup/cacerts/*.crt /setup/cacerts/intcerts/*.crt /setup/cacerts/scep/* /setup/cacerts/client* /setup/cacerts/wlan/* SCEP /setup/scep/wpa.conf.scep /setup/wlan/wpa.conf /setup/scep/* /usr/sbin/scepagent.sh /setup/cacerts/scep/* /setup/cacerts/client/* /setup/xsupplicant/* /setup/wlan/*   Application-related templates Chromium /setup/chromium/* /setup/chromium/managed/*.* /setup/chromium/recommended/* Cisco Secure Client /setup/cacerts/ca/* /opt/cisco/secureclient/profile/* /setup/cisco/* /tmp/DART* /tmp/ls-out* Citrix /tmp/cache/Citrix/Stores/* /setup/ica/* /var/log/* /var/log/ICAClient.log /setup/ica/AuthManConfig.xml /setup/ica/AuthManConfig.xml.template /tmp/AuthManSvr.latest  /setup/ica/module.ini  /setup/ica/wfdent.ini Firefox /tmp/elux/.mozilla/firefox/* /tmp/elux/.mozilla/firefox/*/* /tmp/elux/.mozilla/firefox/*/*/* /setup/firefox/policies/* /setup/firefox/* Microsoft AVD /tmp/*.stdout /run/user/65534/avd/**/*.rdp VMware /tmp/vmware-elux/* /tmp/vmware-elux*/* /setup/elux/.vmware/* /etc/vmware/*  OUs (Organizational unit)In the Scout Enterprise Management Suite, Organizational Units (OUs) form the core structure for managing devices, applications, and configurations. It will be used during this PoC to represent key use cases and demonstrate the level of flexibility it provides to administrators daily. OUs are displayed in a hierarchical tree view within the Scout Console, allowing administrators to organize and group resources efficiently. Each OU can contain sub-OUs, devices, and applications, providing a clear and scalable way to structure your environment. By default, settings and configurations are inherited from parent OUs down to their subordinates. This inheritance simplifies large-scale deployments by ensuring consistency across all levels. For example, when you assign a configuration or application to a top-level OU, it automatically applies to all child OUs and devices unless specified otherwise. Administrators can also override inherited settings at any level to apply custom configurations where needed. This balance between inheritance and flexibility makes the OU model ideal for managing diverse and distributed device environments. From the Scout Board: Expand the OU structure, click the menu next to Enterprise, and select New subordinate OU.   Create the following subordinate OUs, or only those that will represent the use cases to be validated during the PoC. Each device will be assigned to an organizational unit (OU) during enrollment based on its respective use case.  Refer to the following topics in the official documentation for additional information: Organizational structure and Icons in the tree view. To properly support the drag-and-drop of devices between OUs and ensure the configuration is applied to the devices, follow these steps: Navigate to the Enterprise OU and right-click. Click on Advanced device configuration. Click on Advanced file entries. Add the following configuration:  This configuration will disable kiosk mode on the device, ensuring it operates in desktop mode.    Device configurationTo access the device configuration settings for devices within a specific Organizational Unit (OU), locate the base OU in the Scout management console. Locate the Enterprise OU once found, right-click on the name or icon representing that OU. A context menu will appear, presenting various options related to the OU and its contained devices. Within this menu, locate and select the "Device configuration" option. This action will open a new window displaying the available configuration settings that can be applied to the devices within the selected OU. These settings include network configurations, display settings, peripheral configurations, security policies, and various other system parameters that can be managed centrally for all devices under that OU. Modifying these settings allows administrators to ensure consistent configurations across a group of devices, simplifying management and maintaining a standardized environment. The following topics will expose you to some of the configurations that you can apply to the devices. Security / User RightsThe administrator can limit the end-user access to several functions on the device through the Security / User rights option under device configuration.   MirroringMirroring (Shadowing) allows administrators to either view or take control of eLux user sessions. On the mirrored device, control of the mouse and keyboard can be given to the mirroring administrator. This can be very helpful in a variety of scenarios such as when administrators assist users or when administrators need to check the correct functioning of firmware updates or newly installed software. Requirements: VNC viewer - On the administrator’s system, a VNC viewer must be installed. The Scout Console provides this. VNC server - On the target device, a VNC server must be installed. For eLux devices, the VNC Server extension feature package, which is part of the XOrg eLux package, needs to be installed. This may require modifications of the image definition file on the web server via ELIAS. Please refer to VNC server extension (used for mirroring). Configuration - For the target device, in Device configuration &gt; Security &gt; Mirror settings, mirroring must be enabled and configured. For further information, refer to Configuring Mirroring.   Read-only access No input is possible. You can only see the eLux display. User must confirm within x sec You can notify the user to confirm the mirroring request Log mirror session Session information will be logged on the scout server (C:\Users\Public\Documents\Unicon\Scout\Server\mirror)   Encrypt data transmission Encrypt connection between Scout and eLux Allow from Scout only            As we are using VNC you can prevent other VNC viewer to access an eLux client   Log off on disconnection If eLux loses connection during a mirroring session, eLux will automatically log off.   User authentication   More info: Configuring user authentication (Active Directory)   Desktop languageDevice Configuration – Desktop Language preferences can be customized. Time zone and time server settings are fully configurable.   Keyboard languageDevice Configuration - Keyboard and Mouse Keyboard language settings are configurable.    Firmware settingsExample configuration for an ELIAS18 infrastructure.     Power ManagementYou might want to modify the power management settings, such as disabling Sleep mode for the test devices. When devices enter Sleep Mode, they will appear as offline in Scout Board. To change this behavior, navigate to the Power Management configuration under the device OU and define the active power management profile. Ensure that you edit the selected profile to alter the sleep mode behavior.  You can also edit the profiles settings for power management to determine the device behavior of the device on each profile.   Advanced configurationFile transferThis feature helps you transfer files to the devices. The defined files are transferred on the next device restart. You can apply a file transfer to all devices, to individual devices, or to organizational units (OUs). The source files are imported into the Scout database and, therefore, are included in an SQL database backup. For further information, see Files configured for transfer in the Scout guide. CertificatesVarious features and applications require certificates to be provided. Certificates are stored in different directories depending on the application and type of certificate. For more information, visit the  certificate documentation. For certificates on the client, note the following: Unless otherwise stated, the certificates must be Base64-encoded (ASCII) with a file name extension of .crt or .pem.  To transfer certificates to the client, use the Scout feature Files configured for transfer. For further information, see Files configured for transfer in the Scout guide. On the client, the certificates are stored in the local certificate store /setup/cacerts/ or in a sub-directory. Advanced file entriesThe Advanced file entries tab allows you to set parameters in .ini files that cannot be set by using the graphical user interface. For example, you can define additional layout parameters. Define applicationsThis section will delve into the most frequently used application types within the Scout management console, providing a comprehensive guide to their definition and configuration. Understanding these application types is crucial for effectively managing and monitoring your eLux endpoints. We will explore the specific parameters and settings associated with each type, offering practical examples and best practices to ensure accurate and efficient deployment. This detailed guidance enables administrators to leverage Scout's capabilities for fully streamlined endpoint management. To use custom icons for your application definitions, navigate to Devices &gt; Applications &gt; Application Icons in the Scout Board. The steps below outline a set of recommended applications to be configured for this Proof of Concept (PoC). These applications will later be linked to the use cases discussed in the eLux OS modes section. The configurations will be applied to the Enterprise OU, enabling child OUs to inherit the applications and their associated settings. Perform the following steps from the Scout Board, selecting the Enterprise OU. StoreFront / Citrix Workspace AppNavigate to Devices &gt; Applications and add StoreFront as an application, and configure the following settings: Set the Desktop Icon and turn the toggle on. Define the Store URL. Define login credentials.   All related information regarding Connecting to a Citrix farm can be found in the online documentation. Note: Auto-redirection of USB devices is disabled by default. To configure the auto-redirection of USB devices, update the /setup/ica/module.ini Section WFClient entry DesktopApplianceMode=True using the Device Configuration / Advanced File Entries. For more information on the Citrix Workspace App for Linux, refer to the official documentation. Browser (Google Chrome or Chromium)Navigate to Devices &gt; Applications and add Browser as an application, and configure the following settings: If you want to configure a browser application, select the Enterprise OU, navigate to Devices &gt; Applications, add Browser as an application, and configure the following settings: Choose application: Browser. Browser type: select Google Chrome or Chromium.  Note: You’ll need to add the selected browser to the ELIAS image (idf). Note that browsers are not installed by default in eLux OS. The steps to add the browser to the image will be covered later in the ELIAS section.   All related information regarding Defining a browser application can be found in the Scout Enterprise Management Suite Guide at Defining a browser application.  File Manager / XTERM / Resource InfoIf you want to configure a File Manager application, navigate to Devices &gt; Applications and add Local as an application, and configure the following settings: Local application: File Manager / XTERM / Resource Info    Configuring Use Cases (eLux OS modes)eLux offers a versatile range of operating system modes, each tailored for specific use cases. In this PoC, we will focus on four distinct eLux OS modes: eLux Desktop provides a complete and familiar desktop environment, allowing users to run a wide variety of applications locally. This mode is suitable for general-purpose computing tasks that require full desktop functionality. Boot to Apps mode streamlines the user experience by directly launching one or more predefined applications upon system startup. This is ideal for environments where users primarily interact with specific software, such as point-of-sale systems or dedicated workstations. Boot to VDI mode is designed for virtual desktop infrastructure environments, enabling thin clients to connect to remote virtual desktops seamlessly. This mode optimizes resource utilization and simplifies management by centralizing computing resources. Kiosk Mode locks down the device to run a single full screen browser application in a restricted environment, preventing users from accessing other system functions. This is commonly used for public-facing terminals, digital signage, or self-service kiosks where a controlled and focused user experience is essential. Each of these distinct operating system modes allows for flexible deployment and management of eLux-powered devices across diverse IT infrastructures.  Once the eLux devices are enrolled in their respective organizational units (OUs), they are automatically configured based on the predefined settings. Alternatively, devices can be initially enrolled under the Enterprise OU and later moved to a child OU via drag-and-drop. After the device is restarted, the new configuration will be applied and reflected. The image above illustrates how this configuration is presented from the end-user’s perspective. Now, let’s move on to configuring the OS modes. eLux desktopDefine the applications to be displayed on eLux, which can appear as desktop icons or exclusively in the start menu. Since the Citrix Workspace App and Chrome browser has already been configured at the Enterprise OU level, no further setup is required—child OUs will inherit it. However, if you need a customized configuration for Workspace App, you can re-add it under the eLux Desktop OU and apply the specific settings there. Boot to AppsSpecify the applications to be displayed on eLux. These applications can be configured to appear either as desktop icons or solely within the start menu. To configure this OS mode for the Boot for Apps OU, please follow the steps below: Right-click on Boot for Apps OU and select Device Configuration. Uncheck Use parent device configuration. Click Edit. Under User Authentication, update the following settings: a.       Authentication Type to Active Directory b.       In the Domain field list, choose whether you want to show users the specified domain so they can edit it, or if you wish to hide it. Click Apply settings.   Right-click on Boot for Apps OU and select Applications. Deselect Use all parent apps and Use parent defaults.   Add the Citrix Workspace App and configure the following settings: a.       Enable logon to Pass-through b.       Configure the Desktop Icon (optional) c.       Start automatically after x seconds is also optional   The eLux package User authentication modules must be installed on the devices. This may require modifications of the image definition file on the web server via ELIAS, which will be covered in the ELIAS chapter under the Create Custom Image.   Boot to VDIProcedure for configuring Boot to VDI Organizational Unit (OU). Navigate to and select the Boot to VDI OU. Right-click on Boot to VDI OU and select Device Configuration. Click Edit. Under User Authentication, update the following settings: a.       Authentication Type to Citrix b.       Click on the pencil c.       Add the Storefront URL and the name of the published desktop resource (VDI).   Click Apply Settings.   Kiosk modeUnder the Kiosk mode OU perform the following configuration: Navigate to and select the Kiosk mode OU. Right-click and choose "Advanced device configuration". Proceed to the "Advanced file entries" tab and add the following entry:   Create a new file kioskmode.ini or use an existing kioskmode.ini content: [Browser1]

Url=https://www.citrix.com

FriendlyName=Citrix

Default=false

Favorite=true

SaveFirstLink=false

Navbar=true

NavbarPrint=true

NavbarForward=true

NavbarBackward=true

NavbarUrl=false

NavbarRefresh=true

NavbarHome=true

Homepage=https://www.citrix.com

 

[Browser2]

Url=https://community.citrix.com/tech-zone-home/

FriendlyName=Citrix TechZone

Default=true

Favorite=true

SaveFirstLink=false

Navbar=true

NavbarPrint=true

NavbarForward=true

NavbarBackward=true

NavbarUrl=false

NavbarRefresh=true

NavbarHome=true

Homepage=https://community.citrix.com/tech-zone-home/

 

[Browser3]

Url=https://myelux.unicon.com

FriendlyName=myElux Portal

Default=false

Favorite=false

SaveFirstLink=false

Navbar=true

NavbarPrint=true

NavbarForward=true

NavbarBackward=true

NavbarUrl=false

NavbarRefresh=true

NavbarHome=true

Homepage=https://myelux.unicon.com
 5.     Proceed to the "Files" tab 6.      Upload your customized kiosmode.ini and transfer to /setup/kioskmode.ini The above configuration will lock the device in browser mode and add each website to the device’s taskbar bookmark list.  The procedure for configuring Kiosk mode Organizational Unit (OU) can also be found under Browser in kiosk mode (current eLux versions) in the online documentation. ELIASELIAS is used to build custom eLux images. Custom images enhance security and optimize image size by only incorporating necessary modules into the eLux Operating System. The web-based front-end of ELIAS version 18 is platform-independent and user-friendly, designed to simplify firmware management for administrators. It enables seamless updates of firmware images with the latest software packages, ensuring compatibility across all components. ELIAS allows you to manage the firmware of devices running eLux efficiently. Firmware images, which include the operating system and applications, can be customized using ELIAS. The tool helps create Image Definition Files (IDFs), which are structured package lists that define all software packages to be installed on a device's flash memory. Once an IDF is created, it serves as the target for firmware recovery or update operations. After a successful update or recovery using an IDF, the firmware installed on the device is guaranteed to match the configuration defined in that IDF. For simplicity, the term image will be used throughout this guide to refer to an IDF. ELIAS automatically ensures that all necessary software packages for a functional image are included, checks for dependencies, and prevents version conflicts. It can also identify and automatically request the required packages. You have the option to always use the latest version of a given software package, without compromising compatibility with other components. This section will focus on: ELIAS installation Initial Setup Set up Container for eLux 7 Import packages Create your first custom image ELIAS InstallationPerform the following steps from the same server where you previously installed Scout. Copy the ELIAS zip file and the eLux 7 software packages bundle file that you previously downloaded to this server and extract the ELIAS zip file. Open the extracted folder and run EliasInstaller.exe Read and agree to the Unicon End User License Agreement and move to the next page. Keep this on the default “Install MongoDB locally” option. Click the Next button.   Update or leave the database name as the default. Click the Next button.   Since we won’t rely on Active Directory in this guide, uncheck Allow Active Directory logon.  At the bottom, enter your desired password for the ELIAS admin account. Click the Next button to continue. Leave everything here also as default. Click Next.  If necessary, change the default installation path; otherwise, leave it as the default. Click the Install button to begin the installation, and then close the window once the installation is complete.   Binding SSL certificate to IIS configurationIf you want to enable secure communication (HTTPS) between the ELIAS server and the client requests, you need to bind a certificate in IIS (Internet Information Services). You need first to ensure the certificate is installed on the server and then associate it with the ELIAS18 website and port. This involves using the IIS Manager to configure site bindings and selecting the desired certificate as described below: Install the Certificate a.       Import the certificate (usually a .pfx or .crt file) into the server's certificate store using the IIS Manager or a tool like MMC (Microsoft Management Console). Open IIS Manager a.       Launch IIS Manager (Administrative Tools &gt; Internet Information Services Manager). Select the Website a.       In the left-hand Connections pane, expand your server and then the Sites folder. b.       Select the specific website you want to bind the certificate to. Configure Bindings a.       In the Actions pane, click "Bindings...". b.       In the Site Bindings dialog, select the binding for HTTPS (port 443). c.       If an HTTPS binding doesn't exist, click "Add" to create one. Select the Certificate a.       In the Add/Edit Site Binding dialog, select "https" from the Type dropdown. b.       Ensure the IP address is set to "All Unassigned" unless you have specific IP address requirements. c.       In the SSL certificate dropdown, select the certificate you just installed and that you want to bind. d.       Click "OK" to save the binding settings. Initial System Setup and Access ConfigurationTo start ELIAS, open a web browser and enter the URL in the following format: http(s)://&lt;host-name&gt;/elias. &lt;host-name&gt; hostname or IP address of the server where ELIAS is installed or your designated web server. &lt;path&gt; = elias - refers to the specific ELIAS application path configured during the installation process. When prompted, enter your login credentials. For local logon, the default username is admin, and the password is the one specified during the ELIAS installation. Note: The machine where ELIAS is installed will display an ELIAS shortcut icon on the desktop.   System Settings OverviewIn ELIAS 18, administrators can fine-tune system behavior by configuring key backend settings that control authentication, API connectivity, database parameters, and logging. These settings are essential for aligning the solution with your organization's infrastructure and security requirements. As a first step access the ELIAS settings through the gear icon on the top right and familiarize yourself with the settings.  Key configuration options include: Authentication Settings: Enable or disable user authentication via Active Directory (AD) and Keycloak. Specify the domain and protocol (LDAP or LDAPS) for AD integration. API Configuration: Define the connection details for the ELIAS API, including: Protocol (HTTP/HTTPS) Hostname Port API path MongoDB Settings: Set the MongoDB server hostname and authentication credentials. Optionally configure parameters like connection options and database prefix. Logging Options: Enable or disable debug-level logging for troubleshooting. Log files are saved to: C:\Program Files\ELIAS\elias.log on Windows /var/log/elias.log on Linux Note: Access to these settings requires an administrator account with global access rights. For a full breakdown of each configuration option and examples, refer to the official documentation: Citrix ELIAS 18 Settings Guide.  Authentication MethodsELIAS provides administrators with flexible authentication options to suit various deployment needs. By default, three authentication methods are available simultaneously, allowing users to select their preferred method during login. Supported authentication methods include: Active Directory (AD): Integrates with your organization's existing directory service using LDAP or LDAPS protocols. Keycloak: An identity and access management solution supporting single sign-on and federation. Local Admin Account: Intended primarily for initial setup and troubleshooting purposes. This account can be disabled once centralized authentication is in place.  Each method can be enabled or disabled based on your organization’s requirements. For detailed configuration steps and best practices, refer to the official documentation: Access Management and Logon Guide.  User Roles and Access ControlELIAS 18 provides a robust role-based access control (RBAC) system to help administrators manage permissions efficiently and securely. User roles define the scope of access, for example, global administrators can manage the entire image system, while local administrators are limited to specific containers. Additionally, access rights can be customized per user or group to control which functions within ELIAS (e.g., admin, user, info) are accessible. This layered approach ensures that users only see and interact with the components relevant to their responsibilities. Important: It’s crucial to ensure that users have UPNs defined when adding them to AD groups, as this is essential for authentication in ELIAS. For more details, refer to the official Citrix documentation on Access Roles and Access Rights.   ContainersA container contains software packages of a major eLux version. The container name can be freely chosen and can be changed later. Access the Container Library and click on the default container to be redirected to its content.   The screen changes, and you will see the Package Library on the left side and the Image Library on the right side:    Creating a containerThe installer creates an empty first container, unless you import packages. To learn how to create or rename existing containers, see the following documentation. For this guide, we will create a new container and name it UC_ELUX7.    Importing software packagesHaving successfully created the container, it is now time to import all the packages offered by Unicon to the eLux OS. At this stage, you should have already downloaded the eLux packages as previously mentioned. These packages are distributed in zip format files. Please refer to the following steps for the import process of the packages: Click the IMPORT button located at the bottom.   In the new windows, navigate to the AllPackages zip file, which you have downloaded earlier, and select Open. Make sure you are importing the packages for eLUX 7 2503+ A status bar will be displayed at the bottom reporting that the container is being updated. This means that all the eLux packages are being imported from the zip file. With this, we can create our custom image. This can take some time, so please be patient.   When finished, you will see a window with the import result. Click the X in the top right corner.   Create Custom ImageTo get started, refer to the instructional video or the official documentation for a step-by-step guide on creating a custom image. Once your image is created, apply the following common customizations. Customers frequently use these to tailor their environment: X.Org XWindows systemsThe VNC Server Extension for eLux and Scout must be deployed on eLux devices to support desktop mirroring, which allows administrators to quickly support end users. This extension enables remote screen sharing and control of endpoint devices within the eLux and Scout environments. The VNC server extension must be enabled under the X.Org XWindows system.   When enabled, it allows administrators or support teams to: Remotely view the graphical interface of a device. Interact with the desktop in real time. Provide remote assistance. Perform troubleshooting. Monitor activity — all without needing physical access to the device. How It Works: The VNC server runs on the target device (e.g., an eLux endpoint). The screen is broadcast over the network to a VNC viewer (client). An authorized administrator through the Scout Board selects a device and connects using the Mirror option and sees or controls the desktop as if they were sitting in front of it. Once configured, this capability greatly improves the manageability, support, and responsiveness of IT operations in distributed eLux and Scout deployments.   Browser (Chrome or Chromium)On the package library, locate the browser previously configured for the Kiosk Mode OU. Then, drag and drop the browser package into the Image library. The provided screenshot displays the Chromium and Google Chrome Web browser available in the package library, with Google Chrome specifically added to the image.    eLux Desktop ExtensionsScreensaver / Web camera preview / Enable uPilot    Desktop EnvironmentThe following features are optional but highly recommended during a proof of concept, as they can aid in troubleshooting scenarios: Screenshot utility Text editor Terminal (e.g. xterm) System monitor File manager programs   Citrix Workspace App for LinuxAdd the Citrix Workspace App for Linux package and enable the following options: Feature Self-service HDX Plug-n-Play USB 2.0 HDX MediaStream Windows Media Redirection HDX Browser Content Redirection HDX Microsoft Teams Optimization Browser Content Redirection (BCR) using Chromium Embedded Framework (CEF) Support Fluendo codecs in CWA-BCR Support of Web based UI dialog App Protection HDX Webcam H.264 encoder support Citrix blur camera support Desktoplock mode support deviceTRUST Client Extension   Citrix ExtensionsAdd the Citrix Extensions package, select the following options. This package is for the Citrix Workspace App (CWA) and Single Sign-On (SSO).   User authentication modulesAdd the User Authentication package and ensure that Base Files and LDAP or ADS authentication module are selected; these are required for Boot to Apps mode and the Active Directory login screen.   To finalize, scroll to the bottom and click the SOLVE button. This will validate all dependencies and ensure the image is ready for use. Once complete, click Save to finish.  eLux OSWhen working with the eLux OS in enterprise or lab environments, IT administrators can choose from several deployment options to install, recover, or run the OS on endpoint devices. The three primary methods are: eLux Portable – A live, bootable version of eLux that runs directly from a USB stick without installing anything on the host device. USB Recovery Stick – Ideal for manual installations or system recovery on individual devices. Network PXE Boot – Best suited for large-scale, automated deployments across multiple devices. ISO Images - For virtual environments, you can use the ISO format to run eLux on hypervisors like Citrix XenServer, VMware vSphere, Nutanix and others. Each method has its own strengths depending on your scale, environment, and operational preferences. For this Proof of Concept, refer to the comparison table below to determine which method best aligns with your testing needs. Recommendation: Start with eLux Portable for the easiest and quickest evaluation. It allows you to test functionality without altering existing systems, making it ideal for initial validation. To test the use cases provided in this PoC guide, install eLux on a device using the USB Recovery Stick  Feature/Use Case USB Recovery Stick PXE Boot (Network Recovery) eLux Portable (Live USB) Primary Use OS install/recovery Mass deployment via network Temporary live OS environment Requires Installation ✅ Yes ✅ Yes ❌ No Persistence of OS Changes ✅ Yes ✅ Yes ❌ No Disk encryption ✅ Yes ✅ Yes ❌ No Needs PXE/TFTP/DHCP Setup   ✅ Yes ❌ No Requires Physical Access ✅ Yes ❌ No ✅ Yes Best For Small sites/tech desks Enterprise rollouts Roaming/BYOD/evaluation Image Customization ✅ Yes ✅ Yes ✅ Yes   Installation via USB Recovery StickThe USB Recovery Stick method is a straightforward and commonly used approach for installing or reimaging eLux on individual endpoints. Before to procced with this option, you must import the ELIAS USB Recovery system X.XXX.X-X package into ELIAS web console. The USB recovery system package is available on myelux.unicon.com portal, under the eLux Software Packages section for the most recent version of eLux OS, in the Misc category.   Note: Ensure you are using StickWizz 3.5.3 or above, which is available for download here. For further information on creating an individual USB recovery stick, refer to Recovery via USB in the eLux Recovery procedure guide.   Installation via Network recovery (PXE)PXE (Preboot Execution Environment) enables eLux deployment over the network, making it an excellent option for scalable, touchless provisioning. For further information on allowing the devices to boot via LAN, refer to Network recovery (PXE) on the eLux Recovery procedure guide. eLux PortableeLux Portable is a bootable, live version of the eLux operating system that runs entirely from a USB drive without requiring any installation on the host device. It allows you to temporarily use eLux on compatible hardware, making it ideal for: BYOD or roaming scenarios Evaluating eLux on new hardware Running secure sessions without leaving traces Quick diagnosis or support situations It uses the same image structure as standard eLux, but boots in portable mode—meaning the OS and configurations load into RAM and run from the USB, leaving the internal disk untouched. For more information on how to create eLux Portable, refer to Create eLux Live Stick on the eLux Portable Guide.  Create USB ImageThe StickWizz tool creates a bootable USB drive to install the customized eLux operating system on proof-of-concept endpoint devices. The image used can be based on either the eLux Portable version (ISO file) or the USB Recovery Stick (STW file). Copy the newly created ELIAS image to your local device. This image will be used for the USB Recovery Stick scenario, where eLux is fully installed on the endpoint. Once copied, launch the StickWizz application.   Click the folder and browse to the location where you save your elux-image file. Select your image and click Open.   Insert your USB stick and ensure the correct device drive letter is selected. If needed, use the Delete option to erase all existing content on the stick.   Select Verify write process. Click Write.   The writing process begins.   When the write process completes, click OK.   Click Close.  Install Custom eLux OSInsert the bootable USB stick you just created into your device. If needed, update your device's boot order to boot from the USB stick (F8, F10, F12, ENTER depending on the device). Select Install eLux x.xxxx.x-x system on hard disk. Press Enter.   Press 1 on your keyboard to select the hard drive to install eLux. Note that this will destroy all data on the hard drive.   eLux is now being installed on your endpoint.   The endpoint will reboot automatically. After rebooting, remove the USB stick, and the initial configuration screen will appear. Enter the required network settings for your device, then click Connect.   Once the network connection has been made, enter your Scout server name or IP Address and click Next.   Choose the Enterprise OU or one of the child OUs created earlier. Click Next.   Provide a device name. Click Finish.   The endpoint device will finalize the configuration and restart.   eLux has been installed with the base applications defined in the Unicon POC OU. Repeat these steps to create additional eLux devices for this POC.  Onboarding Devices into ScoutThere are multiple methods available to onboard eLux devices into the Scout management system. For this proof of concept (PoC), we recommend starting with manual registration to establish a baseline. As testing progresses, you may explore more automated onboarding methods such as self-registration, which is especially useful in scaled or distributed environments. Manual device registration (enrollment)During the first boot of an eLux device, the First Configuration Wizard will walk the user through essential setup steps, including selecting the preferred display language, keyboard layout, and network connection. To enable centralized management through Scout, the final step involves entering the IP address or fully qualified domain name (FQDN) of the Scout Server or Scout Cloud Gateway. This connects the device to the management infrastructure, allowing administrators to apply policies and monitor the device. Self-registration of devicesScout supports self-registration of eLux devices, simplifying the onboarding process, especially in large or distributed environments. When self-registration is enabled, devices automatically register with the Scout server upon first boot, eliminating the need for manual provisioning. Key benefits include: Faster deployment and reduced administrative overhead. Automated assignment of devices to the correct Organizational Unit (OU) based on predefined rules. Optional control via secure registration keys or filters (e.g., MAC address, hostname, or hardware ID). To enable this feature, configure the Self-Registration policy in Scout Board under Device Management. You can define specific conditions and assign default organizational units (OUs) to streamline and standardize the enrollment of new devices. For complete configuration steps, see the official documentation: Self-Registration Guide. Note: Devices can also be registered manually during the first boot, where the user will be prompted to provide the IP/hostname of the Scout Server.  SummaryThe completion of this Proof of Concept provides a strong foundation for evaluating the eLux and Scout solution stack in your environment. By following the structured approach laid out in this guide, you’ve successfully installed and configured Scout Management Suite, ELIAS18, and the eLux OS across your test devices. You’ve also gained hands-on experience with essential features such as image creation, centralized configuration, secure certificate deployment, and flexible OS modes tailored to different user needs. Key benefits demonstrated throughout this PoC include: Centralized management and scalability via Scout Streamlined deployment of secure, lightweight eLux endpoints Custom image creation and firmware control with ELIAS Seamless integration with Citrix infrastructure and enterprise authentication Multiple operating modes to support diverse use cases (desktop, kiosk, VDI, app-specific) With this evaluation complete, your team is now well-positioned to assess performance, compatibility, and user experience at scale. As you transition from PoC to potential production rollout, continue leveraging Citrix and Unicon best practices to fine-tune configurations, enhance security, and optimize resource efficiency. For further guidance, reach out to your Citrix representative or consult the official documentation at https://docs.citrix.com/en-us/unicon-elux-scout.]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2025_09/image.png.bdf9345548464335c4b903d0837e8980.png" length="120591" type="image/png"/><pubDate>Thu, 05 Jun 2025 12:32:00 +0000</pubDate></item><item><title>eLux 7 2503 VM on XenServer</title><link>https://community.stage.citrix.com/tech-zone/build/deployment-guides/elux7-2503-xenserver/</link><description><![CDATA[eLux OS is a secure, lightweight, hardened operating system designed for Citrix environments. Combined with Scout, it provides a centralized solution for efficiently managing all endpoints in your enterprise. eLux enhances security, reduces operational costs, and supports hybrid and cloud-based workspaces. Ideal for VDI and DaaS scenarios, eLux ensures a reliable and consistent user experience.
 


	The eLux 7 2503 release supports eLux 7 as a virtual machine on XenServer (formerly Citrix Hypervisor), allowing Citrix administrators to deploy eLux devices for testing different eLux configurations, etc.
 


	This deployment guide provides the steps to install and configure an eLux 7 VM on XenServer.
 


	Prerequisites



	
		XenServer 8.4 or later.
	
	
		eLux 2503 
	
	
		Hardware requirements for eLux 7 
	



	Install eLux 7 2503 as XenServer VM



	
		Download the eLux7 2503 ISO Image-1 from the Unicon Products &amp; Downloads portal https://myelux.unicon.com/downloads/elux-usb-stick-images
	



	
 


	
		After downloading the ISO Image ZIP archive, extract the .iso file included in the archive to an SMB ISO library on Citrix XenServer.
	



	
 


	
		Select New VM in XenCenter. Select Ubuntu Jammy Jellyfish 22.04 VM template. Click Next.
	



	
 


	
		Enter the Name of the new virtual machine. Click Next.
	



	
 


	
		Select the eLux-7.2503.0.4.iso from the list under Install from ISO library or DVD drive. Select UEFI Boot for the Boot Mode. Click Next.
	



	
 


	
		Select a home server for the VM. Click Next.
	



	
 


	
		Select a VM group. Click Next.
	



	
 


	
		Specify the number of vCPUs, Topology, and Memory. This example allocates 2 vCPU (2 sockets with 1 core per socket) and 4GB RAM. Click Next.
	



	
 


	
		Select 16GB virtual disk (allows for silent updates). Click Next.
	



	
 


	
		Select a virtual network interface. Click Next.
	



	
 


	
		Review all the information for the virtual machine. Click Create Now.
	



	
 


	
		Select Install eLux 7 7.2503.0-4 System on hard disk from the eLux Recovery USB.
	



	
 


	
		On the eLux 7 Recovery page, press the appropriate number key to select the target medium for installation. 
	



	
 


	
		Installation of eLux 7 will progress. Do not switch off the device.
	



	
 


	
		When installation is completed, the eLux 7 VM will be powered off.
	



	
 


	
		Power on the eLux 7 VM.
	



	
 


	
		The eLux 7 VM will boot and present the Client management dialog. Enter a server name or address, or select Without management, configuration, and updates. Click Next.
	



	
 


	
		On the License Agreement, select I accept the license agreement. Click Finish.
	



	
 


	
		The installation of the eLux 7 VM is complete. The device will reboot.
	



	
 


	
 


	Summary



	This guide walked you through the installation and configuration of eLux 7 VM on XenServer.
 


	For more information, please visit the eLux product documentation.]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2025_04/image.png.899f240cd02ef3876afe136e5e7a775c.png" length="197152" type="image/png"/><pubDate>Tue, 08 Apr 2025 13:56:00 +0000</pubDate></item><item><title>Unicon</title><link>https://community.stage.citrix.com/tech-zone/by-product/unicon/</link><description>Unicon provides a Secure, Lean Operating System and Management Tool for Virtual Desktop Endpoints. With its eLux OS and Scout management system, Unicon&#x2019;s software is highly suitable for on-premises and cloud use cases, consisting of endpoints like thin clients, desktop PCs, and laptops. Deployment GuideseLux 2503 VM on XenServer -  Provides high-level steps to install and configure an eLux 7 VM on XenServer. Integrating Scout Board with Okta SAML 2.0 for Single Sign-On - Describes how to integrate Scout Board with Okta using the SAML 2.0 protocol to enable Single Sign-On (SSO) for end users  POC GuideseLux and Scout - Provides the steps to build a proof-of-concept environment to evaluate the eLux operating system and Scout Management Suite.  Tech BriefsCitrix Unicon Licensing Guide - The Licensing Guide explains the available licensing models for Citrix Unicon OS Management (Scout), their technical requirements and compatibility, and provides step-by-step instructions to help you choose and implement the model that best fits your needs.</description><pubDate>Tue, 08 Apr 2025 12:44:00 +0000</pubDate></item><item><title>VMware to XenServer Migration Guide</title><link>https://community.stage.citrix.com/tech-zone/build/deployment-guides/vmware-to-xenserver-migration-guide/</link><description><![CDATA[Overview



	Multiple scenarios and tools exist for migrating your Citrix workloads and infrastructure components from VMware to XenServer. The best combination of methods or tools will depend on what you are migrating.
 


	This guide is meant to offer high-level steps. It is not meant to be a step-by-step guide of every task. This guide should be used along-side Citrix and XenServer product documentation for full prerequisites, system requirements, planning, tasks, etc. We recommend testing the migration in a test environment prior to migrating production workloads and when migrating production workloads to start with a few VDAs.
 


	Migration tools



	
		Conversion Manager (XCM): A virtual appliance that enables users to batch convert existing VMware virtual machines into XenServer virtual machines, with comparable networking and storage connectivity.

		
			
				Migration Scenarios: Infrastructure, Dedicated VDAs, MCS golden images
			
		
	
	
		XenCenter Import Wizard (XenCenter): A feature of XenCenter that allows importing VMs from Open Virtualization Format (OVF and OVA), disk image formats (VHD, VHDX/AVHDX, and VMDK), and XenServer XVA format. Supporting VHDX/AVHDX files enables direct import of Citrix Provisioning (PVS) vDisks.
	



	Migration Scenarios: Infrastructure, Dedicated VDAs, MCS golden images, PVS golden image (of Non-Persistent VDAs)
 


	
		Citrix Image Portability Service (IPS): A Citrix Cloud service that simplifies the management of images across platforms. The Citrix Virtual Apps and Desktops REST APIs can be used to automate the administration of resources within a Citrix Virtual Apps and Desktops site.

		
			
				Migration Scenarios: Non-Persistent VDAs
			
		
	



	Citrix VDA Migration Scenarios



	Prerequisites:



	
		XenServer infrastructure is in place and has adequate capacity.
	
	
		Computer Active Directory (AD) accounts are provisioned, or you have the rights to provision the AD accounts.
	



	MCS Non-Persistent Citrix VDAs



	Build a new Machine Catalog with your existing golden images and add it to your delivery group.
 


	
		Uninstall the VMware tools on your golden image.
	
	
		Import your golden image VMs into XenServer by using XCM or XenCenter Import Wizard.
	
	
		Install the XenServer VM Tools by using XenCenter, scripting, or third-party tools.
	
	
		In Citrix Studio or Citrix Cloud:
		
			
				Create a hosting connection for your XenServer.
			
			
				Create a new Machine Catalog to provision new machines (with new AD accounts) using your new golden image and XenServer hosting connection.
			
			
				Add your newly provisioned VDAs to your existing Delivery Groups.
			
		
	
	
		Do not start your golden image on VMware after it has been imported to XenServer, as this might cause issues.
	



	MCS Persistent Citrix VDAs



	Import the existing VDAs from VMware to XenServer using XCM or XenCenter Import Wizard. This scenario requires downtime.
 


	
		Enable Maintenance Mode on the existing Machine Catalogs (or individual VDAs) from Citrix Studio.
	
	
		Uninstall the VMware tools on your VDAs.
	
	
		Shut down and import your existing VDA VMs to XenServer with XCM or XenCenter Import Wizard.
		
			
				If you have Firewall rules tied to the VDAs MAC address, select keep MAC address in XCM during the migration wizard.
			
		
	
	
		Install the XenServer VM Tools using XenCenter, scripting, or third-party tools.
	
	
		In Citrix Studio:
		
			
				Create a hosting connection for your XenServer.
			
			
				Create a new Machine Catalog and import the newly migrated VDAs.
			
			
				Add your newly provisioned VDAs to your existing Delivery Groups.
			
		
	
	
		Ensure that after the VDAs are migrated to XenServer, you do not start the VDAs on VMware.
	



	PVS Non-Persistent Citrix VDAs



	There are two common ways to manage the golden image for a PVS Machine:
 


	
		In-place updates: A vDisk is managed through the PVS versioning and updated by placing the vDisk into private mode, where updates are made before assigning it back to target VMs in standard mode.
	
	
		Out-of-band updates: This mechanism relies on a separate VM to manage a golden image, where updates are made. A new PVS vDisk is then generated from the golden image and distributed to targets.
	



	We recommend enabling the PVS-Accelerator in XenServer when using PVS-provisioned Citrix VDAs.
 


	In-place updates



	Import the PVS vDisk to XenServer using the XenCenter Import Wizard:
 


	
		Use the XenCenter Import Wizard to import your existing PVS vDisks. This will create a new VM with a new hard drive based on the PVS vDisk.
	
	
		Before booting the VM, set the has-vendor-device flag on the VM to false and the platform parameter to device_id=0002. In the host console, type the following commands:
	



	
		xe vm-param-set uuid=&lt;uuid&gt; has-vendor-device=false
	 

	
		xe vm-param-set uuid=VM uuid platform:device_id=0002
	 



	
		Install the XenServer VM Tools using XenCenter, scripting, or third-party tools.
	
	
		Log in to the VM and run the PVS Imaging Wizard to upload the image to your PVS Server.
		
			
				In the imaging wizard, ensure that the template uses the same boot method as the imported VHDX image when selecting a template. A mismatch will cause the VM to not boot up (e.g., the template uses BIOS boot, but the VHDX uses UEFI boot).
			
		
	
	
		In Citrix Studio, create a hosting connection for your XenServer.
	
	
		From the Citrix Provisioning Console, use the Citrix Virtual Apps and Desktops Setup Wizard to create a new Machine Catalog using your new PVS Image and XenServer Hosting Connection.
	
	
		Add your newly provisioned VDAs to your existing Delivery Groups in Citrix Studio.
		
			
				Do not start your golden image on VMware after it has been imported to XenServer, as this may cause issues.
			
		
	



	Out-of-band updates



	
		Uninstall the VMware tools on your golden image.
	
	
		Import your golden image VMs to XenServer by using XCM or XenCenter Import Wizard.
	
	
		Before booting the VM, set the has-vendor-device flag on the VM to false and the platform parameter to device_id=0002. In the host console, type the following commands:
	



	
		xe vm-param-set uuid=&lt;uuid&gt; has-vendor-device=false
	 

	
		xe vm-param-set uuid=VM uuid platform:device_id=0002
	 



	
		Install the XenServer VM Tools by using XenCenter, scripting, or third-party tools.
	
	
		Log into the VM and run the PVS Imaging Wizard to upload the image to your PVS Server and create a new PVS vDisk from the new golden image.
	
	
		In Citrix Studio,  create a hosting connection for your XenServer.
	
	
		From the Citrix Provisioning Console, use the Citrix Virtual Apps and Desktops Setup Wizard to create a new Machine Catalog using your new PVS Image and XenServer Hosting Connection.
	
	
		Add your newly provisioned VDAs to your existing Delivery Groups in Citrix Studio.
		
			
				Do not start your golden image on VMware after it has been imported to XenServer, as this may cause issues.
			
		
	



	Dedicated VDAs (provisioned manually or with third-party tools)



	Import the existing VDAs from VMware to XenServer using XCM or XenCenter Import Wizard. This scenario requires downtime.
 


	
		Enable Maintenance Mode on the existing Machine Catalogs (or individual VDAs) from Citrix Studio.
	
	
		Uninstall the VMware tools on your VDAs.
	
	
		Shut down and import your existing VDA VMs to XenServer with XCM or XenCenter Import Wizard.
	
	
		If you have firewall rules tied to the VDA's MAC address, select keep MAC address in XCM during the migration wizard.
	
	
		Install the XenServer VM Tools using XenCenter, scripting, or third-party tools.
	
	
		In Citrix Studio:
		
			
				Create a hosting connection for your XenServer.
			
			
				Create a new Machine Catalog and import the newly migrated VDAs.
			
			
				Add your newly provisioned VDAs to your existing Delivery Groups.
			
		
	
	
		Make sure that after the VDAs are migrated to XenServer, you do not start the VDAs on VMware.
	



	Migrating Citrix infrastructure



	This offers high-level guidance. Refer to the Citrix documentation for complete considerations.
 


	
		Citrix DaaS: Create VMs on XenServer for each Cloud Connector you require and install the Citrix Cloud Connector software.
	
	
		Citrix Virtual Apps and Desktops: Create VMs on XenServer for each Delivery Controller you require. Install the Citrix Delivery Controller component on the VMs and join them to your existing Site.
		
			
				Refer to the Citrix Documentation on Delivery Controllers: Delivery Controllers - Citrix Virtual Apps and Desktops 7 2311.
			
		
	
	
		Storefront: Create VMs on XenServer for each Storefront server you require. Install the Storefront on the VMs and join them to your existing Storefront Server Group.
		
			
				Refer to the Citrix Documentation on Citrix Storefront: Install, set up, upgrade, and uninstall - StoreFront 2402
			
		
	
	
		SQL Database: Multiple options exist for moving your SQL Database hosting your Citrix configuration. Refer to Citrix and Microsoft documentation.
	



	Here are a few articles for guidance:
 


	
		Databases, Citrix Product Documentation: Databases - Citrix Virtual Apps and Desktops 7 2311
	
	
		Migrate Citrix Virtual Apps and Desktop databases to a new SQL server
	



	
		NetScaler: Migrate the configuration of an existing NetScaler appliance to another NetScaler appliance
	



	Other considerations



	vTPM: If you require a VM to be imported to have a TPM attached, this can be added after the import of the VM through XenCenter.
 


	AD Accounts: As part of this process, you may need to provision additional AD computer accounts, especially for MCS non-persistent.
 


	Disk Storage Consumption: When migrating MCS thin-provisioned disks for persistent VDAs, the storage needed may be larger than expected. Each VM that is migrated will result in a full copy of all of the VM-attached disks (including the content of the common parent image). The exact storage use will vary depending on the storage used and the number of updates each VM makes to the common image.
 


	Sysprepping: If you are managing the golden image in VMware simultaneously with the golden image that you have copied to XenServer, you should sysprep your golden image in XenServer.
 


	Rebuild golden image: For Non-Persistent Machine Catalogs, you can consider rebuilding your golden image from scratch.
 


	Deprovisioning VMware environment: Once testing is complete, you can remove your old VDAs from your Delivery Group and your VMware infrastructure.
 


	
		
	



	
		Note:
	 

	
		This article was produced in collaboration with Ferroque Systems.]]></description><pubDate>Wed, 02 Apr 2025 13:00:00 +0000</pubDate></item><item><title>XenServer</title><link>https://community.stage.citrix.com/tech-zone/by-product/xenserver/</link><description>Reference ArchitecturesXenServer for Citrix Workloads -  Provides a blueprint for deploying XenServer to run Citrix workloads suitable for enterprise-sized deployments that can scale from a few hundred to a few thousand VDAs. Deployment GuidesVMware to XenServer Migration Guide -  Provides high-level steps and tools for migrating Citrix workloads and infrastructure components from VMware to XenServer. Citrix Hypervisor 8.2 CU1 to XenServer 8.4 Migration Guide - End of life for Citrix Hypervisor is June 25, 2025. This deployment guide provides the steps to migrate your Citrix Hypervisor 8.2 CU1 hosts to XenServer 8.4. Tech PapersSecurity Recommendations When Deploying XenServer -  Provides security recommendations for deploying XenServer, covering best practices for protecting networks, storage, virtual machines, and the control domain and guidance on configuration, auditing, and managing XenServer environments.</description><pubDate>Wed, 02 Apr 2025 13:00:00 +0000</pubDate></item><item><title>XenServer for Citrix Workloads</title><link>https://community.stage.citrix.com/tech-zone/design/reference-architectures/xenserver-for-citrix-workloads/</link><description>Overview



	This document serves as a blueprint for deploying XenServer to run Citrix workloads for the most common commercial-sized deployment that can scale from a few hundred to a few thousand VDAs. Whether using Citrix Virtual Apps and Desktops or Citrix DaaS, this reference architecture is valid. Enterprise-sized deployments may have additional considerations that are not covered in this reference architecture. Use XenServer product documentation alongside this document.
 


	Blueprint



	Host and resource pool layer



	
		XenServer hosts should be part of a resource pool with a recommended maximum of 16 hosts for this deployment type.
	
	
		XenServer hosts in the same resource pool must have the same vendor, model, and features as the CPUs and the same amount of memory.
	
	
		Memory should not be overcommitted. You need as much memory in a host as the VMs have allocated.
	
	
		See the Citrix Provisioning workloads section for local storage requirements and host memory considerations.
	



	Network layer



	
		XenServer hosts should have network card speeds of 10 Gbps or greater.
	
	
		XenServer hosts should have at least four network cards: 2 bonded pairs with 1 pair dedicated to storage traffic and one used for the VM and management traffic.
	
	
		Where there are insufficient network cards to provide physical separation between VM and management traffic, you must ensure that logical separation is provided using VLANs so that management traffic (XenServer control domains, license servers etc.) are on a different logical network to guest VMs such as CVAD desktop or server VDAs.
	



	Storage layer



	
		Shared storage is recommended to ensure VMs can be migrated between hosts.
	
	
		NFS or SMB is recommended when using Machine Creation Service (MCS).
	
	
		Any supported storage option works when using Citrix Provisioning.
	
	
		Isolate storage networking traffic as outlined in the Network Layer section.
	



	Citrix Image Provisioning layer



	Citrix Machine Creation Services (MCS) and Citrix Provisioning Services can be used separately or in combination to provision VDAs to XenServer.
 


	Citrix Provisioning workloads



	If using Citrix Provisioning, we recommend enabling the XenServer feature PVS-Accelerator.
 


	
		5 GB of cache on each host is recommended per vDisk version you actively use.
	
	
		Memory cache is recommended instead of disk cache, if enough memory is available.
		
			
				If using disk cache, local storage is recommended.
			
		
	



	Machine Creation Services workloads



	If using Citrix Machine Creation Services, we recommend that you use both Intellicache and storage read caching.
 


	Intellicache:
 


	
		Enable Intellicache during the installation of XenServer by selecting &#x201C;Enable thin provisioning (Optimized storage for Virtual Desktops)&#x201D;.
	
	
		Intellicache uses local storage for the cache.
	
	
		XenServer hosts should have enterprise-grade SSDs or NVME drives that support 512-byte sectors (or can emulate 512-byte sectors).
	
	
		NFS or SMB shared storage is recommended, as the VDAs require it to allow a fully thin-provisioned solution with IntelliCache.
	
	
		When creating the Hosting Connection from Citrix, select the IntelliCache option.
	



	Storage read caching:
 


	
		On each XenServer host, increase Dom0 memory by 10 GB to provide space.
	



	Design decisions



	This section details the reasons for the blueprint configuration and other potential configuration options.
 


	Host and resource pool layer



	
		While XenServer can support a resource pool with up to 64 hosts, limiting the resource pool to 16 hosts ensures that the time needed to perform updates (even when host reboots are required) is achievable within a working day. Additionally, there is an increased resilience to failures, and the impact of failure (if one occurs) is constrained to this set of hosts.
	
	
		When allocating VMs to XenServer resource pools, ensure that there is enough capacity to operate all VMs with at least 1 host unavailable. This allows for pool maintenance without requiring VM outages.
	
	
		If XenServer hosts in the same Resource Pool have different amounts of memory, the XenServer host with the smallest amount of memory must be able to support workloads placed on it during failover scenarios or upgrades.
	
	
		XenServer hosts within the same resource pool should be on the same network, in the same datacenter or physical location, and only separated by a L2 switch (not a router).
	
	
		Create a separate resource pool for each set of XenServer hosts on a different network or physical location.
	
	
		XenServer HA is not recommended for Citrix workloads/VDAs.
		
			
				Protection at the VM level is not typically required due to the inherent manner in which Citrix Virtual Apps and Desktops workloads are dynamically created and destroyed
			
			
				HA in a Citrix Virtual Apps and Desktops deployment can be beneficial in handling hardware failure or hypervisor crashes. However, when HA is enabled, there is an increased risk of any temporary interruptions (in network or storage infrastructure) causing a host to &#x2018;fence&#x2019; for safety, interrupting services (for end users) that otherwise might not have occurred.
			
		
	
	
		If possible, splitting VDAs over multiple pools ensures for availability in case of a pool failure.
	
	
		The total number of vCPUs allocated to an individual VM on a host should not exceed the number of physical CPU threads for the host.
	



	Network layer



	Other network card options for your hosts:
 


	
		6 Network cards: Three bonded pairs with one pair dedicated to storage traffic, 1 pair dedicated to VM traffic, and one pair dedicated to management traffic.
	
	
		3 network cards: One is dedicated to storage traffic, one to VM traffic, and one to management traffic.
	
	
		2 network cards: One network card is dedicated to the storage traffic, and one network card is used for the VM and management traffic. As described above, you must ensure that logical network separation is provided using VLANs.
	



	Citrix Provisioning Layer



	
		Minimize the number of different golden images used in each resource pool to maximize the use of the available caching technologies.
	



	Each image uses the caches. The more golden images there are, the more likely the caches are to become full and less effective. Making the caches bigger may also help with this aspect as the number of golden images increases.
 


	Intellicache



	If you are using block-based storage with Intellicache, we recommend that you use full provision (LVM) mode. This model is compatible with IntelliCache (which will enable faster VM operation with older/slower storage devices). Some block storage filers provide thin provisioning, which can be used, but care must be taken to avoid out-of-space conditions.
 


	Reference materials



	
		XenServer Product Documentation: https://docs.xenserver.com/en-us/xenserver/8/
	
	
		XenServer Technical Overview: https://docs.xenserver.com/en-us/xenserver/8/technical-overview
	
	
		Getting Started with XenCenter: https://docs.xenserver.com/en-us/xencenter/current-release/intro-welcome</description><pubDate>Wed, 02 Apr 2025 13:00:00 +0000</pubDate></item><item><title>Security Recommendations When Deploying XenServer</title><link>https://community.stage.citrix.com/tech-zone/build/tech-papers/xenserver-security-recommendations/</link><description><![CDATA[Overview



	This tech paper helps you design security for a virtualized XenServer environment. It includes general best practices as well as information about the following:
 


	
		Protecting XenServer networks and storage
	
	
		Installing and deploying XenServer securely
	
	
		Configuring virtual machines
	
	
		Securing virtualized storage
	



	The recommendations and guidance in this document are not intended to be exhaustive. Unless needed for clarity, this document does not provide detailed step-by-step procedures.
 


	Applicable Versions



	This guide applies to the following versions of XenServer:
 


	
		XenServer 8.4
	



	Audience



	Before reading this guide, you should have a basic knowledge of security, XenServer, and physical networking.
 


	This guide has several audiences:
 


	
		Security specialists
	
	
		Systems architects
	
	
		Administrators
	



	This tech paper assumes that you are familiar with basic XenServer concepts, including XenServer installation, XenCenter, resource pools, networking, and the pool coordinator (formerly pool master). You should also ensure that you are familiar with the release notes for the version of XenServer that you install.
 


	Finding Configuration Instructions



	You can find configuration instructions in the following locations:
 


	
		XenServer product documentation. The XenServer 8.4 product documentation provides overview information and command-line based instructions.
	
	
		XenCenter product documentation. The XenCenter documentation provides UI-based step-by-step instructions using the XenCenter administration console. Users who are not comfortable with the XenServer xe commands may prefer this option.
	



	Terminology



	
		Guest Network: Guest networks carry VM traffic — the network traffic that originates or terminates from a virtual machine. These networks may also be referred to as VM networks.
	
	
		Management Interface: The management interface is a NIC (or a VLAN on a NIC) assigned an IP address that XenServer uses for its management network, including, but not limited to, traffic between hosts, between a host and Workload Balancing, and for live migration. This is also the IP address to which management clients such as XenCenter will connect.
	
	
		Hostile Traffic: Any network traffic that could potentially violate the confidentiality, integrity, or availability of your network or its associated systems.
	
	
		Control Domain: A special-purpose domain (VM instance), based on a Linux kernel, that exists in a single instance on each XenServer host. The control domain is usually the only privileged domain (meaning that it can use privileged hypervisor calls, for example to map physical memory into and out of domains) on a XenServer host, and is thus the only domain that can control access to physical input/output resources directly and access the content of other domains (that is, Domain U). The control domain is also known as Domain 0 or “dom0”.
	
	
		PV Drivers: Drivers in a guest that accelerate storage and network data paths. These are treated as part of the guest operating system, use unprivileged XenServer interfaces, and are not involved in implementing XenServer security functions.
	
	
		The management API: This is the API for managing XenServer environments (that is, for remotely configuring and controlling domains running on hosts in a XenServer pool). It is sometimes referred to as “XenAPI.”
	



	Introduction



	The security requirements for your virtual environment depend on your organization’s risk appetite and external factors such as regulation and compliance needs.
 


	These requirements also depend on where your virtual environment is placed: within a co-location facility, within a private cloud, or hosted within your corporate network. Differing locations present different risk profiles. For example, a co-lo facility may present risks from other tenants but may be better resourced to provide dedicated, round-the-clock security monitoring. In contrast, internal threats may not be as significant in many enterprise environments. Consequently, efforts to secure internal guest networks might be a lower priority.
 


	Many of the same security principles and techniques that apply to physical environments also apply to virtual ones. Since the hypervisor is protecting every VM from every other VM, a hypervisor must be designed with security from the ground up.
 


	Approach



	When planning a hypervisor environment, you need to focus mainly on security design. This is because you have to protect not only the virtualization layer itself but also the virtual machines running above it. Both layers may be complex and large-scale, with differing lifecycles and administrators. You need to meet your security expectations, governance, risk management, and compliance (GRC) needs and those of the VMs running on your infrastructure. Many VM types, usages, and policies exacerbate the challenge.
 


	In many deployments, different parts of the organization share services and may share computing resources. Separation between these groups must usually be achieved virtually rather than physically. This use of virtualization should be secure by design.
 


	
		Recommendation:
	 

	
		We recommend that you clearly understand and document:
	 

	
		
			The traffic flow between different components, including hypervisors, switches, and guest traffic.
		
		
			All components in the environment, including hypervisors, workloads, networks, management consoles, and physical hardware components.
		
		
			Communications and data flow between hosts.
		
		
			Any other potential communication channels for components, whether enabled or not.
		
		
			What is expected not to change and what is expected to be reconfigured as differing organizational groups join, expand, shrink, or leave?
		
		
			RBAC roles and the individuals to whom they are assigned.
		
		
			Any removable hardware components, such as USB and disk drives.
		
		
			The details, including assigned IP addresses, of management interfaces and how they are physically configured for network connectivity (switches, ports, and so on).
		
	



	 
 


	Planning Your Virtual Environment



	Your virtual environment is structured as three layers, as shown in the diagram that follows:
 


	
		Network layer, also including storage
	
	
		Hypervisor (host) layer
	
	
		Virtual machine (VM) layer
	



	This document provides security guidance in corresponding chapters following this introductory chapter.
 


	
 


	This illustration shows the different layers that people can secure in virtual environments, including the virtual machine layer, the hypervisor layer, and the network layer. The network layer, represented in gray, effectively straddles the hypervisor and virtual machine layers since there are networking aspects in all of these layers.
 


	Many of the standard ways to secure physical environments apply to virtual environments. For example, it is possible to secure the virtual machine layer the same way you normally secure an operating system, such as by hardening Microsoft Windows.
 


	
		Recommendation:
	 

	
		We recommend that you secure guest operating systems according to the standard practice of your organization’s security policy.
	 

	
		We recommend that you secure networks according to your organization’s security policy. In particular, protect networking passwords and configure digital certificates accordingly.
	 

	
		We recommend that you manage access to the XenServer control domain carefully. The control domain provides administrative access to a pool and the VMs executing on it. The following chapters will provide you with recommendations on how to control users and isolate networks to restrict access.
	 



	 
 


	Protecting XenServer Networks and Storage



	In a XenServer environment, networking occurs not only on the physical level but also on the logical level. From a network Layer 2 perspective, XenServer functions as a virtual network switch that can send traffic internally within the host (such as for private networks) and externally to the physical network.
 


	Consequently, it is important that the XenServer environment is secured at all its levels: the virtual machine (guest) layer, the hypervisor layer, and the physical network layer, including storage networks.
 


	
		Recommendation:
	 

	
		We recommend that you ensure that no data crosses between networks, including the logical networks in the hypervisor layer.
	 



	 
 


	Network Traffic Separation



	XenServer has three distinct categories of network traffic in each pool: (a) management traffic, (b) storage traffic, and (c) VM (guest operating system) traffic. You can configure your XenServer hosts to send this traffic over separate or shared networks.
 


	The following illustration provides an example of this separation, where different classes of traffic use different physical NICs.
 


	
 


	This illustration shows how NICs that are not designated for management or storage traffic only carry VM traffic.
 


	
		Management Traffic: XenServer uses the management network for XenServer management communications, including traffic between hosts, live migration, VM import and export, and resource pool metadata backups. The management network is the network connected to the NIC that is configured as the management interface. Management traffic does not include communications between guest virtual machines but does include traffic involving XenCenter, the administration console.
	



	The XenServer installation creates the management network on each host and assigns it to a specific NIC that will function as the management interface. XenServer allows you to use a single interface for all three types of traffic: management, storage, and guest. However, this is not the most secure networking configuration, and we strongly recommend separating traffic as described throughout this section.
 


	
		Storage Traffic: The term storage traffic refers to Fibre Channel traffic and IP-based storage traffic, such as iSCSI software initiator traffic, NFS traffic, and SMBv3 traffic. However, when this guide discusses placing storage traffic on its own network this refers explicitly to iSCSI and NFS traffic. (Fibre Channel traffic is inherently on its own network.) Storage traffic is discussed in more detail throughout this section and in Protecting Virtualized Storage.
	
	
		Guest Traffic: Guest traffic is sent to or from guest virtual machines.
	



	Physical network separation provides additional security. The following illustration provides an example.
 


	
 


	This illustration shows the management interface, the NIC used for management communications, and the management network, which is separated from the storage network and the guest network.
 


	
		Recommendation:
	 

	
		We strongly recommend physically separating the management, storage, and guest networks through NICs, cables, switches, ports, and network configuration as well as logically separating through configuration in XenServer. This includes ensuring that you do not unintentionally send guest traffic across the management network, as explained in the following section.
	 



	Ensuring Traffic is Separated



	To understand how to separate traffic, it is essential to know how VMs communicate.
 


	A VM can only use a NIC for guest traffic if it has a virtual network interface on the same network as the NIC.
 


	Not all NICs have virtual network interfaces associated with them. If you do not configure a virtual network interface connecting to the management network, the management NIC becomes dedicated to management traffic. For example, in the previous illustration, NICs are connected to the management and storage networks that do not have corresponding virtual network interfaces.
 


	The previous illustration depicts VMs with only virtual network interfaces for the guest network. The VMs are not connected to the management or storage networks; they do not have virtual network interfaces that connect to those networks.
 


	
		Recommendation:
	 

	
		We recommend isolating production networks from staging, testing, and development networks.
	 



	 
 


	Planning a Separate Storage Network



	
		Recommendation:
	 

	
		We recommend segregating traffic by configuring an additional storage interface(s) for IP-based storage traffic and configuring XenServer to access storage through that interface. (However, you must also physically isolate the guest networks and configure virtual machines only to use those isolated networks). Note that additional interfaces are also known as secondary interfaces in some literature.
	 



	 
 


	The overall process for creating a separate storage network is as follows:
 


	
		Configuring physical network infrastructure so that traffic is on different subnets.
	
	
		Creating a storage interface to use the new network.
	



	If desired, consider configuring redundancy, multipathing, or bonding during the above steps.
 


	Following the above process lets you establish separate networks for IP-based traffic provided that:
 


	
		You do not configure XenServer to use this network for any other purpose (for example, by connecting a virtual network interface to this network).
	
	
		The appropriate physical network configuration is in place.
	



	For example, to dedicate a NIC to storage traffic, the NIC, storage target, switch, and/or VLAN must be configured so that the target is only accessible over the assigned NIC.
 


	The storage network must be on a different subnet to separate the storage traffic from the management traffic. The subnet for storage must be a separate IP subnet that is not “routable” from the management interface. If the physical or logical configuration does not enforce the traffic separation, XenServer may direct storage traffic over the management interface after a host reboot, due to the order in which XenServer initializes NICs.
 


	Planning a Separate Guest Network



	
		Recommendation:
	 

	
		We recommend that you configure all VMs to have guest networks only. As explained below, VMs do not require access to storage and management networks.
	 

	
		We recommend that you configure your guest networks correctly so they do not unintentionally send traffic over the management and storage networks:
	 

	
		Ensure the guest network is physically isolated from management and storage traffic. Connect the NIC that the VMs will use for guest traffic to a switch port that will be used for VM traffic only. This port must be physically isolated from the storage and management networks.
	 

	
		Create a virtual network interface on the same network(s) as the NIC.
	 

	
		Do not route any traffic besides guest traffic across that NIC.
	 

	
		If you configured the management interface on NIC 0 when you created VMs, make sure that NIC 0 is not enabled in the VM wizard when you create the VM, as explained in the procedure that follows.
	 

	
		Alternative: When hosts only have one NIC dedicated to guest traffic, we recommend separating traffic into logical networks. For example, a common configuration is for an application server to manage “front-end” traffic (for example, to a web server) and “back-end” traffic (for example, to a database). This can be done using VLANs. A VLAN tags the Ethernet traffic separately, but traffic still goes over the same physical NIC on the host.
	 

	
		We recommend not assigning the control domain an IP address for any network other than storage networks and the isolated management network. Do not place guest VM traffic on any network with a control domain IP address.
	 

	
		We recommend that if you have a guest network that connects to a network that is not under your organizational control (such as the Internet), you make sure the local segment of the network is protected. Any unprotected network may be vulnerable to Level 2 network attacks, such as fake DHCP attacks, that must be performed from the same switch as the attack’s target.
	 

	
		We recommend that if you have some VMs that require Internet access and some that do not, you configure separate networks so that the VMs that do not require Internet access are not directly exposed to possible attacks from the Internet.
	 



	 
 


	To configure VMs with guest networks only



	
		Note:
	 

	
		This procedure may be easier to perform if you change the networks' names to assign them clear names, as described in Naming Networks Clearly.
	 



	 
 


	
		After launching the XenCenter New VM wizard (VM &gt; New VM), follow the prompts until you reach the Networking page.
	
	
		On the Networking page, remove all networks the VM will not use. Select the network to remove and click Delete.
	



	
 


	By default, XenServer installation configures the management interface on Network 0, which is typically NIC0.
 


	The resulting networks may look like the following:
 


	
 


	
		If necessary, click Properties to configure QoS settings or Virtual MAC addresses.
	



	
		Important:
	 

	
		Any virtual network interfaces that appear in this dialog box will be created as networks on the virtual machine.
	 



	 
 


	Naming Networks Clearly



	
		Recommendation:
	 

	
		We recommend that you rename the default networks so they have clear, meaningful names that are relevant to your configuration. This will prevent inadvertently connecting a guest to a network to which it should not have access, such as the management network.
	 



	 
 


	For example, change the default names XenServer assigns to NICs to ones that represent the functions they perform in your environment:
 


	
		
			
				
					NIC
				 
			
			
				
					Network
				 
			
			
				
					Name
				 
			
		
		
			
				
					0
				 
			
			
				
					Network 0
				 
			
			
				
					Management Network
				 
			
		
		
			
				
					1
				 
			
			
				
					Network 1
				 
			
			
				
					Storage Network
				 
			
		
		
			
				
					2
				 
			
			
				
					Network 2
				 
			
			
				
					Internal Private Network
				 
			
		
		
			
				
					3
				 
			
			
				
					Network 3
				 
			
			
				
					External Guest Network
				 
			
		
	



	 
 


	To change the names of networks



	In the Networking tab in XenCenter, select the network and click Properties, as shown in the following screen capture:
 


	
 


	This screen capture displays the network names, which were changed from their default names for clarity. You can also add information about the network to the description column.
 


	You can also change network names by running the following command:
 


	
		# xe network-param-set uuid=&lt;network uuid&gt; name-label="&lt;new network name&gt;"
	 



	 For example, to assign Network 1 the name “Storage Network” run:
 


	
		# xe network-param-set uuid=213e396e-1e1f-0744-7a43-64c3e9dfd649 \ name-label="Storage Network"
	 



	 Using the name-label parameter is the same as renaming the network in XenCenter.
 


	
		Note:
	 

	
		To obtain the network UUID, run xe network-list.
	 



	 
 


	Protecting the Management Network



	Protecting the management network is vital because this network can potentially provide access to all components connected to a host. Communication between XenServer 8.4 hosts and XenCenter and other XenServer 8.4 hosts is over (encrypted) TLS connections to port 443. However, XenServer management functions listen on port 80 (unencrypted) and port 443 (TLS encrypted) to manage API requests. This allows interoperability with older versions of the product and other (e.g., third-party) products.
 


	
		Recommendation:
	 

	
		Unless you have third-party management API agents that cannot use TLS, we recommend blocking access to port 80 with the CLI command: xe pool-param-set uuid=&lt;pool uuid&gt; https-only=true.
	 

	
		If you need to reverse this command, for example, if you need to migrate VMs to a XenServer 8.4 host from an earlier version of the product, you can do so with the CLI command: xe pool-param-set uuid=&lt;pool uuid&gt; https-only=false.
	 



	 
 


	It is possible to configure the management interface without an IP address bound to it. However, this means that none of the management functions will work from outside the local console on the XenServer host. Be aware that in this configuration, you cannot create resource pools, import/export VMs, or otherwise take advantage of features such as email alerting.
 


	Configuring Switch Port Locking



	In environments with untrusted guest VMs, security measures, such as spoofing protection, may be desirable to reduce VMs' ability to attack other virtual machines over the network.
 


	
		Recommendation:
	 

	
		
			We recommend that environments with potentially hostile or unknown guest traffic configure the XenServer switch-port locking feature:
		
		
			We recommend using the switch-port locking feature to define specific IP addresses from which an individual VM can send traffic.
		
		
			The XenServer switch-port locking feature lets you control traffic being sent from unknown, untrusted, or potentially hostile VMs by limiting their ability to pretend they have a MAC or IP address that was not assigned to them.
		
		
			We recommend that environments with both (a) VMs containing sensitive data and (b) untrusted VMs use the switch-port locking feature to prevent an untrusted VM from sending spoofed traffic to sensitive VMs.
		
	



	 
 


	The XenServer switch-port locking feature may assist deployments with a network architecture in which each VM has a public, Internet-connected IP address. For more information about switch port locking, see the product documentation.
 


	Securing the Physical Network



	
		Recommendation:
	 

	
		We recommend that you do not inadvertently cable the management interface into the guest network. For example:
	 

	
		We recommend consistently designating the same NICs for use with the same networks across all pool hosts.
	 

	
		In environments with multiple pools, we recommend always designating specific ports for specific types of traffic and using these ports consistently. For example, across all pools, always use the first network port for your management network, the second for your storage network, and so on.
	 

	
		Where your organization does not already have cabling standards, we recommend that you consider labeling or color-coding cables to indicate their purpose.
	 

	
		We recommend that you erase all previous configurations when you reuse physical switches.
	 

	
		Previous passwords or configurations could expose your environment to hostile entities or traffic. For example, somebody might have configured the switch to route traffic automatically to another site or network.
	 

	
		We recommend that you protect the network's physical location, including the security of the area where it is located.
	 



	 
 


	Protecting Virtualized Storage



	Data storage is considered virtualized when there is no one-to-one relationship between the physical storage device and the resulting virtualized storage devices (or disks). For example, when one physical storage device is presented to users as one or more virtualized storage devices or when multiple physical devices are presented as one virtual disk. While virtualizing data storage reduces the user’s perception of complexity, it is important to ensure storage is virtualized securely.
 


	It is worth noting that virtualized storage can reside in multiple physical locations simultaneously, which can be beneficial for companies that want to secure and monitor their virtualized storage.
 


	
		Recommendation:
	 

	
		We recommend that you consider the location of all the physical drives being virtualized when determining the physical security of disks and that you do not place disks in a location where they are physically at risk.
	 

	
		For example, if you add another SR because your original SR ran out of space and the new SR is in a different physical location (for example, on a server that might not be in the same machine room), ensure both locations are physically secure.
	 



	 
 


	Dedicated storage NICs require an IP address on a different IP subnet from the main administration interface. You can use XenCenter to dedicate a storage NIC and bond multiple NICs together for resilience.
 


	
		Recommendation:
	 

	
		We recommend isolating storage traffic from management traffic. If you use IP-based storage devices, such as an NFS or iSCSI SR, the storage traffic also flows through the XenServer control domain.
	 

	
		We recommend that you configure target and host authentication where appropriate to the storage type. This helps protect your data if your storage network is exposed to other less trustworthy parts of your organization. Internal attacks on storage networks may enable hostile parties to intercept any data destined for storage. Such data ranges from data that could compromise the integrity of your virtual environment to sensitive data contained within the virtual machines.
	 



	 
 


	NFS



	A file-based NFS storage repository mounts the specified NFS repository on the control domain. Each SR is represented as a directory on the remote NFS server, with each virtual disk being a file in that directory stored in the VHD file format.
 


	
		Recommendation
	 

	
		We recommend ensuring that because the VHD is not an encrypted file format, only authorized XenServer hosts and administrators can mount the file system. You can limit the export to specific IP addresses, which is typically done when setting up the NFS storage system.
	 

	
		We recommend ensuring that the remote storage device's storage interfaces are not visible outside of the dedicated storage network.
	 

	
		We recommend enabling the NFSv4 AUTH_SYS host authentication.
	 



	 
 


	iSCSI Software Initiator



	In the case of iSCSI traffic, XenServer uses the software OpenISCSI initiator to connect to iSCSI targets.
 


	If you share your iSCSI storage network with other XenServer pools or non-XenServer systems, consider separating your iSCSI storage traffic with VLANs.
 


	
		Recommendation:
	 

	
		If you have multiple XenServer pools, we recommend not sharing iSCSI storage networks between pools with differing levels of trust. For example, a production and test server pool should not share a storage network.
	 

	
		We recommend that you configure CHAP target and host authentication unless you are using GFS2.
	 



	 
 


	Fibre Channel



	
		Recommendation:
	 

	
		We recommend enabling LUN masking on your SAN to secure Fibre Channel traffic.
	 

	
		LUN masking is configuring a LUN only to accept requests from specific HBAs so that a LUN is available to some hosts and not others. LUN masking shields LUNs from detection when a target is scanned. Depending on your HBA configuration, LUN masking may make it possible to control which hosts can access what volumes on a SAN. LUN masking lets you configure the exact LUNs that a specified host can detect.
	 

	
		We recommend creating zones in your SAN to separate and secure Fibre Channel traffic.
	 

	
		SAN zoning is a way to group Fibre Channel devices logically over the storage fabric’s physical configuration. Consider implementing SAN zoning to separate storage traffic from more vulnerable servers (for example, web servers or other servers with a lot of Internet traffic) and the storage traffic of other servers.
	 

	
		Zones effectively isolate devices in the zone from devices outside of the zone and prevent devices outside the zone from detecting devices in the zone. Because zoning is implemented in the fabric by the Fibre Channel switch, zoning can control traffic between devices and, in essence, provide a method of access control for SANs. Zones also provide security because zone traffic is isolated from traffic in other zones, which, in turn, limits the amount of traffic compromised in a successful attack.
	 



	 
 


	SMB



	If you share your SMB storage network with other XenServer pools or non-XenServer systems, consider separating your SMB storage traffic with VLANs.
 


	
		Recommendation:
	 

	
		If you have multiple XenServer pools, we recommend that you do not share SMB storage networks between pools with differing levels of trust. For example, a production and test server pool should not share a storage network.
	 

	
		We recommend that you configure SMB username/password host authentication.
	 



	 
 


	Protecting XenServer Hosts



	Isolating Sensitive VMs



	When designing your virtual environment and determining which servers will host which workloads, consider which virtual machines are particularly sensitive.
 


	
		Recommendation:
	 

	
		We recommend that if you have particularly sensitive VMs, you consider putting them in a pool limited to specific administrators.
	 

	
		We also recommend using VLANs to isolate traffic for systems hosting sensitive data.
	 

	
		We recommend that, in environments with Workload Balancing enabled, if you have a host in a pool that has a different (even if higher) physical security level, you exclude such a host from Workload Balancing optimization and placement recommendations by using the Workload Balancing Exclude Hosts feature. When enabled, the Exclude Hosts feature excludes physical hosts from Workload Balancing optimization and placement recommendations, including Start On placement recommendations.
	 



	 
 


	Securing the Control Domain



	Each host in a pool has a control domain. The control domain is a privileged VM that provides low-level services to other VMs, such as providing access to physical devices. It also runs the management tool stack.
 


	The control domain contains the minimal functionality necessary to manage the host and is designed specifically for that purpose.
 


	
		Recommendation:
	 

	
		We strongly recommend that, to maintain the security, integrity, and supportability of the control domain, you do not make any alterations to the control domain that we do not specifically recommend.
	 

	
		We recommend configuring a strong password for your control domain, tracking who has these passwords, and having a policy for changing both the pool secret and the root account passwords on control domains when individuals with access leave the organization.
	 



	 
 


	For more information, see the product documentation for Managing your administrator password and Rotating your pool secret.
 


	Designing Environments for Auditing



	
		Recommendation:
	 

	
		We recommend designing your environment with auditing in mind, including planning to enable role-based access Control (RBAC).
	 

	
		Enabling RBAC makes audit log data more meaningful since the transactions are logged with the associated user names.
	 



	If you do not wish your administrators to have different access levels, RBAC allows you to audit which administrator performed which operation.
 


	
		Recommendation:
	 

	
		We recommend configuring individual RBAC user accounts for each administrator to provide accountability.
	 



	XenServer provides support for running reports on administrative changes. These reports can help detect changes made to your XenServer environment by administrators for troubleshooting, auditing, and compliance reasons.
 


	Support for auditing is provided through the Workload Balancing component and its workload reports. Configuring support for auditing reports requires Active Directory and the following relatively simple tasks:
 


	
		Adding the Active Directory accounts of the XenServer administrators to XenServer.
	
	
		Configure the XenServer Role-Based Access Control (RBAC) feature and assign roles to the XenServer user accounts. For information about RBAC, see the product documentation.
	
	
		Deploy Workload Balancing, as described in the product documentation, so that you can run the Audit Trail History report. For more information about this report and what it contains, see the “Pool Audit Trail” information in the product documentation.
	



	Depending on your need to demonstrate your environment's compliance, you might want to take additional steps to secure the Workload Balancing database, where the audit-trail data is stored. Such steps might include limiting access to the password or documenting procedures to show how the access is controlled.
 


	Tracking Possibly Unauthorized Changes to Your Environment



	When you need to track down the entry point of malware or another threat, it is essential to be able to review the sources of changes to your environment.
 


	
		Recommendation:
	 

	
		We recommend that you require administrators to use their user accounts since isolating the source of unauthorized administrative changes is more manageable. Not providing the root account password to administrators may help encourage the use of individual accounts.
	 

	
		We recommend enabling RBAC and Workload Balancing so you can run the Audit Trail History Report using Workload Balancing.
	 

	
		The Audit Trail History Report lists administrative changes. For more information, see the product documentation.
	 

	
		We recommend setting all systems in a geographic region to use the same time source (e.g., an NTP server).
	 



	 
 


	When searching for events and changes across systems, it is extremely helpful if all of the systems through which you are searching are set to the same time. This makes it easier to use logs to correlate events across logs from different systems.
 


	When reviewing changes, be aware of time zones and whether the particular log entry reflects local time or Coordinated Universal Time (UTC)/Greenwich Mean Time (GMT).
 


	Enabling Audit Logging



	Audit logging, which records specific administrative changes, is enabled by default in XenServer.
 


	The XenServer audit log records any operation with side effects (successful or unsuccessful). This record includes the server name targeted by the action and its success or failure. The audit log also records associated user names or, if RBAC is not enabled, the type of account association with the action.
 


	
		Recommendation:
	 

	
		To increase the security and availability of the contents of the audit log and reduce the risk of an attacker changing its contents, consider sending your audit log to a remote server, ideally one inaccessible to XenServer administrators.
	 

	
		We recommend both of the following:
	 

	
		Remote servers for storing logs be managed by somebody with the different operational role (for example, by somebody on a team that does not manage the XenServer hosts)
	 

	
		Administrators with access to XenServer should not be granted permissions to modify or delete logs on the remote server
	 

	
		We recommend retaining audit logs in accordance with your organizational security policy.
	 



	 
 


	To configure remote storage of system logs using XenCenter
 


	
		In the XenCenter Resource pane, select the host and click the General tab.
	
	
		On the General tab, click the Properties button.
	
	
		On the Log Destination page, select Also store the system logs on a remote server and enter the remote server where you want to save the logs.
	



	
 


	Disabling Remote SSH Access



	
		Recommendation:
	 

	
		We recommend that customers who do not intend to use SSH access to the control domain disable SSH. The console will still be available locally or through XenCenter.
	 



	 
 


	To disable remote SSH access



	
		In XenCenter, click the Console tab. Alternatively, you can go to the physical console.
	
	
		At the command prompt, type xsconsole.
	
	
		In the shell menu, scroll down to the Remote Service Configuration option and press Enter.
	
	
		Select Enable/Disable Remote Shell and press Enter.
	
	
		Enter your credentials and press Enter.
	
	
		In the Configure Remote Shell options, select Disable and press Enter.
	
	
		After a message appears stating configuration was successful, click OK.
	



	
		Note: 
	 

	
		If you need to re-enable remote SSH access, repeat this procedure but select Enable in the Configure Remote Shell options.
	 



	 
 


	
 


	XenCenter Security



	If your version of XenCenter is not up to date, it will notify you that an update is available unless you have disabled that notification.
 


	
		Recommendation:
	 

	
		We recommend that you ensure that you are using the latest version of XenCenter.
	 



	XenCenter can store credentials for all the hosts it controls. (This option is configured in Tools &gt; Options &gt; Save and Restore.)
 


	
		Recommendation:
	 

	
		We recommend that you ensure that users only use the Save and Restore Service Connection State on Startup option if their organizational security policy permits. In particular, we do not recommend storing Active Directory credentials.
	 



	You can protect XenCenter credentials stored by creating a main password. When enabled, XenCenter prompts users for a password to see any managed servers.
 


	
		Recommendation:
	 

	
		Where your organizational security policy permits XenCenter to store credentials, we recommend creating a main password for XenCenter to prevent unauthorized users from obtaining the credentials of managed XenServer hosts, for example, if a laptop used to manage XenServer is stolen.
	 

	
		We recommend restricting physical access to XenCenter. For example, do not leave unauthorized people unattended with access to a logged-in XenCenter console.
	 



	 
 


	To set a main password



	
 


	
		From the XenCenter Tools menu, click Options and then click the Save and Restore tab.
	
	
		Ensure that the Save and restore server connection state on startup check box is selected.
	
	
		Under Main password, select the Require a main password check box, then enter and confirm the password, and click OK. Remember that passwords are case-sensitive.
	



	Naming Hosts Clearly



	
		Recommendation:
	 

	
		We recommend using meaningful naming conventions when assigning host names to prevent inadvertently configuring hosts in the same pool that should not be connected for security reasons.
	 

	
		Choose a naming convention with names indicating physical security constraints (for example, the physical location).
	 

	
		We recommend that you ensure your host names are significant to your organization so as to avoid moving a VM to the wrong host.
	 



	Securing Physical Hosts



	This section recommends limiting physical access to hosts and securing out-of-band (“lights-out”) management cards.
 


	Limiting Physical Host Access



	
		Recommendation:
	 

	
		We recommend that you take steps to limit physical access to systems. In particular, as several different functions may be running in a virtual environment at a specific physical location, you should consider the number of functions that may be simultaneously compromised.
	 



	Securing Out-of-band Management Cards (Lights-out Management)



	
		Recommendation:
	 

	
		If you require support for hardware features that enable remote administration on the host (for example, Dell DRAC or HP iLO), ensure that access is secured in accordance with your organization’s security policy for these features. Otherwise, disable these features.
	 

	
		We recommend that you consider the type of traffic (for example, untrusted guest VM traffic) on a network when considering enabling remote administration on an interface on that network.
	 



	Protecting VMs



	Securing Virtual Machines



	
		Recommendation:
	 

	
		We recommend that you use the same practices that you would use to secure virtual machines that you would use to secure a physical server: in many ways, their security needs are comparable. This includes taking the same steps to secure the guest operating system as you would to secure the operating system on a physical machine.
	 

	
		We recommend enabling all standard protection methods on the virtual machines, including virus, spyware, and malware protection and operating-system-level firewalls, if appropriate.
	 

	
		We recommend checking that any virtual machines you start after a long period of dormancy have the latest updates to their anti-virus definitions and apply any relevant security patches.
	 

	
		We recommend disabling any unnecessary system components at the guest level, including in the operating system, that may provide an attack surface. For example, follow the operating system vendor’s security guidance document.
	 

	
		We recommend reducing the risk of omitting to set a secure configuration by using templates with the guest operating system installed and configured with security settings whenever you create a VM.
	 

	
		We recommend creating various templates that include the secured operating system and any frequently deployed applications, such as Citrix Virtual Apps and Desktops (if applicable), that also have their security settings configured.
	 



	XenServer VM Tools



	
		Recommendation:
	 

	
		We recommend that customers have the latest XenServer VM Tools running on their guest virtual machines to ensure the best manageability. However, as the tools are running in the guest virtual machines and not in the control domain or the hypervisor, they do not present an additional threat to the XenServer installation. Information on applying updates for XenServer VM Tools can be found at Update XenServer VM Tools for Windows.
	 



	Secure boot



	XenServer supports UEFI Secure Boot for Windows VMs. This method blocks incorrect binaries from being executed within the guest as the OS is loaded, helping to protect the guest VM.
 


	
		Recommendation:
	 

	
		We recommend enabling secure boot for your more sensitive VMs, considering whether the VM OS has secure boot support.
	 



	Avoiding Resource Exhaustion During Abnormal System Load



	Denial-of-service attacks (DoS attacks) are attempts to saturate a system’s resources to suspend the services of a host connected to the Internet. Often, this is done by attempting to saturate the target system with external communication requests to the extent that the target cannot respond and becomes unavailable. Typical targets may include websites associated with financial transactions, including banks, credit card companies, and e-commerce sites.
 


	
		Recommendation:
	 

	
		We recommend configuring sufficient CPU and memory resources to support the expected load of business-critical VMs. There are two key principles to consider:
	 

	
		Ensure that a VM is provisioned with sufficient virtual resources to carry out its expected workloads.
	 

	
		Ensure that a VM is not overprovisioned so that in abnormal conditions, it cannot consume an unreasonable proportion of the host resources.
	 

	
		We recommend that you do not assign so much resource to one VM that, under its maximum load, it precludes other business-critical VMs from running.
	 

	
		We recommend that your configuration is sufficient to avoid the “domino” effect where the unavailability of one host or VM puts sufficient load on others that they, in turn, fail to be sufficiently responsive.
	 



	If required, you can allocate CPU and initial memory resources to the new VM during VM creation or after the new VM is installed. Specifically, you can allocate a number of virtual CPUs to a VM and set a priority for the VM's access to physical CPU resources on the host.
 


	You can adjust the memory allocation when a VM is created in the XenCenter New VM wizard by changing it from the default or after the new VM is created on the VM’s Memory tab.
 


	Naming and tagging VMs



	
		Recommendation:
	 

	
		If your organization does not have a policy on naming, we recommend naming VMs using meaningful names.
	 

	
		For example, indicate specific properties of significant VMs (for example, “-HR-server” or “- finance-server”).
	 

	
		We recommend tagging VMs to group VMs with similar characteristics. For example, tags of “migrateable” or “missioncritical” can be used in XenCenter searches to reduce the risk of accidentally operating on the wrong VM.
	 



	Securing Resource Pools



	When multiple hosts are grouped in resource pools, the pool has a pool coordinator host that controls the other hosts. All management communication between pooled hosts is done over TLS; hosts authenticate using a randomly generated secret created when you form the pool.
 


	
		Recommendation:
	 

	
		We recommend physically isolating and segregating the management traffic from other non-management traffic.
	 

	
		However, if this is impossible, we recommend logically isolating the management traffic from other non-management traffic.
	 



	One method of logically isolating this management traffic is to use VLANs. You can configure your routers to tag all traffic from the management interfaces in the pool with a VLAN tag dedicated to this purpose.
 


	If you decide to configure a VLAN for management traffic, be aware that remote clients such as XenCenter must connect to the management network VLAN.
 


	Creating Pools



	Consider the security requirements of the workloads you want to run on the pool.
 


	
		Recommendation:
	 

	
		We recommend that if workloads must be run on hosts that require a specific level of physical security, you consider creating pools that contain hosts in one physical location only. That is, do not design a pool with hosts in one physical location that is less secure than another physical location.
	 



	Using Live Migration Features



	When using live migration features, such as Workload Balancing and High Availability, it is vital to be aware of the sensitivity of the data on the virtual machines. Live migration can potentially change:
 


	
		The host on which a VM resides
	
	
		The groupings of VMs on hosts
	



	For example, if you enable automation in Workload Balancing so that it applies optimization recommendations automatically, a VM can move to a host without you being aware of it. (In fact, this is the expected and desirable behavior of the feature.)
 


	As a result of automation, if pools contain hosts in multiple physical locations, Workload Balancing could move a VM containing highly sensitive data to a host in a server room that is not physically secured to the required level.
 


	
		Recommendation:
	 

	
		We recommend that if you want to enable any live migration (for example, caused by Workload Balancing, High Availability, or Rolling Pool Upgrade) features in the pool, you ensure that all workloads can be run securely on hosts in all of the pool's physical locations, including the physical security of the premises in which the hosts are located. See also Using Live Migration Features.
	 

	
		Also, be aware that Workload Balancing and High Availability features can change the host on which a VM resides.
	 

	
		We recommend using any of the following to address live migration requirements:
	 

	
		Designing pools from hosts that meet a common level of logical and physical security requirements
	 

	
		Not enabling automation features in Workload Balancing
	 

	
		Enabling automation in Workload Balancing, but excluding hosts from live migration
	 

	
		We recommend that if you want to enable any live migration (Workload Balancing, High Availability) features in the pool, you ensure that all workloads can be run securely on hosts in all of the pool's physical locations, including the physical security of the room in which the hosts are located.
	 

	
		In some cases, we recommend excluding specific hosts from Workload Balancing to ensure that specific workloads are never run on the same system. Likewise, you could run those workloads in different pools.
	 

	
		Features such as Workload Balancing and High Availability can change the host on which a VM resides.
	 



	Deploying XenServer Hosts



	Preparing the host server



	It is important to ensure that your hardware has been updated according to your hardware vendor’s recommendations prior to installing XenServer.
 


	
		Recommendation:
	 

	
		We recommend applying all firmware (BIOS/UEFI) updates recommended by your hardware vendor before installing XenServer.
	 

	
		We recommend that you ascertain how your hardware vendor will inform you of future security and stability updates to their firmware.
	 



	Installing XenServer



	When installing XenServer, it is essential to be aware of best practices that can help ensure your deployment remains secure. These practices include:
 


	
		Checking the integrity of downloaded installation files.
	
	
		Waiting to connect (cable) hosts to physical networks until all hosts are configured. These considerations are described in more detail in the following sections.
	



	Installation Media



	Ensuring that the XenServer installation media you are using is a genuine copy from the XenServer downloads page is essential.
 


	
		Recommendation:
	 

	
		We recommend the following for customers downloading XenServer installation media:
	 

	
		
			Downloading the XenServer installation files only from the secured xenserver.com site.
		
		
			Before installing XenServer, check the integrity of the downloaded installation files. Verify the download against the secure checksum information on the XenServer downloads page. The checksum information lets you verify that the image you downloaded or obtained has not changed from how we packaged it.
		
	



	Checking the Integrity of Downloaded ISO Files



	
		Recommendation:
	 

	
		We recommend verifying the integrity of downloaded ISO files.
	 



	This section provides examples of verifying the integrity of downloaded ISO files for Windows and Linux. There are several different ways of accomplishing this task. The instructions are provided as an example.
 


	To verify the integrity of the ISOs on Windows
 


	
		At a Windows command line, type the following command:
	



	certutil \-hashfile XenServer8\_YYYY-MM-DD.iso SHA256
 


	
		Compare the checksum output to the checksum information on the XenServer downloads page.
	



	
		Note:
	 

	
		It is essential to compare the entire length of the checksum and not just a few characters.
	 



	To verify the integrity of the ISOs on Linux
 


	
		Log in or open a console to display a shell prompt.
	
	
		At the prompt, type the following command:
	
	
		 % sha256sum XenServer8\_YYYY-MM-DD.iso
	
	
		Compare the checksum output to the checksum information on the XenServer downloads page.
	



	
		Note: 
	 

	
		It is essential to compare the entire length of the checksum and not just a few characters.
	 



	Network installation



	
		Recommendation:
	 

	
		We recommend that you use a trusted server if you are installing from a PXE boot server.
	 

	
		In practice, this means that you should make sure that you connect to the PXE boot server over the management network and not the guest network. As previously discussed, this prevents a tenant PXE server with malicious XenServer installations from impersonating your PXE server.
	 



	Before Deploying Hosts



	Before deploying a XenServer host or pool, we recommend performing both of the following:
 


	
		Verifying that you configured your physical networks and VLANs as you expected
	
	
		Verifying the identity of a host
	



	
		Recommendation:
	 

	
		We recommend that before you put a pool into production, you double-check that your networks are configured and isolated as you expect. If you separated the different types of traffic from each other, check to make sure that you successfully isolated each traffic type.
	 

	
		For example, you might consider monitoring network traffic, examining network port activity lights (if present), and, if your network numbering and masking permits, trying to ping a device across networks.
	 

	
		You should check for isolation for each guest, as well as management and IP-based storage networks.
	 

	
		We recommend that you do not cable the host when installing XenServer beyond connecting it to the management network – until you have all the host networks configured and the correct security policies in place.
	 

	
		We recommend that you ensure you have physically and logically isolated the management network through cabling and configuration before physically plugging the host into the guest network, where it may be exposed to potentially hostile traffic. This is particularly true in environments where the users and administrators of the guest VMs are unknown and may have malicious intent.
	 

	
		We recommend that if you have a bare metal machine and are booting it off the network using a boot server (for example, a PXE installation), you validate the installation server's identity before proceeding with the installation.
	 

	
		This is particularly true in environments where a malicious guest VM could have a PXE server that you unwittingly connect to when performing installation.
	 

	
		We recommend configuring your host’s BIOS only to use trusted network interfaces for PXE boot.
	 



	Adding Hosts to Pools



	
		Recommendation:
	 

	
		We recommend that you follow the previous advice when adding new hosts to an existing pool, with the following difference:
	 

	
		When adding hosts to a pool, do not connect to the guest networks until after:
	 

	
		
			You add the host to the pool
		
		
			The pool coordinator is finished replicating the pool networks on the new host
		
	



	Creating Secure Templates



	A template created with only one use case in mind might be reused for many other VMs with differing security requirements.
 


	
		Recommendation:
	 

	
		We recommend that you take extra care when creating VMs for replication (as templates) to ensure that the configurations are suitable for all potential uses of the VM.
	 

	
		We recommend that you ensure that when you create templates, you do not unintentionally include data such as user accounts and data, particularly secrets such as SSH keys.
	 

	
		We recommend that templates be named to indicate their intended purpose (for example, to avoid using an experimental template for a production VM, specify “-test” as part of their name).
	 

	
		We recommend that if you are going to create VMs from a template, you make sure the template does not include any unnecessary or undesirable networks when you build it.
	 

	
		We recommend that you do not assign unnecessary network ports to each guest.
	 



	For example, when initially making the VM from which you want to generate the template, verify that you do not create virtual network interfaces for all of the networks (NICs) available on your host.
 


	The following image shows the screen where you can delete unnecessary network ports from VMs.
 


	
 


	In the Create New VM wizard, when creating a new VM for use as a template, delete any unused networks to prevent the wizard from creating virtual network interfaces for them.
 


	
		Recommendation:
	 

	
		We recommend that you ensure that VM templates are considered as part of your organization’s patching schedule.
	 



	Accessing XenServer Hosts and Using Certificates



	When it is installed, each XenServer host generates a set of random cryptographic key pairs. Each key pair is split into two portions: a public key that is displayed to clients and a private key that is only known to the host and can be used to prove that it is the owner of the public key.
 


	The XenServer host generates two classes of keys: one for the Secure Shell (SSH) protocol and a second for the management API TLS network service. SSH is only used for advanced configuration of the control domain and should not be needed for everyday use. The management API TLS communication path is used much more often (for example, by XenCenter).
 


	The primary use of these keys is to confirm that you are connecting to the correct host.
 


	Adding Hosts to XenCenter Securely



	
		Recommendation:
	 

	
		We recommend verifying that when you add a host to XenCenter, the host you are adding is not being impersonated. Perform the steps in the following procedure to ensure you are not under a man-in-the-middle attack by an attacker trying to impersonate a XenServer host.
	 



	To verify you are not connecting to an impersonated host, obtain the TLS key for the host before adding the host to XenCenter. After noting the key, you can compare it to the key XenCenter displays when adding the host. After XenCenter connects to a host for the first time, XenCenter stores the TLS key and associates it with the host record. If the host key changes (for example, the host is upgraded or reinstalled), a warning shows the new host key.
 


	
		Note: 
	 

	
		The terms thumbprint and fingerprint are used interchangeably throughout this section.
	 



	To obtain the TLS key fingerprint for a host



	
		At the physical host, open the xsconsole by typing xsconsole at the command prompt.
	
	
		Open the Status Display. Note the SSL Key Fingerprint for https that appears in the console.
	



	
 


	To verify the host’s identity when you add it to XenCenter



	
		Add the host to XenCenter (Server &gt; Add).
	
	
		In the New Security Certificate dialog that appears, click View Certificate.
	
	
		In the Certificate dialog that appears, click Details.
	
	
		Scroll down to the Thumbprint field and select it.
	



	
 


	
		If desired, click Copy to File to copy the thumbprint to the clipboard.
	
	
		If you copied the thumbprint, click OK to exit the dialog. The New Security Certificate dialog appears.
	
	
		Compare the TLS key fingerprint XenCenter displays to the one you noted at the host console. If the two fingerprints do not match, click Cancel in the dialog box and investigate why the host XenCenter is connecting to is not the same as the one you believe were adding.
	



	
		Note: 
	 

	
		It is essential to compare the entire length of the fingerprint and not just a few characters.
	 



	To verify the TLS key fingerprint



	
		In the Security Certificate Changed dialog, click View Certificate.
	
	
		In the Certificate dialog box, click the Details tab.
	
	
		Scroll towards the bottom of the fields list and select the Thumbprint field.
	



	
 


	
		If desired, click Copy to File to copy the thumbprint to your clipboard.
	



	To display a warning for new or modified TLS certificates on hosts



	
		In XenCenter, in the Tools menu, select Options.
	
	
		In the left-hand pane, select Security.
	
	
		Select Warn me when a new certificate is found and Warn me when a certificate changes.
	



	
 


	When a certificate changes, you see the following message:
 


	
 


	Configuring Trusted Certificates for XenServer Hosts



	
		Recommendation:
	 

	
		We recommend replacing self-signed certificates with certificates from a trusted authority to reduce the risk of malicious servers impersonating your hosts.
	 

	
		We recommend using HTTPS to secure communication between XenServer customers running Citrix Virtual Desktops workloads and the Desktop Delivery Controller. To use HTTPS, you must replace the default TLS certificate installed with XenServer with one from a trusted certificate authority also trusted by the Desktop Delivery Controller.
	 



	For information about configuring XenServer certificates, see the topic Install a TLS certificate on your server in the product documentation.
 


	Decommissioning Systems



	Decommissioned hosts may contain sensitive data on VMs.
 


	
		Recommendation:
	 

	
		We recommend completely erasing all data on local disks when decommissioning hosts containing sensitive data or that are part of highly secured environments.
	 

	
		We recommend that you remove any access granted by switches, storage backends, etc., based on the removed host’s MAC addresses or HBA identifiers when removing hosts.
	 



	 
 


	Reusing Physical Servers



	
		Recommendation:
	 

	
		We recommend that you completely wipe and reimage hosts before reusing them in your environment; hosts may contain traces of sensitive data, passwords, or viruses.
	 



	Managing XenServer VMs



	Managing Snapshots, Backups, and Archives



	
		Recommendation:
	 

	
		We recommend that you consider whether the VM contains potentially sensitive data before configuring VM snapshots, backups, archives, or disaster recovery. Specifically, when you create the extra copy, be aware of the storage array’s security level and physical security and who can access it.
	 

	
		If VM snapshots include sensitive data, we recommend applying the same level of security to all aspects of the archival or backup process as you would to the VM. For example, ensure the following are secured appropriately: the storage array for the snapshots or archives and the network used to send the archives.
	 

	
		When configuring scheduled snapshots of VMs using the CLI or backed up VMs using Disaster Recovery, we recommend carefully reviewing the RBAC roles assigned to administrators. Be aware of which administrators can access the snapshots and mirrored Disaster Recovery site.
	 

	
		Any administrator assigned an RBAC role of Pool Admin, Pool Operator, or VM Power Admin can start a snapshot taken from any VM in the pool (for which they have that role).
	 

	
		We recommend backing up copies of VMs for storage in a location with sufficient logical and physical security. Sometimes, you may need to back up VMs containing sensitive data to a different location or configure different disaster recovery settings for sensitive VMs.
	 



	 
 


	Scanning for Malware



	
		Recommendation:
	 

	
		We recommend avoiding simultaneous virus scans on VMs.
	 

	
		Running simultaneous security scans on multiple VMs on a host can cause unnecessary spikes in resource usage. Unlike physical servers that typically have excess resource capacity, hosts are often run at maximum VM density, so unexpected processes can potentially cause overloads – especially if features like Workload Balancing, which can balance virtual-machine loads across hosts, are not configured.
	 

	
		We recommend that, if supported, you configure your virus scanner to scan VMs randomly or download updates to VMs at different times. Some anti-virus applications have virtualization-aware features to help mitigate these issues.
	 



	 
 


	Managing Dormant Virtual Machines



	
		Recommendation:
	 

	
		We recommend deleting dormant, inactive VMs or actively monitoring, managing, securing, and including them in update cycles. Otherwise, they may potentially provide easy access to the environment.
	 

	
		We recommend that when applying security updates, VMs that are shut down or suspended are not overlooked. Failing to update any layer of software (for example, guest operating systems or workload applications) may leave security vulnerabilities and holes.
	 

	
		Dormant VMs should be tracked and included in all security policies and updates.
	 



	Consider the following:
 


	
		Dormant virtual machines may contain sensitive data, such as credit card transactions or employee social security numbers.
	
	
		Dormant VMs may not be included in the latest access policies, may not have the latest virus software, and may be inadvertently omitted from monitoring and virus updates. Consequently, organizations may believe their environments are secure when, in reality, they are vulnerable to known threats that they think they've addressed.
	



	
		Recommendation:
	 

	
		We recommend that, depending on your environment's risk profile (that is, if it has potentially dangerous or hostile traffic), if you must start a dormant VM that may be lacking significant security updates, you start that VM on a network with restricted connectivity until the VM has a full complement of security updates.
	 



	Managing XenServer Administrators



	Separating Administrative Responsibilities



	
		Recommendation:
	 

	
		We recommend providing administrative access to XenServer through its Role Based Access Control (RBAC) feature. The RBAC feature lets you use the Active Directory accounts for administrators and assign access levels to these accounts (through roles). Assigning role-based access allows you to troubleshoot and monitor administrative activity through audit logging.
	 

	
		Ensuring each administrative tier is available only to appropriately authorized people helps avoid unintentional and intentional unauthorized changes, potentially affecting the stability of physical hosts and VMs and the associated cost of resulting system outages.
	 



	If not already in place, consider separating administrative responsibilities for your virtualized environment, possibly based on the ones suggested by the predefined XenServer RBAC roles, and then define which RBAC roles or people will be associated with which responsibilities.
 


	The product documentation provides detailed information about configuring RBAC.
 


	
		Recommendation:
	 

	
		Before deploying RBAC, we recommend familiarizing yourself with the RBAC feature and understanding the division of responsibilities between the roles. Make sure you understand what tasks each role can perform and the data of which each role has visibility.
	 

	
		Do not assign roles solely based on their titles.
	 

	
		We recommend matching the assignment of roles to your organizational security policy.
	 

	
		We recommend carefully reviewing access policies and roles because access to the hypervisor can enable access to key infrastructure components, such as network switches and firewalls, as well as sensitive hosted applications and workloads.
	 

	
		We recommend carefully reviewing the RBAC roles assigned to administrators in relation to the VM snapshots when making RBAC assignments.
	 

	
		Any administrator assigned an RBAC role of Pool Admin, Pool Operator, or VM Power Admin can start a snapshot taken from any VM in the pool (for which they have that role).
	 

	
		When using RBAC, it is important to remember that a root account (the local super-user account) still exists.
	 

	
		We recommend that you keep the root account password secure in accordance with your organizational security policy. You may want to store it in a physically secure location with restricted access. (For example, you could store it in a safe or safety deposit box, where nobody has routine access, but a suitably authorized person could retrieve the password in the event of an emergency.)
	 



	There are two approaches to implementing XenServer administrative accounts with RBAC:
 


	
		You could use the same account for XenServer administration as you use for other company activities (for example, email, network access, and so on). This is usually undesirable as it increases the opportunities for the account to become compromised and the scope of potential impact if the account is compromised.
	
	
		You could create separate Active Directory accounts solely for XenServer administration with a different user name.
	



	
		Recommendation:
	 

	
		We recommend that you consult your organizational security policy when you implement RBAC to determine whether you should create separate Active Directory accounts that are only used for XenServer administration. If you don’t have such a policy, we recommend that you create separate accounts for XenServer administration.
	 

	
		We recommend considering business continuity when using the RBAC feature.
	 

	
		Ensure you can still access XenServer if the person assigned the Pool Admin role is no longer available. This might involve ensuring:
	 

	
		More than one person is assigned an individual Pool Admin account
	 

	
		The root account password is stored in a secure, restricted location with protocols to access it in an emergency by your organizational security policy.
	 

	
		We recommend that when you implement RBAC in your environment, you be aware that you ultimately trust the Active Directory administrator to control access to your environment.
	 

	
		Whoever can control Active Directory accounts can also access those accounts and, by extension, access the XenServer environment.
	 

	
		The Active Directory administrator can inadvertently create a denial of service situation by unwittingly disabling accounts (for example, if the organizational security policy changes and the administrator changes the properties of the Active Directory accounts)
	 

	
		Consider how you will gain access to the XenServer environment if the Active Directory administrator inadvertently disables the XenServer Pool Admin account, which has only one such account.
	 

	
		We recommend that you do not share RBAC accounts. If you do, your ability to trace the owner of changes in the audit log will be compromised.
	 



	Role Based Access Control (RBAC) Overview



	XenServer’s Role Based Access Control (RBAC) lets you assign predefined roles, or sets of XenServer permissions, to Active Directory users and groups. These permissions control the level of access XenServer users (that is, people administering XenServer) have to servers and pools: RBAC is configured and deployed at the pool level. Because users acquire permissions through their assigned role, you must assign a role to a user or their group.
 


	Using Active Directory accounts for XenServer user accounts, RBAC lets you restrict which operations different groups of users can perform, reducing the likelihood of inexperienced users making disastrous, accidental changes. Assigning RBAC roles also helps meet compliance requirements by preventing unauthorized changes to your resource pools. RBAC also provides an Audit Log feature and its corresponding Workload Balancing Pool Audit Trail report to facilitate compliance and auditing.
 


	RBAC depends on Active Directory for authentication services. Specifically, XenServer lists authorized users based on Active Directory user and group accounts. As a result, you must join the pool to the domain and add Active Directory accounts before you can assign roles.
 


	Local Super-User



	The local super-user, or root, is a special user account used for system administration and has all rights or permissions. In XenServer, the local super-user is the default account at installation. The local super-user is authenticated by XenServer and not an external authentication service. The local super-user can still log in and manage the system if the external authentication service fails. The local super-user can access the XenServer physical server through SSH if SSH is enabled.
 


	RBAC Roles



	XenServer comes with six pre-established roles that are designed to align with different functions in an IT organization.
 


	
		Pool Administrator (Pool Admin). This is the most powerful role available. Pool Admins have full access to all XenServer features and settings. They can perform all operations, including role and user management, and grant access to the XenServer console. As a best practice, we recommend assigning this role to an extremely limited number of users.
	



	
		Note: 
	 

	
		The local super user (root) always has the Pool Admin role, which has the same permissions as the local root.
	 



	
		Pool Operator (Pool Operator). This role is designed to let the assignee manage pool-wide resources, including creating storage, servers, patches, and pools. Pool Operators can configure pool resources. They also have full access to the following features: High Availability (HA), Workload Balancing, and patch management. Pool Operators cannot add users or modify roles.
	
	
		Virtual Machine Power Administrator (VM Power Admin). This role has full access to VM and Template management. It can choose where to start VMs, the dynamic memory control features, and the VM snapshot feature. In addition, it can set the Home Server and decide where to run workloads. Assigning this role grants the assignee sufficient permission to provide virtual machines for VM Operator use.
	
	
		Virtual Machine Administrator (VM Admin). This role can manage VMs and Templates and access the storage necessary to complete these tasks. However, this role relies on XenServer to choose where to run workloads, and the settings must be used in templates for dynamic memory control and the Home Server. (This role cannot access the dynamic memory control features, make snapshots, set the Home Server, or choose where to run workloads.)
	
	
		Virtual Machine Operator (VM Operator). This role can use the VMs in a pool and manage their basic lifecycle. VM Operators can interact with the VM consoles and start or stop VMs, provided sufficient hardware resources are available. Likewise, VM Operators can perform start and stop lifecycle operations. The VM Operator role cannot create or destroy VMs, alter VM properties, or server resources.
	
	
		Read-only (Read Only). This role can only view resource pool and performance data.
	



	For information about the permissions associated with each role, see the product documentation.
 


	Monitoring for XenServer Security Updates



	We continually release updates for the XenServer family of products, including updates that address security issues. We recommend that customers monitor the availability of new updates and apply them to their installations.
 


	You can find information about whether there are pending updates for your systems on the XenCenter UI.
 


	
		Recommendation:
	 

	
		We recommend subscribing to XenServer Security Bulletins. These are listed at https://support.citrix.com/s/article/CTX691478-security-bulletins-for-xenserver where the “My support alerts” option in the account pull-down lets you register to receive email alerts when we release security bulletins and updates. Alternatively, Atom and RSS feeds of security alerts are published at https://support.citrix.com/s/rss-feeds.
	 

	
		We recommend selecting the Notifications tab in XenCenter when events are flagged and examining the relevant Alerts and Updates.
	 

	
		We recommend regularly applying any pending XenServer updates to your systems. However, we recommend expediting this when a security bulletin is published. After a security bulletin is published, the security vulnerability becomes publicly known. This principle also applies to any guest operating systems and any application workloads.
	 



	Many updates use XenServer’s Live Patching feature to minimize the disruption caused by updating.
 


	
		Recommendation:
	 

	
		When deploying updates, consider whether they use the Live Patch functionality and whether that can expedite deployment.
	 

	
		We recommend following your hardware manufacturer’s guidance when applying any relevant firmware updates.]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2025_03/image.png.3d75e127968fd2328df35d570302ee04.png" length="332924" type="image/png"/><pubDate>Tue, 01 Apr 2025 13:01:06 +0000</pubDate></item><item><title>Multi-Domain Federated Authentication Service (FAS) Architecture</title><link>https://community.stage.citrix.com/tech-zone/build/tech-papers/citrix-fas-multi-domain-architecture/</link><description>Tech Paper_Multi-Domain Federated Authentication Service (FAS) Architecture.pdf
 


	Overview



	Many public and private organizations are integrating Citrix solutions into Active Directory infrastructures in multi-forest scenarios, typically to separate user objects from resources.
 


	In addition to this deployment type, many companies are moving toward SAML-based authentication to improve security, as it offers more flexibility and granular authentication policies. SAML could be a challenge for implementations using Citrix environments, and to enable consistent SSO to VDAs without LDAP authentication, Citrix Federated Authentication Service (FAS) implementation is required.
 


	Previous Citrix Blog and Tech Zone articles have covered this topic in other areas. However, this deployment guide will share the key points of an experience to identify how and where to install Citrix Federated Authentication Service (FAS) in different multi-forest scenarios.
 


	Assessment



	During the assessment phase, checking the architecture and whether infrastructure components are already required for FAS to work is essential. The corresponding table contains questions that will help you identify where Citrix FAS can be installed and the key points.
 


	
		
			
				
					Question
				 
			
			
				
					Description
				 
			
		
		
			
				
					Describe the network topology regarding the domain and forests involved in the architecture.
				 
			
			
				
					Specify how forests are divided.
				 
			
		
		
			
				
					Is replication between forests active?
				 
			
			
				
					Requirement - two-way replication or selective between forests
				 
			
		
		
			
				
					On which forests are users located who will authenticate on the platform?
				 
			
			
				
					 
				 
			
		
		
			
				
					Is the Certification Authority available?
				 
			
			
				
					Verify the structure of the existing CA (Root and Sub).
				 

				
					A CA is required. Microsoft CA is preferred***
				 
			
		
		
			
				
					Is CA in the resource forest?
				 
			
			
				
					Depending on where the CA is, requirements could change.
				 
			
		
		
			
				
					If yes, is Cross-Forest CA present with the user forest?
				 
			
			
				
					Useful to use autoenrollment for user forest DCs.
				 
			
		
		
			
				
					If yes, is LDAPReferrarls active?
				 
			
			
				
					Needed to issue certificates to users from a different forest
				 
			
		
		
			
				
					Do the DCs in the user forest have a Domain Controller or Kerberos-type certificate issued by the same CA?
				 
			
			
				
					In a scenario of RootCA and several SubCAs, it is also okay for it to be issued from any SubCA (more below for details)
				 
			
		
		
			
				
					Is it possible to install FAS in the same forest as CA?
				 
			
			
				
					It would facilitate the final design and avoid configuring the CA cross-forest between the FAS and CA forests.
				 
			
		
	



	***Starting with Citrix FAS version 2311, it&#x2019;s possible to use a Certification Authority other than Microsoft's. Refer to the latest available Citrix FAS documentation for the list of validated non-Microsoft PKIs.
 


	Having the answers to the above questions will make it easier to study where to install Citrix FAS and what the requirements will be to make the architecture work.
 


	The following flowchart will help you design the target solution. The sections below will go into detail about each possible scenario and the solutions to adopt:
 


	
 


	Active Directory



	Different multi-forest scenarios may exist. Forest segmentation in Active Directory is usually based on separating IT objects from user accounts, but this is not always true. More complex multi-forest scenarios may come up when we work with customers.
 


	This article will help you understand how to design the SSO solution to VDAs with Citrix FAS, depending on where you can place this new resource.
 


	It is also helpful to know how authentication by Smartcard-Logon certificate used by FAS works. 
 


	You can refer to this Microsoft article. Be aware that when trying to access a device with a smartcard-logon certificate, the device requires the Domain Controller to authenticate the user. The Domain Controller uses the Domain Controller Authentication or Kerberos-type certificate to verify the device's identity and establish a secure connection. Without this certificate installed on the DCs, authentication with a smartcard certificate would not be possible.
 


	Finally, the configurations described below have been validated in environments that meet the following requirements:
 


	
		There is a Microsoft Certification Authority.
	
	
		There is bidirectional or selective replication between forests.
	
	
		That the VDAs trust the reference CAs for issuing certificates generated with Citrix FAS.
	
	
		That Domain controller where user accounts are located has in trust on the NTAuth store the reference CA for issuing certificates generated with Citrix FAS (PKI-Trust); in some cases, we will see that it will be automatic, and in others, it will be necessary to implement it during the FAS setup phase.
	



	Single Forest



	 
 


	
 


	Citrix FAS installation and configuration will occur in this forest. 
 


	Microsoft Certification Authority is implemented in the forest, and domain controllers will probably already have the domain controller authentication certificate issued by Microsoft CA by auto-enroll. However, it is good to verify that it is present or a certificate can be issued to each Domain Controller based on one of these templates:
 


	
		Domain Controller
	
	
		Domain Controller Authentication
	
	
		Kerberos Authentication
	



	However, Microsoft recommends that the Domain Controller use the Kerberos Authentication certificate, which has a more specific use case. From then on, we will always refer to the Kerberos Authentication certificate but remember that you can use any of the above certificates.
 


	Citrix FAS will be able to install the necessary templates for proper SSO operation in the CA. VDAs and DCs will automatically have Microsoft Enterprise CA certificates in trust.
 


	KEY POINTS:
 


	
		Install the FAS in the single forest
	
	
		Verify that the DCs have the required Kerberos certificate
	



	Multiple resource forest



	Multiple forests are used for resource placement, and a single forest is for user accounts. In this scenario, we can expect Citrix FAS to be placed in different locations.
 


	Citrix FAS and Microsoft CA in the user forest



	
 


	 
 


	This scenario could occur when a Microsoft CA is already located in the user forest, and you do not intend to create another one or trust it in the resource forests. 
 


	The scenario described above can be followed in a single forest by installing FAS in the user forest where CA is also located.
 


	KEY POINTS:
 


	
		Install the FAS in the single forest
	
	
		Verify that the DCs have the required Kerberos certificate
	



	Citrix FAS and Microsoft CA in the same resource forest



	
 


	Being in the same forest and with the correct permissions, Citrix FAS can install the necessary templates for proper SSO operation in the Microsoft CA.
 


	By default, Microsoft CA can issue certificates only for users in its forest. LDAPReferrarls must be enabled to issue certificates for the user account forest as well.
 


	Finally, Domain Controllers in the user account forest must have the Kerberos Authentication certificate issued by the same CA used by Citrix FAS (in this case, located in Resource Forest #2).
 


	For the Microsoft CA in the resource forest to issue the Kerberos certificate for the DCs 
 


	Cross-Forest Certification Enrollment must be enabled in the user forest using autoenrollment (between the User Forest and Resource Forest #2).
 


	Otherwise, issuing certificates for DCs using certutil commands will be possible. In this case, remember to also place the CA's certificate in the NTAuth store of each Domain Controller in the user forest (PKI-Trust).
 


	KEY POINTS:
 


	
		Install FAS in the resource forest where Microsoft CA is located.
	
	
		Enable LDAPReferrals on the CA
	
	
		Issue Kerberos certificates for the DCs in the user forest:
		
			
				With auto-enroll, you need to enable Cross-Forest Certification Enrollment between the forest where the CA is and the user forest. 
			
			
				Without auto-enroll, you can use the certutil command to issue the Kerberos certificate and add the CA's certificate to the NTAuth store (PKI Trust).
			
		
	



	Citrix FAS and Microsoft CA in different resource forests



	 
 


	
 


	Citrix FAS and Microsoft CA are in different resource forests. In this scenario, FAS will only be able to install the templates required for proper SSO operation on VDAs if Cross-Forest Certification Enrollment between the two resource forests is enabled (between Resource Forest #1 and Resource Forest #2), which becomes a requirement.
 


	The requirement to enable LDAPReferrals and Kerberos certificates for user DCs remains the same.
 


	KEY POINTS:
 


	
		Enable Cross-Forest Certification Enrollment between resource forests where Microsoft CA and Citrix FAS reside.
	
	
		Enable LDAPReferrals on the CA
	
	
		Issue Kerberos certificates for the DCs in the user forest:
		
			
				With auto-enroll, you need to enable Cross-Forest Certification Enrollment between the forest where the CA is and the user forest. 
			
			
				You can use the certutil command to issue the Kerberos certificate and add the CA's certificate to the NTAuth store (PKI Trust) without auto-enroll.
			
		
	



	Multiple user forest and multiple resource forest



	
 


	Multiple forests are used for resource placement, and multiple forests are where user accounts reside.
 


	This may seem like an even more complex scenario than the previous ones, but everything is related to the previous ones.
 


	Consider installing Citrix FAS in the same forest where the Microsoft CA is located and following the previous suggestions. Alternatively, you will need to enable Cross-Forest Certification Enrollment between the resource forests where Microsoft CA is located and the one where Citrix FAS is.
 


	Since user accounts are stored in multiple forests, always install Kerberos Authentication certificates on each Domain Controller where users reside. For autoenrollment mode, you will need to enable Cross-Forest Certification Enrollment.
 


	KEY POINTS:
 


	
		If you can install Citrix FAS in the same forest as Microsoft CA, follow the above suggestions. Alternatively, enable Cross-Forest Certification Enrollment active between the resource forests where Microsoft CA and Citrix FAS are located.
	
	
		Enable LDAPReferrals on the CA
	
	
		Issue Kerberos certificates for the DCs in the user forest:
		
			
				With auto-enroll, you need to enable Cross-Forest Certification Enrollment between the forest where the CA is and the user forest. 
			
			
				You can use the certutil command to issue the Kerberos certificate and add the CA's certificate to the NTAuth store (PKI Trust) without auto-enroll.
			
		
	



	SubCA in multiple forests



	
 


	When we work with customers, complex and unpredictable multi-forest scenarios may arise. The last case might be when different SubCAs in multiple forests are part of the same Microsoft Enterprise CA.
 


	The only difference from the previous case is that Kerberos certificates are used on DCs where user accounts can be issued from different SubCAs (it does not matter which SubCA is the issuer). The basic requirement remains that on all DCs in the forests where user accounts reside, there is trust in the NTAuth to store the certificate of the SubCA (more specifically) used by FAS or RootCA (PKI-Trust).
 


	KEY POINTS:
 


	
		Check if you can install Citrix FAS in the same forest as a SubCA. Alternatively, enable Cross-Forest Certification Enrollment between the resource forests where SubCA and Citrix FAS are located.
	
	
		Enable LDAPReferrals on the CA
	
	
		Check if all DCs with user accounts have a Kerberos certificate issued by one of the SubCAs (it does not matter which SubCA is the issuer). If there is a SubCA in the same forest, use autoenrollment or:
		
			
				With autoenrollment, you need to enable Cross-Forest Certification Enrollment between the forest where there is a SubCA and the user forest and add in the NTAuth store the certificate of the SubCA used by the FAS (PKI-Trust if you do not use the same one).
			
			
				Without auto-enroll, use the certutil command to issue the Kerberos certificate and add the certificate of the SubCA used by the FAS (PKI Trust) to the NTAuth store.
			
		
	



	Summary



	During the assessment phase, engaging the teams responsible for Active Directory and security is crucial to fully assess the status of Certification Authorities (CAs) if they are already in place. It is also critical to check for existing predefined policies related to CAs and ensure that the customer has already carefully considered these critical elements.
 


	Another important aspect is the hardening of CAs and related templates, which include key retention management, encryption, and other security measures. For detailed guidance on hardening, see the Citrix FAS documentation, which provides specific guidance and best practices.
 


	Finally, other considerations should be considered when integrating Citrix components, such as Delivery Controller or StoreFront, into multidomain environments. Each Active Directory implementation is slightly different, so additional steps may be required to make your solution work.</description><enclosure url="https://media.invisioncic.com/r328636/monthly_2025_03/Tech01.jpg.c789b4b1d9394f7d0f4e3dbbb2f7cffb.jpg" length="55514" type="image/jpeg"/><pubDate>Tue, 25 Mar 2025 12:29:00 +0000</pubDate></item><item><title>Installing and Configuring Ansible, Terraform, and Packer for Citrix Infrastructure-as-Code-based Environments</title><link>https://community.stage.citrix.com/tech-zone/build/deployment-guides/create-iac-vm/</link><description><![CDATA[Installing and Configuring Ansible, Terraform, and Packer for Citrix Infrastructure-as-Code-based Environments 
	 
	Overview
 


	This guide provides an overview of using Ansible, Terraform, and Packer to deploy Infrastructure-as-Code-based environments. 
	We will install all the prerequisites and IaC frameworks needed on one Virtual Machine to ensure smooth communication between Ansible, Terraform, and Packer. Our standard installation of an IaC-VM also has Docker installed. However, the Docker installation is not part of this guide. 
 


	Note: 
	 
	Please find a description of a stand-alone installation of Terraform and initial configurations in our Tech Zone Deployment Guide Installing Terraform and Configuring the Citrix Terraform Provider.
 


	 
	What is Terraform



	Terraform is an Infrastructure-as-Code (IaC) tool that defines Cloud and On-Premises 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. They are created in the native declarative configuration language, 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. 
	 
 


	What is Ansible



	Ansible provides automation for managing Cloud- and On-Premises Infrastructures.  
	Some everyday use cases for Ansible are:
 


	
		Manage and maintain system configuration
	
	
		Deploy complex software
	



	Ansible automates tasks using simple, human-readable scripts written in YAML called Ansible Playbooks. In these playbooks, you declare the desired state of a local or remote system, and Ansible ensures that the system remains in that state. 
	 
 


	What is Packer



	Packer is a tool for creating Machine Images from a single source configuration.  It helps you automate the creation of Virtual Machine Images for multiple Cloud- and On-Premises-based environments. Packer uses other IaC frameworks for Configuration- and Software-Management, like Terraform and Ansible. 
	 
 


	What are the benefits of using IaC frameworks?



	Our guides primarily use Terraform to deploy the infrastructure components in Azure and the entities in Citrix Cloud. Unfortunately, not all Terraform providers have all the necessary functionalities, such as putting a Virtual Machine into an Active Directory Domain or installing needed Software components on the Virtual Machines—for example, to deploy the Cloud Connector software on the Cloud Connector VMs. 
	We used PowerShell and the Citrix Remote PowerShell SDK in prior guide versions, which were directly called out of the adjacent Terraform code snippets. This approach has significantly complicated the deployments due to numerous dependencies. 
	 
	In all our actual guides, we (will) use Ansible to add configurations and needed software components to entities created by Terraform. This approach allows us to be far more flexible.  
	Ansible, Terraform, and Packer can be installed on the same machine or different hosts based on your needs. 
	 
 


	Installation of the IaC Virtual Machine



	As mentioned, we will install all IaC frameworks on the same Virtual Machine - additionally, we will install GNOME, XRDP, and Visual Studio Code on the VM.  
	We recommend using any UNIX-like machine with Python installed – in this guide, we will use Ubuntu 24.04.
 


	
		Important:
	 

	
		There are many ways to install Ansible, Terraform, and Packer on Ubuntu. 
		In this example, we have used a proven workflow to install Ansible, Terraform, and Packer on Ubuntu-based Virtual Machines for different operating systems and Hypervisors/Hyperscalers. 
		Please install all the tools according to your needs, best practices, etc. 
		 
	 



	We assume you already have a (Virtual) Machine with Ubuntu 24.04 installed. 
	 
 


	Basic configuration (examples)



	First of all, we update all packages and define some basic settings:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						tmm-azadmin@TMM-GK-UBUNTU-AUTOMATION-DEVOPS:~$ sudo nano /etc/sudoers 
						tmm-azadmin@TMM-GK-UBUNTU-AUTOMATION-DEVOPS:~$ sudo usermod -a -G sudo tmm-azadmin 
						tmm-azadmin@TMM-GK-UBUNTU-AUTOMATION-DEVOPS:~$ sudo apt-get update 
						Hit:1 http://azure.archive.ubuntu.com/ubuntu noble InRelease 
						Get:2 http://azure.archive.ubuntu.com/ubuntu noble-updates InRelease [126 kB] 
						Get:3 http://azure.archive.ubuntu.com/ubuntu noble-backports InRelease [126 kB] 
						...
					 

					
						Get:49 http://azure.archive.ubuntu.com/ubuntu noble-security/multiverse amd64 Components [212 B] 
						Get:50 http://azure.archive.ubuntu.com/ubuntu noble-security/multiverse amd64 c-n-f Metadata [356 B] 
						Fetched 31.5 MB in 6s (4969 kB/s) 
						Reading package lists... Done 
						tmm-azadmin@TMM-GK-UBUNTU-AUTOMATION-DEVOPS:~$ sudo apt-get upgrade -y 
						Reading package lists... Done 
						Building dependency tree... Done 
						Reading state information... Done 
						Calculating upgrade... Done 
						# 
						# Patches available for the local privilege escalation issue in needrestart 
						# tracked by CVE-2024-48990, CVE-2024-48991, CVE-2024-48992, and CVE-2024-10224 
						# For more see: https://ubuntu.com/blog/needrestart-local-privilege-escalation 
						# 
						The following NEW packages will be installed:
					 

					
						... 
						 
						Fetched 125 MB in 8s (16.3 MB/s) 
						Extracting templates from packages: 100%
					 

					
						...
					 

					
						 
						Restarting the system to load the new kernel will not be handled automatically, so you should consider rebooting. 
						 
						Restarting services... 
						 /etc/needrestart/restart.d/systemd-manager 
						 systemctl restart cron.service polkit.service ssh.service systemd-journald.service systemd-networkd.service systemd-resolved.service systemd-udevd.service walinuxagent.service 
						 
						Service restarts being deferred: 
						 systemctl restart ModemManager.service 
						 /etc/needrestart/restart.d/dbus.service 
						 systemctl restart networkd-dispatcher.service 
						 systemctl restart systemd-logind.service 
						 systemctl restart unattended-upgrades.service 
						 
						No containers need to be restarted. 
						 
						User sessions running outdated binaries: 
						 tmm-azadmin @ session #1: sshd[5579] 
						 tmm-azadmin @ user manager service: systemd[5584] 
						 
						No VM guests are running outdated hypervisor (qemu) binaries on this host.
					 

					
						 
						Restarting the system to load the new kernel will not be handled automatically, so you should consider rebooting. 
						 
						Restarting services... 
						 /etc/needrestart/restart.d/systemd-manager 
						 systemctl restart cron.service polkit.service ssh.service systemd-journald.service systemd-networkd.service systemd-resolved.service systemd-udevd.service walinuxagent.service 
						 
						Service restarts being deferred: 
						 systemctl restart ModemManager.service 
						 /etc/needrestart/restart.d/dbus.service 
						 systemctl restart networkd-dispatcher.service 
						 systemctl restart systemd-logind.service 
						 systemctl restart unattended-upgrades.service 
						 
						No containers need to be restarted. 
						 
						User sessions running outdated binaries: 
						 tmm-azadmin @ session #1: sshd[5579] 
						 tmm-azadmin @ user manager service: systemd[5584] 
						 
						No VM guests are running outdated hypervisor (qemu) binaries on this host.
					 

					
						tmm-azadmin@TMM-GK-UBUNTU-AUTOMATION-DEVOPS:~
					 

					
						tmm-azadmin@TMM-GK-UBUNTU-AUTOMATION-DEVOPS:~$ ssh-keygen -t rsa 
						Generating public/private rsa key pair. 
						Enter file in which to save the key (/home/tmm-azadmin/.ssh/id_rsa): 
						Enter passphrase (empty for no passphrase): 
						Enter same passphrase again: 
						Your identification has been saved in /home/tmm-azadmin/.ssh/id_rsa 
						Your public key has been saved in /home/tmm-azadmin/.ssh/id_rsa.pub 
						The key fingerprint is: 
						SHA256:MbJbzS84... tmm-azadmin@TMM-GK-UBUNTU-AUTOMATION-DEVOPS 
						The key's randomart image is: 
						+---[RSA 3072]----+ 
						|   ..o       ..  | 
						|    . o     .  E | 
						| . . o..o  o .   | 
						|  =XXXXXXXXXX    | 
						| o +  . S=oo .   | 
						|XXXXXXXXXXXXXXXXX| 
						|o ... oo=o....   | 
						|   XXXX   XXXX   | 
						| XxXxXxXxXxXxXxX | 
						+----[SHA256]-----+ 
						tmm-azadmin@TMM-GK-UBUNTU-AUTOMATION-DEVOPS:~$
					 
				
			
		
	

	
		 
	 

	
		Installation of python and pip
	
	As python3 and pip are required, let´s check if both are installed:

	
		
			
				
					
						tmm-azadmin@TMM-GK-UBUNTU-AUTOMATION-DEVOPS:~$ python3 --version 
						Python 3.12.3 
						tmm-azadmin@TMM-GK-UBUNTU-AUTOMATION-DEVOPS:~$ pip3 --version 
						pip 24.0 from /usr/lib/python3/dist-packages/pip (python 3.12)
					 

					
						tmm-azadmin@TMM-GK-UBUNTU-AUTOMATION-DEVOPS:~$
					 
				
			
		
	

	
		 
	 

	
		Installation of GNOME Desktop and XRDP (optional) 
	

	
		Our standard deployment includes the installation of GNOME desktop to provide a GUI if needed. On top of GNOME, we will install XRDP to provide Remote Access to the VM if needed:
	 

	
		
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}		
		
			
				
					
						
							### Installing GNOME Desktop
						 

						
							tmm-azadmin@TMM-GK-UBUNTU-AUTOMATION-DEVOPS:~$ sudo apt install ubuntu-gnome-desktop -y 
							Reading package lists... Done 
							Building dependency tree... Done 
							Reading state information... Done 
							The following additional packages will be installed: 
							  accountsservice acl adwaita-icon-theme alsa-base alsa-topology-conf alsa-ucm-conf alsa-utils anacron apg apport-gtk 
							...
						 

						
							Fetched 679 MB in 1min 10s (9750 kB/s) 
							Extracting templates from packages: 100% 
							Preconfiguring packages ...
						 

						
							...
						 

						
							 
						 

						
							Restarting the system to load the new kernel will not be handled automatically, so you should consider rebooting. 
							 
							Restarting services... 
							 
							Service restarts being deferred: 
							 /etc/needrestart/restart.d/dbus.service 
							 systemctl restart networkd-dispatcher.service 
							 systemctl restart systemd-logind.service 
							 systemctl restart unattended-upgrades.service 
							 
							No containers need to be restarted. 
							 
							User sessions running outdated binaries: 
							 tmm-azadmin @ session #1: sshd[5579] 
							 tmm-azadmin @ user manager service: systemd[5584] 
							 
							No VM guests are running outdated hypervisor (qemu) binaries on this host. 
							tmm-azadmin@TMM-GK-UBUNTU-AUTOMATION-DEVOPS:~$
						 

						
							 
						 

						
							### Installing XRDP
						 

						
							tmm-azadmin@TMM-GK-UBUNTU-AUTOMATION-DEVOPS:~$ sudo apt install xrdp -y 
							Reading package lists... Done 
							Building dependency tree... Done 
							Reading state information... Done 
							The following additional packages will be installed: 
							  libfuse2t64 libpipewire-0.3-modules-xrdp pipewire-module-xrdp xorgxrdp 
							...
						 

						
							Generating 2048 bit rsa key... 
							 
							ssl_gen_key_xrdp1 ok 
							 
							saving to /etc/xrdp/rsakeys.ini 
							 
							Created symlink /etc/systemd/system/multi-user.target.wants/xrdp-sesman.service → /usr/lib/systemd/system/xrdp-sesman.service. 
							Created symlink /etc/systemd/system/multi-user.target.wants/xrdp.service → /usr/lib/systemd/system/xrdp.service. 
							Setting up pipewire-module-xrdp (0.2-2) ... 
							Processing triggers for man-db (2.12.0-4build2) ... 
							Processing triggers for libc-bin (2.39-0ubuntu8.3) ... 
							Scanning processes... 
							Scanning linux images... 
							 
							Running kernel seems to be up-to-date. 
							 
							No services need to be restarted. 
							 
							No containers need to be restarted. 
							 
							No user sessions are running outdated binaries. 
							 
							No VM guests are running outdated hypervisor (qemu) binaries on this host. 
							tmm-azadmin@TMM-GK-UBUNTU-AUTOMATION-DEVOPS:~$
						 
					
				
			
		
	

	
		 
	 

	
		Installation of Terraform 
	

	
		Terraform is the first IaC framework to be installed:
	 

	
		
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}		
		
			
				
					
						
							tmm-azadmin@TMM-GK-UBUNTU-AUTOMATION-DEVOPS:~$ sudo apt install gnupg software-properties-common -y 
							Reading package lists... Done 
							Building dependency tree... Done 
							Reading state information... Done 
							gnupg is already the newest version (2.4.4-2ubuntu17). 
							gnupg set to manually installed. 
							software-properties-common is already the newest version (0.99.49.1). 
							software-properties-common set to manually installed. 
							0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded. 
							tmm-azadmin@TMM-GK-UBUNTU-AUTOMATION-DEVOPS:~$
						 

						
							tmm-azadmin@TMM-GK-UBUNTU-AUTOMATION-DEVOPS:~$ wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg 
							--2024-12-13 15:21:43--  https://apt.releases.hashicorp.com/gpg 
							Resolving apt.releases.hashicorp.com (apt.releases.hashicorp.com)... 13.225.47.104, 13.225.47.21, 13.225.47.116, ... 
							Connecting to apt.releases.hashicorp.com (apt.releases.hashicorp.com)|13.225.47.104|:443... connected. 
							HTTP request sent, awaiting response... 200 OK 
							Length: 3980 (3.9K) [binary/octet-stream] 
							Saving to: ‘STDOUT’ 
							 
							-                             100%[=================================================&gt;]   3.89K  --.-KB/s    in 0s 
							 
							2024-12-13 15:21:43 (488 MB/s) - written to stdout [3980/3980] 
							 
							tmm-azadmin@TMM-GK-UBUNTU-AUTOMATION-DEVOPS:~$
						 

						
							tmm-azadmin@TMM-GK-UBUNTU-AUTOMATION-DEVOPS:~$ echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list 
							deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com noble main 
							tmm-azadmin@TMM-GK-UBUNTU-AUTOMATION-DEVOPS:~$
						 

						
							tmm-azadmin@TMM-GK-UBUNTU-AUTOMATION-DEVOPS:~$ sudo apt update 
							Hit:1 https://packages.microsoft.com/repos/code stable InRelease 
							Hit:2 https://esm.ubuntu.com/apps/ubuntu noble-apps-security InRelease 
							Hit:3 https://esm.ubuntu.com/apps/ubuntu noble-apps-updates InRelease 
							Hit:4 https://esm.ubuntu.com/infra/ubuntu noble-infra-security InRelease 
							Hit:5 https://esm.ubuntu.com/infra/ubuntu noble-infra-updates InRelease 
							Hit:6 http://azure.archive.ubuntu.com/ubuntu noble InRelease 
							Get:7 https://apt.releases.hashicorp.com noble InRelease [12.9 kB] 
							Hit:8 http://azure.archive.ubuntu.com/ubuntu noble-updates InRelease 
							Hit:9 http://azure.archive.ubuntu.com/ubuntu noble-backports InRelease 
							Get:10 https://apt.releases.hashicorp.com noble/main amd64 Packages [161 kB] 
							Hit:11 http://azure.archive.ubuntu.com/ubuntu noble-security InRelease 
							Fetched 174 kB in 1s (146 kB/s) 
							Reading package lists... Done 
							Building dependency tree... Done 
							Reading state information... Done 
							All packages are up to date. 
							tmm-azadmin@TMM-GK-UBUNTU-AUTOMATION-DEVOPS:~$ sudo apt install terraform -y 
							Reading package lists... Done 
							Building dependency tree... Done 
							Reading state information... Done 
							The following NEW packages will be installed: 
							  terraform 
							0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded. 
							Need to get 27.4 MB of archives. 
							After this operation, 90.2 MB of additional disk space will be used. 
							Get:1 https://apt.releases.hashicorp.com noble/main amd64 terraform amd64 1.10.2-1 [27.4 MB] 
							Fetched 27.4 MB in 2s (13.1 MB/s) 
							Selecting previously unselected package terraform. 
							(Reading database ... 179937 files and directories currently installed.) 
							Preparing to unpack .../terraform_1.10.2-1_amd64.deb ... 
							Unpacking terraform (1.10.2-1) ... 
							Setting up terraform (1.10.2-1) ... 
							Scanning processes... 
							Scanning linux images... 
							 
							Running kernel seems to be up-to-date. 
							 
							No services need to be restarted. 
							 
							No containers need to be restarted. 
							 
							No user sessions are running outdated binaries. 
							 
							No VM guests are running outdated hypervisor (qemu) binaries on this host. 
							tmm-azadmin@TMM-GK-UBUNTU-AUTOMATION-DEVOPS:~$
						 

						
							tmm-azadmin@TMM-GK-UBUNTU-AUTOMATION-DEVOPS:~$ terraform --version 
							Terraform v1.10.2 
							on linux_arm64 
							tmm-azadmin@TMM-GK-UBUNTU-AUTOMATION-DEVOPS:~$
						 
					
				
			
		
	

	
		Terraform was successfully installed, and the correct version is reported.  
		 
	 

	
		Installation of Ansible 
	

	
		We will now install Ansible onto the same machine to ensure smooth communication between Ansible and Terraform:
	 

	
		
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}		
		
			
				
					
						
							####Run on all hosts:
						 

						
							tmm-azadmin@TMM-GK-UBUNTU-AUTOMATION-DEVOPS:~$ echo "sysops ALL=(ALL) NOPASSWD:ALL" | sudo tee /etc/sudoers.d/sysops 
							sysops ALL=(ALL) NOPASSWD:ALL 
							 
						 

						
							tmm-azadmin@TMM-GK-UBUNTU-AUTOMATION-DEVOPS:~$ sudo apt update 
							Hit:1 https://packages.microsoft.com/repos/code stable InRelease 
							Hit:2 https://esm.ubuntu.com/apps/ubuntu noble-apps-security InRelease 
							Hit:3 https://esm.ubuntu.com/apps/ubuntu noble-apps-updates InRelease 
							Hit:4 https://esm.ubuntu.com/infra/ubuntu noble-infra-security InRelease 
							Hit:5 https://esm.ubuntu.com/infra/ubuntu noble-infra-updates InRelease 
							Hit:6 http://azure.archive.ubuntu.com/ubuntu noble InRelease 
							Hit:7 http://azure.archive.ubuntu.com/ubuntu noble-updates InRelease 
							Hit:8 https://apt.releases.hashicorp.com noble InRelease 
							Hit:9 http://azure.archive.ubuntu.com/ubuntu noble-backports InRelease 
							Hit:10 http://azure.archive.ubuntu.com/ubuntu noble-security InRelease 
							Reading package lists... Done 
							Building dependency tree... Done 
							Reading state information... Done 
							All packages are up to date. 
							tmm-azadmin@TMM-GK-UBUNTU-AUTOMATION-DEVOPS:~$ sudo apt upgrade -y 
							 
							 
							Reading package lists... Done 
							Building dependency tree... Done 
							Reading state information... Done 
							Calculating upgrade... Done 
							# 
							# Patches available for the local privilege escalation issue in needrestart 
							# tracked by CVE-2024-48990, CVE-2024-48991, CVE-2024-48992, and CVE-2024-10224 
							# For more see: https://ubuntu.com/blog/needrestart-local-privilege-escalation 
							# 
							0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded. 
							tmm-azadmin@TMM-GK-UBUNTU-AUTOMATION-DEVOPS:~$ sudo apt install -y software-properties-common 
							Reading package lists... Done 
							Building dependency tree... Done 
							Reading state information... Done 
							software-properties-common is already the newest version (0.99.49.1). 
							0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded. 
							tmm-azadmin@TMM-GK-UBUNTU-AUTOMATION-DEVOPS:~$ sudo add-apt-repository --yes --update ppa:ansible/ansible 
							Repository: 'Types: deb 
							URIs: https://ppa.launchpadcontent.net/ansible/ansible/ubuntu/ 
							Suites: noble 
							Components: main 
							' 
							Description: 
							Ansible is a radically simple IT automation platform that makes your applications and systems easier to deploy. Avoid writing scripts or custom code to deploy and update your applications— automate in a language that approaches plain English, using SSH, with no agents to install on remote systems. 
							 
							http://ansible.com/ 
							 
							If you face any issues while installing Ansible PPA, file an issue here: 
							https://github.com/ansible-community/ppa/issues 
							More info: https://launchpad.net/~ansible/+archive/ubuntu/ansible 
							Adding repository. 
							Hit:1 https://packages.microsoft.com/repos/code stable InRelease 
							Hit:2 https://esm.ubuntu.com/apps/ubuntu noble-apps-security InRelease 
							Hit:3 https://esm.ubuntu.com/apps/ubuntu noble-apps-updates InRelease 
							Get:4 https://ppa.launchpadcontent.net/ansible/ansible/ubuntu noble InRelease [17.8 kB] 
							Hit:5 https://esm.ubuntu.com/infra/ubuntu noble-infra-security InRelease 
							Hit:6 https://esm.ubuntu.com/infra/ubuntu noble-infra-updates InRelease 
							Hit:7 http://azure.archive.ubuntu.com/ubuntu noble InRelease 
							Get:8 https://ppa.launchpadcontent.net/ansible/ansible/ubuntu noble/main amd64 Packages [772 B] 
							Get:9 https://ppa.launchpadcontent.net/ansible/ansible/ubuntu noble/main Translation-en [472 B] 
							Hit:10 http://azure.archive.ubuntu.com/ubuntu noble-updates InRelease 
							Hit:11 https://apt.releases.hashicorp.com noble InRelease 
							Hit:12 http://azure.archive.ubuntu.com/ubuntu noble-backports InRelease 
							Hit:13 http://azure.archive.ubuntu.com/ubuntu noble-security InRelease 
							Fetched 19.0 kB in 1s (18.7 kB/s) 
							Reading package lists... Done 
							tmm-azadmin@TMM-GK-UBUNTU-AUTOMATION-DEVOPS:~$ sudo apt update 
							Hit:1 https://esm.ubuntu.com/apps/ubuntu noble-apps-security InRelease 
							Hit:2 https://esm.ubuntu.com/apps/ubuntu noble-apps-updates InRelease 
							Hit:3 https://esm.ubuntu.com/infra/ubuntu noble-infra-security InRelease 
							Hit:4 https://esm.ubuntu.com/infra/ubuntu noble-infra-updates InRelease 
							Hit:5 https://packages.microsoft.com/repos/code stable InRelease 
							Hit:6 http://azure.archive.ubuntu.com/ubuntu noble InRelease 
							Hit:7 http://azure.archive.ubuntu.com/ubuntu noble-updates InRelease 
							Hit:8 https://apt.releases.hashicorp.com noble InRelease 
							Hit:9 http://azure.archive.ubuntu.com/ubuntu noble-backports InRelease 
							Hit:10 http://azure.archive.ubuntu.com/ubuntu noble-security InRelease 
							Hit:11 https://ppa.launchpadcontent.net/ansible/ansible/ubuntu noble InRelease 
							Reading package lists... Done 
							Building dependency tree... Done 
							Reading state information... Done 
							All packages are up to date. 
							tmm-azadmin@TMM-GK-UBUNTU-AUTOMATION-DEVOPS:~$ sudo apt install -y ansible 
							Reading package lists... Done 
							Building dependency tree... Done 
							Reading state information... Done 
							The following additional packages will be installed: 
							  ansible-core python3-kerberos python3-ntlm-auth python3-requests-ntlm python3-resolvelib python3-winrm 
							  python3-xmltodict sshpass 
							The following NEW packages will be installed: 
							  ansible ansible-core python3-kerberos python3-ntlm-auth python3-requests-ntlm python3-resolvelib python3-winrm 
							  python3-xmltodict sshpass 
							0 upgraded, 9 newly installed, 0 to remove and 0 not upgraded. 
							Need to get 19.0 MB of archives. 
							After this operation, 212 MB of additional disk space will be used. 
							Get:1 https://ppa.launchpadcontent.net/ansible/ansible/ubuntu noble/main amd64 ansible-core all 2.17.7-1ppa~noble [1015 kB] 
							Get:2 http://azure.archive.ubuntu.com/ubuntu noble/universe amd64 python3-resolvelib all 1.0.1-1 [25.7 kB] 
							Get:3 https://ppa.launchpadcontent.net/ansible/ansible/ubuntu noble/main amd64 ansible all 10.7.0-1ppa~noble [17.8 MB] 
							Get:4 http://azure.archive.ubuntu.com/ubuntu noble/universe amd64 python3-kerberos amd64 1.1.14-3.1build9 [21.2 kB] 
							Get:5 http://azure.archive.ubuntu.com/ubuntu noble/universe amd64 python3-ntlm-auth all 1.5.0-1 [21.3 kB] 
							Get:6 http://azure.archive.ubuntu.com/ubuntu noble/universe amd64 python3-requests-ntlm all 1.1.0-3 [6308 B] 
							Get:7 http://azure.archive.ubuntu.com/ubuntu noble/main amd64 python3-xmltodict all 0.13.0-1 [13.4 kB] 
							Get:8 http://azure.archive.ubuntu.com/ubuntu noble/universe amd64 python3-winrm all 0.4.3-2 [31.9 kB] 
							Get:9 http://azure.archive.ubuntu.com/ubuntu noble/universe amd64 sshpass amd64 1.09-1 [11.7 kB] 
							Fetched 19.0 MB in 3s (6995 kB/s) 
							Selecting previously unselected package python3-resolvelib. 
							(Reading database ... 179940 files and directories currently installed.) 
							Preparing to unpack .../0-python3-resolvelib_1.0.1-1_all.deb ... 
							Unpacking python3-resolvelib (1.0.1-1) ... 
							Selecting previously unselected package ansible-core. 
							Preparing to unpack .../1-ansible-core_2.17.7-1ppa~noble_all.deb ... 
							Unpacking ansible-core (2.17.7-1ppa~noble) ... 
							Selecting previously unselected package ansible. 
							Preparing to unpack .../2-ansible_10.7.0-1ppa~noble_all.deb ... 
							Unpacking ansible (10.7.0-1ppa~noble) ... 
							Selecting previously unselected package python3-kerberos. 
							Preparing to unpack .../3-python3-kerberos_1.1.14-3.1build9_amd64.deb ... 
							Unpacking python3-kerberos (1.1.14-3.1build9) ... 
							Selecting previously unselected package python3-ntlm-auth. 
							Preparing to unpack .../4-python3-ntlm-auth_1.5.0-1_all.deb ... 
							Unpacking python3-ntlm-auth (1.5.0-1) ... 
							Selecting previously unselected package python3-requests-ntlm. 
							Preparing to unpack .../5-python3-requests-ntlm_1.1.0-3_all.deb ... 
							Unpacking python3-requests-ntlm (1.1.0-3) ... 
							Selecting previously unselected package python3-xmltodict. 
							Preparing to unpack .../6-python3-xmltodict_0.13.0-1_all.deb ... 
							Unpacking python3-xmltodict (0.13.0-1) ... 
							Selecting previously unselected package python3-winrm. 
							Preparing to unpack .../7-python3-winrm_0.4.3-2_all.deb ... 
							Unpacking python3-winrm (0.4.3-2) ... 
							Selecting previously unselected package sshpass. 
							Preparing to unpack .../8-sshpass_1.09-1_amd64.deb ... 
							Unpacking sshpass (1.09-1) ... 
							Setting up python3-ntlm-auth (1.5.0-1) ... 
							Setting up python3-resolvelib (1.0.1-1) ... 
							Setting up python3-kerberos (1.1.14-3.1build9) ... 
							Setting up ansible-core (2.17.7-1ppa~noble) ... 
							Setting up sshpass (1.09-1) ... 
							Setting up python3-xmltodict (0.13.0-1) ... 
							Setting up ansible (10.7.0-1ppa~noble) ... 
							Setting up python3-requests-ntlm (1.1.0-3) ... 
							Setting up python3-winrm (0.4.3-2) ... 
							Processing triggers for man-db (2.12.0-4build2) ... 
							Scanning processes... 
							Scanning linux images... 
							 
							Running kernel seems to be up-to-date. 
							 
							No services need to be restarted. 
							 
							No containers need to be restarted. 
							 
							No user sessions are running outdated binaries. 
							 
							No VM guests are running outdated hypervisor (qemu) binaries on this host. 
							tmm-azadmin@TMM-GK-UBUNTU-AUTOMATION-DEVOPS:~$ ansible --version 
							ansible [core 2.17.7] 
							  config file = /etc/ansible/ansible.cfg 
							  configured module search path = ['/home/tmm-azadmin/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules'] 
							  ansible python module location = /usr/lib/python3/dist-packages/ansible 
							  ansible collection location = /home/tmm-azadmin/.ansible/collections:/usr/share/ansible/collections 
							  executable location = /usr/bin/ansible 
							  python version = 3.12.3 (main, Nov  6 2024, 18:32:19) [GCC 13.2.0] (/usr/bin/python3) 
							  jinja version = 3.1.2 
							  libyaml = True 
							tmm-azadmin@TMM-GK-UBUNTU-AUTOMATION-DEVOPS:~$
						 
					
				
			
		

		
			Ansible was successfully installed, and the correct version is reported.  
			 
		 

		
			Installation of important Ansible Libraries
		

		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
			
				
					
						
							
								####Run on all hosts:
							 

							
								tmm-azadmin@TMM-GK-UBUNTU-AUTOMATION-DEVOPS:~$ ansible-galaxy collection install azure.azcollection --force 
								Starting galaxy collection install process 
								Process install dependency map 
								Starting collection install process 
								Downloading https://galaxy.ansible.com/api/v3/plugin/ansible/content/published/collections/artifacts/azure-azcollection-3.3.1.tar.gz to /home/azadmin/.ansible/tmp/ansible-local-4576vflvb_gi/tmpu0orj7n3/azure-azcollection-3.3.1-wid_5_qj 
								Installing 'azure.azcollection:3.3.1' to '/home/azadmin/.ansible/collections/ansible_collections/azure/azcollection' 
								azure.azcollection:3.3.1 was installed successfully
							 
						
					
				
			
		

		
			 
			Installation of Packer
		

		
			The last framework to install is Packer:
		 

		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
			
				
					
						
							
								tmm-azadmin@TMM-GK-UBUNTU-AUTOMATION-DEVOPS:~$ sudo apt install packer 
								Reading package lists... Done 
								Building dependency tree... Done 
								Reading state information... Done 
								The following NEW packages will be installed: 
								  packer 
								0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded. 
								Need to get 15.4 MB of archives. 
								After this operation, 49.7 MB of additional disk space will be used. 
								Get:1 https://apt.releases.hashicorp.com noble/main amd64 packer amd64 1.11.2-1 [15.4 MB] 
								Fetched 15.4 MB in 2s (8383 kB/s) 
								Selecting previously unselected package packer. 
								(Reading database ... 205335 files and directories currently installed.) 
								Preparing to unpack .../packer_1.11.2-1_amd64.deb ... 
								Unpacking packer (1.11.2-1) ... 
								Setting up packer (1.11.2-1) ... 
								Scanning processes... 
								Scanning linux images... 
								 
								Running kernel seems to be up-to-date. 
								 
								No services need to be restarted. 
								 
								No containers need to be restarted. 
								 
								No user sessions are running outdated binaries. 
								 
								No VM guests are running outdated hypervisor (qemu) binaries on this host. 
								tmm-azadmin@TMM-GK-UBUNTU-AUTOMATION-DEVOPS:~$ packer --version 
								Packer v1.11.2 
								tmm-azadmin@TMM-GK-UBUNTU-AUTOMATION-DEVOPS:~$
							 
						
					
				
			
		

		
			Packer was successfully installed, and the correct version is reported. 
		 

		
			After installation of the IaC frameworks, some further configuration steps are needed.
		 

		
			 
		 

		
			Configuration of the IaC Virtual Machine
		

		
			Configuration of Terraform
		

		
			After successfully installing Terraform, no special configuration steps are necessary to use Terraform. 
			 
		 

		
			Configuration of the Ansible Control Node 
		

		
			After successfully installing Ansible, you may need to go through further configuration steps to get a working Control Node. 
			 
		 

		
			Modify ansible.cfg
		

		
			ansible.cfg stores relevant configuration details.
		 

		
			
				Note: 
			 

			
				You can find more information about configuring Ansible using ansible.cfg here: https://github.com/ansible/ansible/blob/stable-2.9/examples/ansible.cfg  
				Configure the ansible.cfg hile according to your needs and best practises. 
				 
			 
		

		
			 
		 

		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
			
				
					
						
							tmm-azadmin@TMM-GK-UBUNTU-AUTOMATION-DEVOPS:/etc/ansible$ sudo nano ansible.cfg
						
					
				
			
		

		
			 
		 

		
			Modify hosts
		

		
			The hosts file stores information about the Managed Nodes, which Ansible calls to deploy the desired configurations.
		 

		
			Example of a hosts file (explicitly listing all information for demonstration purposes): 
			In our deployment, we use WinRM to communicate between the Ansible Control Node and the Managed Nodes = Windows-based Machines
		 

		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
			
				
					
						
							tmm-azadmin@TMM-GK-UBUNTU-AUTOMATION-DEVOPS:/etc/ansible$ sudo nano hosts
						
					
				
			
		

		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
			
				
					
						
							
								[cloudconnector1-ip] 
								10.53.16.101 
								 
								[cloudconnector2-ip] 
								10.53.16.102 
								 
								[mi-ip] 
								10.53.16.11 
								 
								[cloudconnector1-nb] 
								TF-AZ-WEUR-CC1 
								 
								[cloudconnector2-nb] 
								TF-AZ-WEUR-CC2 
								 
								[cloudconnector1-dns] 
								TF-AZ-WEUR-CC1.WWCO.NET 
								 
								[cloudconnector2-dns] 
								TF-AZ-WEUR-CC2.WWCO.NET
							 

							
								[cloudconnector1-ip:vars] 
								ansible_user="azXxXxXxXxXxXxX" 
								ansible_password="XxXxXxXxXxXxXxXxXxXxXxXxX" 
								ansible_connection=winrm 
								ansible_winrm_transport=basic 
								ansible_winrm_port=5985 
								ansible_winrm_server_cert_validation=ignore 
								 
								[cloudconnector2-ip:vars] 
								ansible_user="azXxXxXxXxXxXxX" 
								ansible_password="XxXxXxXxXxXxXxXxXxXxXxXxX" 
								ansible_connection=winrm 
								ansible_winrm_transport=basic 
								ansible_winrm_port=5985 
								ansible_winrm_server_cert_validation=ignore 
								 
								[mi-ip:vars] 
								ansible_user="wwXxXxXxXxXxXxX" 
								ansible_password="XxXxXxXxXxXxXxXxXxXxXxXxX" 
								ansible_connection=winrm 
								ansible_winrm_transport=basic 
								ansible_winrm_port=5985 
								ansible_winrm_server_cert_validation=ignore
							 

							
								 
								[cloudconnectors-dns:vars] 
								ansible_user="wwXxXxXxXxXxXxX" 
								ansible_password=XxXxXxXxXxXxXxXxXxXxXxXxX 
								ansible_connection=winrm 
								ansible_winrm_transport=basic 
								ansible_winrm_server_cert_validation=ignore
							 
						
					
				
			
		

		
			 
		 

		
			Configure WinRM on the Control Node
		

		
			Ansible needs a unique Python library to enable WinRM communication – pywinrm. 
			If it is not automatically installed during the Ansible installation. you need to install it manually on the Control Node:
		 

		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
			
				
					
						
							tmm-azadmin@TMM-GK-UBUNTU-AUTOMATION-DEVOPS:/etc/ansible$ pipx install pywinrm --include-deps
							
								  installed package pywinrm 0.5.0, installed using Python 3.12.3 
								  These apps are now globally available 
								    - normalizer 
								    - pyspnego-parse 
								done!
							 

							
								tmm-azadmin@TMM-GK-UBUNTU-AUTOMATION-DEVOPS:/etc/ansible$ 
							 
						
					
				
			
		

		
			The Control Node is now ready to use WinRM to communicate with Windows-based machines. 
			 
		 

		
			Configure WinRM on the Target Machines
		

		
			As mentioned, WinRM is used for communication not only between the Ansible Control Node and the target machines, but also between Terraform and the target machines. 
			Ensure that the target machines accept WinRM calls – you can configure the Windows Machines for WinRM using GPOs.
		 

		
			
				Caution:
			 

			
				The GPOs will not work on Windows Machines that are not Domain-joined. 
				Therefore, you need to configure these machines using local policies.
			 

			
				This step is imperative for the Master Images, which are not Domain-joined, but need to be accessible by Terraform and Ansible. 
				 
			 
		

		
			We recommend trying the communication flow before starting automation deployments. 
			You can use a short Terraform snippet to check If the WinRM communication works as intended:  
		 

		
			Terraform code for testing WinRM - It simply tries to upload and execute a PowerShell script on the target servers (example based on a Windows-based Terraform deployment, adjust according to your needs) :
		 

		
			
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}			
			
				
					
						
							
								
									resource "null_resource" "UploadTestFileToCC1" {
								 

								
									 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 Test script to CC1
								 

								
									  provisioner "file" {
								 

								
									     source      = "${path.module}/data/Test-Script.ps1"
								 

								
									    destination = "c:/temp/Test-Script.ps1"
								 

								
									    
								 

								
									  } 
								 

								
									}
								 

								
									 
								 

								
									resource "null_resource" "UploadTestFileToCC2" {
								 

								
									 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 Test script to CC2
								 

								
									  provisioner "file" {
								 

								
									    source      = "${path.module}/data/Test-Script.ps1"
								 

								
									    destination = "c:/temp/Test-Script.ps1"
								 

								
									    
								 

								
									  } 
								 

								
									}
								 

								
									 
									 
								 

								
									resource "null_resource" "InstallSFonDDC1" {
								 

								
									depends_on = [ null_resource.UploadTestFileToCC1 ]
								 

								
									connection {
								 

								
									    type            = var.Provisioner_Type
								 

								
									    user            = var.Provisioner_Admin-Username
								 

								
									    password        = var.Provisioner_Admin-Password
								 

								
									    host            = var.Provisioner_DDC1-IP
								 

								
									    timeout         = var.Provisioner_Timeout
								 

								
									 
								 

								
									  }
								 

								
									 provisioner "remote-exec" {
								 

								
									    inline = [
								 

								
									      "powershell -File c:/temp/Test-Script.ps1"
								 

								
									    ]
								 

								
									  }
								 

								
									}
								 

								
									 
								 

								
									resource "null_resource" "InstallSFonDDC2" {
								 

								
									depends_on = [ null_resource.UploadTestFileToCC2 ]
								 

								
									connection {
								 

								
									    type            = var.Provisioner_Type
								 

								
									    user            = var.Provisioner_Admin-Username
								 

								
									    password        = var.Provisioner_Admin-Password
								 

								
									    host            = var.Provisioner_DDC2-IP
								 

								
									    timeout         = var.Provisioner_Timeout
								 

								
									 
								 

								
									  }
								 

								
									 provisioner "remote-exec" {
								 

								
									    inline = [
								 

								
									      "powershell -File c:/temp/Test-Script.ps1"
								 

								
									    ]
								 

								
									  }
								 

								
									}
								 
							
						
					
				
			
		

		
			Sample output of the Terraform Provider: 
		 

		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
			
				
					
						
							
								PS C:\TMM-Team\TestWinRM&gt; terraform apply 
								 
								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: 
								 
								  # null_resource.InstallSFonDDC1 will be created 
								  + resource "null_resource" "InstallSFonDDC1" { 
								      + id = (known after apply) 
								    } 
								 
								  # null_resource.InstallSFonDDC2 will be created 
								  + resource "null_resource" "InstallSFonDDC2" { 
								      + id = (known after apply) 
								    } 
								 
								  # null_resource.UploadTestFileToCC1 will be created 
								  + resource "null_resource" "UploadTestFileToCC1" { 
								      + id = (known after apply) 
								    } 
								 
								  # null_resource.UploadTestFileToCC2 will be created 
								  + resource "null_resource" "UploadTestFileToCC2" { 
								      + id = (known after apply) 
								    } 
								 
								Plan: 4 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.UploadTestFileToCC2: Creating... 
								null_resource.UploadTestFileToCC1: Creating... 
								null_resource.UploadTestFileToCC2: Provisioning with 'file'... 
								null_resource.UploadTestFileToCC1: Provisioning with 'file'... 
								#&lt; CLIXML 
								&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
								&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
								&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadTestFileToCC2: Creation complete after 1s [id=918033952701580746] 
								null_resource.InstallSFonDDC2: Creating... 
								null_resource.InstallSFonDDC2: Provisioning with 'remote-exec'... 
								null_resource.InstallSFonDDC2 (remote-exec): Connecting to remote host via WinRM... 
								null_resource.InstallSFonDDC2 (remote-exec):   Host: 10.53.16.102 
								null_resource.InstallSFonDDC2 (remote-exec):   Port: 5985 
								null_resource.InstallSFonDDC2 (remote-exec):   User: azXXXXXXXX 
								null_resource.InstallSFonDDC2 (remote-exec):   Password: true 
								null_resource.InstallSFonDDC2 (remote-exec):   HTTPS: false 
								null_resource.InstallSFonDDC2 (remote-exec):   Insecure: false 
								null_resource.InstallSFonDDC2 (remote-exec):   NTLM: false 
								null_resource.InstallSFonDDC2 (remote-exec):   CACert: false 
								null_resource.InstallSFonDDC2 (remote-exec): Connected! 
								#&lt; CLIXML 
								&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadTestFileToCC1: Creation complete after 1s [id=8367997441106547133] 
								null_resource.InstallSFonDDC1: Creating... 
								null_resource.InstallSFonDDC1: Provisioning with 'remote-exec'... 
								null_resource.InstallSFonDDC1 (remote-exec): Connecting to remote host via WinRM... 
								null_resource.InstallSFonDDC1 (remote-exec):   Host: 10.53.16.101 
								null_resource.InstallSFonDDC1 (remote-exec):   Port: 5985 
								null_resource.InstallSFonDDC1 (remote-exec):   User: azXXXXXXXX 
								null_resource.InstallSFonDDC1 (remote-exec):   Password: true 
								null_resource.InstallSFonDDC1 (remote-exec):   HTTPS: false 
								null_resource.InstallSFonDDC1 (remote-exec):   Insecure: false 
								null_resource.InstallSFonDDC1 (remote-exec):   NTLM: false 
								null_resource.InstallSFonDDC1 (remote-exec):   CACert: false 
								null_resource.InstallSFonDDC1 (remote-exec): Connected! 
								#&lt; CLIXML 
								&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
								&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
								&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
								null_resource.InstallSFonDDC2 (remote-exec): C:\Users\azXXXXXXXX&gt;powershell -File c:/temp/Test-Script.ps1 
								#&lt; CLIXML 
								&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
								&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
								null_resource.InstallSFonDDC1 (remote-exec): C:\Users\azXXXXXXXX&gt;powershell -File c:/temp/Test-Script.ps1 
								#&lt; CLIXML 
								&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.InstallSFonDDC2: Creation complete after 3s [id=4379278538003823966] 
								#&lt; CLIXML 
								&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
								&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.InstallSFonDDC1: Creation complete after 4s [id=6466929064112579037] 
								 
								Apply complete! Resources: 4 added, 0 changed, 0 destroyed. 
								PS C:\TMM-Team\TestWinRM&gt;
							 
						
					
				
			
		

		
			The output shows a flawless communication flow. WinRM is working as intended. 
			 
		 

		
			Test the Ansible communication flow between the Control Node and the Managed Nodes
		

		
			Assuming that Ansible and its configurations are correct, you can try to deploy an Ansible Playbook on a Managed Node in check mode – Ansible will not alter anything but show you if a smooth run would occur or errors can be expected:
		 

		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
			
				
					
						
							
								tmm-azadmin@TMM-GK-UBUNTU-AUTOMATION-DEVOPS:/etc/ansible$ ansible-playbook /etc/ansible/Join-VMToDomain-CC1.microsoft.ad.ansible.yml --check -c winrm 
								 
								PLAY [join host to domain with automatic reboot] *********************************************************************** 
								 
								TASK [Gathering Facts] ************************************************************************************************* 
								ok: [10.53.16.101] 
								 
								TASK [join host to domain with automatic reboot] *********************************************************************** 
								ok: [10.53.16.101] 
								 
								PLAY RECAP ************************************************************************************************************* 
								10.53.16.101               : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 
								 
								tmm-azadmin@TMM-GK-UBUNTU-AUTOMATION-DEVOPS:/etc/ansible$
							 
						
					
				
			
		

		
			This output shows that the Ansible Control Node can successfully contact a Managed Node and run a Playbook. 
			 
		 

		
			Test the communication flow between Terraform and Ansible 
		

		
			The last test checks if Terraform can call the Ansible Control Node to run an Ansible Playbook. 
			You can use the following Terraform snippet:
		 

		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
			
				
					
						
							 
							PS C:\TMM-Team\UseAnsibleToJoinDomain&gt; terraform apply 
							 
							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: 
							 
							  # null_resource.AddCC1ToDomain will be created 
							  + resource "null_resource" "AddCC1ToDomain" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.CopyPlaybookForCC1ToAnsibleServer will be created 
							  + resource "null_resource" "CopyPlaybookForCC1ToAnsibleServer" { 
							      + id = (known after apply) 
							    } 
							 
							Plan: 2 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.CopyPlaybookForCC1ToAnsibleServer: Creating... 
							null_resource.CopyPlaybookForCC1ToAnsibleServer: Provisioning with 'file'... 
							null_resource.CopyPlaybookForCC1ToAnsibleServer: Creation complete after 1s [id=4032269803971053850] 
							null_resource.AddCC1ToDomain: Creating... 
							null_resource.AddCC1ToDomain: Provisioning with 'remote-exec'... 
							null_resource.AddCC1ToDomain (remote-exec): Connecting to remote host via SSH... 
							null_resource.AddCC1ToDomain (remote-exec):   Host: 10.53.16.10 
							null_resource.AddCC1ToDomain (remote-exec):   User: azXXXXXXXXXX 
							null_resource.AddCC1ToDomain (remote-exec):   Password: true 
							null_resource.AddCC1ToDomain (remote-exec):   Private key: false 
							null_resource.AddCC1ToDomain (remote-exec):   Certificate: false 
							null_resource.AddCC1ToDomain (remote-exec):   SSH Agent: false 
							null_resource.AddCC1ToDomain (remote-exec):   Checking Host Key: false 
							null_resource.AddCC1ToDomain (remote-exec):   Target Platform: unix 
							null_resource.AddCC1ToDomain (remote-exec): Connected! 
							 
							null_resource.AddCC1ToDomain (remote-exec): PLAY [join host to domain with automatic reboot] ******************************* 
							 
							null_resource.AddCC1ToDomain (remote-exec): TASK [Gathering Facts] ********************************************************* 
							null_resource.AddCC1ToDomain (remote-exec): ok: [10.53.16.101] 
							 
							null_resource.AddCC1ToDomain (remote-exec): TASK [join host to domain with automatic reboot] ******************************* 
							null_resource.AddCC1ToDomain (remote-exec): ok: [10.53.16.101] 
							 
							null_resource.AddCC1ToDomain (remote-exec): PLAY RECAP ********************************************************************* 
							null_resource.AddCC1ToDomain (remote-exec): 10.53.16.101               : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 
							 
							null_resource.AddCC1ToDomain: Creation complete after 6s [id=4275192198217677814] 
							 
							Apply complete! Resources: 2 added, 0 changed, 0 destroyed. 
							PS C:\TMM-Team\UseAnsibleToJoinDomain&gt;
						
					
				
			

			
				This output shows that Terraform can successfully connect to the Ansible Control Node and call to run a Playbook.
			 

			
				Configuration of Packer
			

			
				After successfully installing Packer, some special configuration steps are necessary to use Packer. 
				We will cover these configurations in the upcoming guides as they are Hypervisor-/Hyperscaler-specific. 
				 
			 

			
				Summary
			

			
				All needed steps to configure a Virtual Machine for IaC are now complete. 
				Please watch out for the upcoming guides showcasing the usage of Ansible, Terraform, and Packer to deploy Citrix Infrastructure-as-Code-based Environments.
			 

			
				 
			 

			
				 
			 
		
	


.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}]]></description><pubDate>Fri, 21 Mar 2025 13:52:31 +0000</pubDate></item><item><title>Using Citrix Automation with Terraform and Ansible to deploy Citrix DaaS on Microsoft Azure (2025 Update)</title><link>https://community.stage.citrix.com/tech-zone/build/deployment-guides/daas-azure-iac/</link><description><![CDATA[Using Citrix Automation with Terraform and Ansible to deploy Citrix DaaS on Microsoft Azure (2025 Update).pdf
 


	Using Citrix Automation with Terraform and Ansible to deploy Citrix DaaS on Microsoft Azure (2025 Update)
 


	 
 


	Overview



	This guide provides an overview of using Terraform and Ansible to create a complete Citrix DaaS deployment on Microsoft Azure.
 


	At the end of the process, you will have created:
 


	
		A new Citrix Cloud Resource Location (RL) on Azure (optional)
	
	
		A Shared Image Gallery (SIG) on Azure
	
	
		A Shared Master Image Definition and a shared Master Image Version stored on the SIG
	
	
		Two Cloud Connector Virtual Machines registered with the Active Directory Domain and the Resource Location.
	
	
		A Hypervisor Connection and a Hypervisor Pool pointing to the new Resource Location in Azure
	
	
		A Machine Catalog based on a Prepared Image 
	
	
		A Delivery Group based on the Machine Catalog with full Autoscale-Support
	
	
		A default Citrix Policy Set
	
	
		Examples of Admin Scopes and Roles
	
	
		A default configuration of Citrix Workspace App and Citrix Enterprise Browser using the Citrix Global App Configuration service. 
		 
	



	
		IMPORTANT:
	 

	
		This version of the guide does not need additional components like REST-API- and PowerShell calls out of Terraform anymore. We also do not require additional software components like the Remote PowerShell SDK. 
		Only Terraform, combined with Ansible, is used for the automated deployment. 
		 
	 



	 
 


	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. They are created in the native declarative configuration language, 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 and Configuration of Terraform



	Please find a description of installing Terraform and initial configurations in our Tech Zone Deployment Guide Installing and Configuring Ansible, Terraform, and Packer for Citrix Infrastructure-as-Code-based Environments
 


	 
 


	What is Ansible



	Ansible provides automation for managing cloud and on-premises infrastructure. 
	Some everyday use cases for Ansible are:
 


	
		Manage and maintain system configuration
	
	
		Deploy complex software
	



	Ansible automates tasks using simple, human-readable scripts written in YAML called Ansible Playbooks. In these playbooks, you declare the desired state of a local or remote system, and Ansible ensures that the system remains in that state.
 


	 
 


	Installation and Configuration of Ansible



	Please find a description of installing Ansible and initial configurations in our Tech Zone Deployment Guide Installing and Configuring Ansible, Terraform, and Packer for Citrix Infrastructure-as-Code-based Environments
 


	 
 


	What are the benefits of using IaC Frameworks



	Our guides use Terraform to deploy the infrastructure components in Azure and the entities in Citrix Cloud. Unfortunately, not all Terraform providers have all the necessary functionalities, such as putting a Virtual Machine into an Active Directory Domain or installing needed Software components on the Virtual Machines—for example, to deploy the Cloud Connector software on the Cloud Connector VMs. 
	We used PowerShell and the Citrix Remote PowerShell SDK in prior guide versions, which were directly called out of the adjacent Terraform code snippets. This approach has significantly complicated the deployments due to numerous dependencies.
 


	Starting with this guide, we use Ansible to add the Virtual Machines created by Terraform to the Active Directory Domain and automatically deploy the Cloud Connector software without calling PowerShell anymore. This approach allows us to be far more flexible. Terraform and Ansible can be installed on the same machine or on different hosts to meet your needs. 
	 
 


	Prerequisites



	Image Templates



	All the Images used in this guide to deploy the Virtual Machines are part of the Shared Image Gallery that Terraform will create in the first module.
 


	
		IMPORTANT: 
	 

	
		Ensure you have a Windows Server-based Virtual Machine that is not Domain-joined, configured according to all your needs, and allows WinRM connectivity (see below). 
		The OS disk of this Virtual Machine will be the source for the Shared Image Gallery version.
	 

	
		Terraform will use it to deploy the needed Windows Server-based Virtual Machines.
	 



	You can use PowerShell to retrieve all the needed entity values. Write down the required entity information for later use in the corresponding .auto.tfvars.json files so the Terraform provider can use this Information.
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PowerShell 7.5.0 
						PS C:\_TMM-Team&gt; Connect-AzAccount 
						Please select the account you want to login with. 
						 
						Retrieving subscriptions for the selection... 
						 
						Subscription name Tenant 
						----------------- ------ 
						WWCO-US-East      Citrix (TM-WWCO) 
						 
						PS C:\_TMM-Team&gt; Get-AzResourceGroup | FT ResourceGroupName 
						 
						ResourceGroupName 
						----------------- 
						Core-rg 
						az-eus-vm-rg 
						citrix-xd-0cf5f2cb-7a1b-4846-a8df-e2602927299c-vb5ze 
						imprivata1-rg 
						Prod-VDA-rg 
						Win365-rg 
						TMM-SGaXXXX-RG 
						TMM-GKrXXXX-RG 
						TMM-SBeXXXX-RG 
						TMM-SGaXXXX-RG-UKS 
						NetworkWatcherRG 
						TMM-TEAM-RG-WEUR 
						citrix-xd-f45db9c7-cd06-46ce-b1bd-53231379480d-dp6d9 
						citrix-xd-e1e583b8-f808-4ac1-8490-f26998a7b40b-8nh3s 
						 
						PS C:\_TMM-Team&gt;
					 

					
						PS C:\_TMM-Team&gt; Get-AzDisk -ResourceGroupName 'TMM-TEAM-RG-WEUR' | FT Name,Id 
						 
						Name                                                                        Id 
						----                                                                        -- 
						az-eur-w2k25-m_OsDisk_1_b1d5ab4c60794df5b8a1907314ce65e0                    /PS C:\_TMM-Team&gt;subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TEAM-RG-WEUR/providers/Microsoft.C… 
						AZ-West-EUR-W22-CC1_OsDisk_1_5ba3e7faff854eba9e9030f2c040efae               /PS C:\_TMM-Team&gt;subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TEAM-RG-WEUR/providers/Microsoft.C… 
						AZ-West-EUR-W22-CC2_OsDisk_1_df87de18dc0d4b08b7bc864264cd6d91               /PS C:\_TMM-Team&gt;subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TEAM-RG-WEUR/providers/Microsoft.C… 
						az-west-eur-w25-adm_OsDisk_1_a1f2dfa8208c4ef28ddcc23d073f7245               /PS C:\_TMM-Team&gt;subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TEAM-RG-WEUR/providers/Microsoft.C… 
						az-weur-cc-test_OsDisk_1_4ac0f28ca75d401a96b3d5888eba80b4                   /PS C:\_TMM-Team&gt;subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TEAM-RG-WEUR/providers/Microsoft.C… 
						az-weur-devops-automation_OsDisk_1_be08dac7d5554d00b5f31bef781edfef         /PS C:\_TMM-Team&gt;subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TEAM-RG-WEUR/providers/Microsoft.C… 
						az-weur-w11-mi-ua-wem-vda-notl-tf_OsDisk_1_bdb9ec4e69af426ca1ad8db58a0b839d /PS C:\_TMM-Team&gt;subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TEAM-RG-WEUR/providers/Microsoft.C… 
						az-weur-w2k25-m-notl-ss-osdisk                                              /PS C:\_TMM-Team&gt;subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TEAM-RG-WEUR/providers/Microsoft.C… 
						az-weur-w2k25-m-notl_OsDisk_1_f8a83963b50f45af9f1fc662b8766ab7              /PS C:\_TMM-Team&gt;subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TEAM-RG-WEUR/providers/Microsoft.C… 
						ipsexp-w11m                                                                 /PS C:\_TMM-Team&gt;subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TEAM-RG-WEUR/providers/Microsoft.C… 
						ipsexp-w2k25m                                                               /PS C:\_TMM-Team&gt;subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TEAM-RG-WEUR/providers/Microsoft.C… 
						Preparati-3204t-osdisk-gy7dj                                                /PS C:\_TMM-Team&gt;subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TEAM-RG-WEUR/providers/Microsoft.C… 
						TF-TMM-CC1-OsDisk                                                           /PS C:\_TMM-Team&gt;subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TEAM-RG-WEUR/providers/Microsoft.C… 
						TF-TMM-CC2-OsDisk                                                           /PS C:\_TMM-Team&gt;subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TEAM-RG-WEUR/providers/Microsoft.C… 
						weur-connectorapp_OsDisk_1_4de5f09e91be4581ad6ffa28beac3d46                 /PS C:\_TMM-Team&gt;subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TEAM-RG-WEUR/providers/Microsoft.C… 
						 
						PS C:\_TMM-Team&gt;
					 
				
			
		
	



	 
 


	Active Directory



	This guide uses an existing Active Directory Domain consisting of a Hub-and-Spoke model. Each Resource Location running on a Hypervisor or Hyperscaler is connected to the primary Domain Controller using IPSec-based Site-to-Site VPNs. If needed, each Resource Location can have its own subdomain. 
	 
 


	WinRM connectivity



	Terraform and Ansible deployments are agentless. The communication flow relies on SSH or WinRM. In this guide, the communication flow is based on WinRM. The Machine where Terraform and Ansible are installed and which we use for deployment is located in the same Virtual Network and the same Network Security Group as the target machines. You can configure WinRM using Group Policy Objects to standardize and ensure proper communication between all components. 
	 
 


	
		IMPORTANT: 
	 

	
		It is imperative that WinRM also works flawlessly on the non-domain-joined Windows Server template, as Ansible will be used to put the VMs into the Active Directory Domain. 
		The GPOs are deployed after the Domain join. Make sure you configure the adjacent WinRM settings using the Local Policy feature.  
		 
	 



	
		Caution:
	 

	
		It is necessary to open the adjacent WinRM ports on the Firewalls to ensure flawless communication. 
		Normally, the correct ports are opened automatically on the Windows Machines if you enable WinRM. 
		You can set the correct rule(s) using a Group Policy Object. 
		In this guide, we create a new rule in the corresponding Azure Network Security Group to allow WinRM communication using Terraform and Ansible in the same Virtual Network.
	 

	
		Please alter this code part according to your needs and security requirements.   
		 
	 



	You can use a short Terraform snippet to check if the WinRM communication works as intended:  
 


	Terraform code for testing WinRM - It simply tries to upload and execute a PowerShell script on the target servers:
 


	 
 


	
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}	
	
		
			
				
					
						
							resource "null_resource" "UploadTestFileToCC1" {
						 

						
							 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 Test script to CC1
						 

						
							  provisioner "file" {
						 

						
							     source      = "${path.module}/data/Test-Script.ps1"
						 

						
							    destination = "c:/temp/Test-Script.ps1"
						 

						
							    
						 

						
							  } 
						 

						
							}
						 

						
							 
						 

						
							resource "null_resource" "UploadTestFileToCC2" {
						 

						
							 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 Test script to CC2
						 

						
							  provisioner "file" {
						 

						
							    source      = "${path.module}/data/Test-Script.ps1"
						 

						
							    destination = "c:/temp/Test-Script.ps1"
						 

						
							    
						 

						
							  } 
						 

						
							}
						 

						
							 
							 
						 

						
							resource "null_resource" "InstallSFonDDC1" {
						 

						
							depends_on = [ null_resource.UploadTestFileToCC1 ]
						 

						
							connection {
						 

						
							    type            = var.Provisioner_Type
						 

						
							    user            = var.Provisioner_Admin-Username
						 

						
							    password        = var.Provisioner_Admin-Password
						 

						
							    host            = var.Provisioner_DDC1-IP
						 

						
							    timeout         = var.Provisioner_Timeout
						 

						
							 
						 

						
							  }
						 

						
							 provisioner "remote-exec" {
						 

						
							    inline = [
						 

						
							      "powershell -File c:/temp/Test-Script.ps1"
						 

						
							    ]
						 

						
							  }
						 

						
							}
						 

						
							 
						 

						
							resource "null_resource" "InstallSFonDDC2" {
						 

						
							depends_on = [ null_resource.UploadTestFileToCC2 ]
						 

						
							connection {
						 

						
							    type            = var.Provisioner_Type
						 

						
							    user            = var.Provisioner_Admin-Username
						 

						
							    password        = var.Provisioner_Admin-Password
						 

						
							    host            = var.Provisioner_DDC2-IP
						 

						
							    timeout         = var.Provisioner_Timeout
						 

						
							 
						 

						
							  }
						 

						
							 provisioner "remote-exec" {
						 

						
							    inline = [
						 

						
							      "powershell -File c:/temp/Test-Script.ps1"
						 

						
							    ]
						 

						
							  }
						 

						
							}
						 
					
				
			
		

		
			Sample output of the Terraform Provider: 
		 

		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
			
				
					
						
							
								PS C:\TMM-Team\TestWinRM&gt; terraform apply 
								 
								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: 
								 
								  # null_resource.InstallSFonDDC1 will be created 
								  + resource "null_resource" "InstallSFonDDC1" { 
								      + id = (known after apply) 
								    } 
								 
								  # null_resource.InstallSFonDDC2 will be created 
								  + resource "null_resource" "InstallSFonDDC2" { 
								      + id = (known after apply) 
								    } 
								 
								  # null_resource.UploadTestFileToCC1 will be created 
								  + resource "null_resource" "UploadTestFileToCC1" { 
								      + id = (known after apply) 
								    } 
								 
								  # null_resource.UploadTestFileToCC2 will be created 
								  + resource "null_resource" "UploadTestFileToCC2" { 
								      + id = (known after apply) 
								    } 
								 
								Plan: 4 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.UploadTestFileToCC2: Creating... 
								null_resource.UploadTestFileToCC1: Creating... 
								null_resource.UploadTestFileToCC2: Provisioning with 'file'... 
								null_resource.UploadTestFileToCC1: Provisioning with 'file'... 
								#&lt; CLIXML 
								&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
								&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
								&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadTestFileToCC2: Creation complete after 1s [id=918033952701580746] 
								null_resource.InstallSFonDDC2: Creating... 
								null_resource.InstallSFonDDC2: Provisioning with 'remote-exec'... 
								null_resource.InstallSFonDDC2 (remote-exec): Connecting to remote host via WinRM... 
								null_resource.InstallSFonDDC2 (remote-exec):   Host: 10.53.16.102 
								null_resource.InstallSFonDDC2 (remote-exec):   Port: 5985 
								null_resource.InstallSFonDDC2 (remote-exec):   User: azXXXXXXXX 
								null_resource.InstallSFonDDC2 (remote-exec):   Password: true 
								null_resource.InstallSFonDDC2 (remote-exec):   HTTPS: false 
								null_resource.InstallSFonDDC2 (remote-exec):   Insecure: false 
								null_resource.InstallSFonDDC2 (remote-exec):   NTLM: false 
								null_resource.InstallSFonDDC2 (remote-exec):   CACert: false 
								null_resource.InstallSFonDDC2 (remote-exec): Connected! 
								#&lt; CLIXML 
								&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadTestFileToCC1: Creation complete after 1s [id=8367997441106547133] 
								null_resource.InstallSFonDDC1: Creating... 
								null_resource.InstallSFonDDC1: Provisioning with 'remote-exec'... 
								null_resource.InstallSFonDDC1 (remote-exec): Connecting to remote host via WinRM... 
								null_resource.InstallSFonDDC1 (remote-exec):   Host: 10.53.16.101 
								null_resource.InstallSFonDDC1 (remote-exec):   Port: 5985 
								null_resource.InstallSFonDDC1 (remote-exec):   User: azXXXXXXXX 
								null_resource.InstallSFonDDC1 (remote-exec):   Password: true 
								null_resource.InstallSFonDDC1 (remote-exec):   HTTPS: false 
								null_resource.InstallSFonDDC1 (remote-exec):   Insecure: false 
								null_resource.InstallSFonDDC1 (remote-exec):   NTLM: false 
								null_resource.InstallSFonDDC1 (remote-exec):   CACert: false 
								null_resource.InstallSFonDDC1 (remote-exec): Connected! 
								#&lt; CLIXML 
								&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
								&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
								&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
								null_resource.InstallSFonDDC2 (remote-exec): C:\Users\azXXXXXXXX&gt;powershell -File c:/temp/Test-Script.ps1 
								#&lt; CLIXML 
								&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
								&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
								null_resource.InstallSFonDDC1 (remote-exec): C:\Users\azXXXXXXXX&gt;powershell -File c:/temp/Test-Script.ps1 
								#&lt; CLIXML 
								&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.InstallSFonDDC2: Creation complete after 3s [id=4379278538003823966] 
								#&lt; CLIXML 
								&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
								&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.InstallSFonDDC1: Creation complete after 4s [id=6466929064112579037] 
								 
								Apply complete! Resources: 4 added, 0 changed, 0 destroyed. 
								PS C:\TMM-Team\TestWinRM&gt;
							 
						
					
				
			
		

		
			The output shows a flawless communication flow. WinRM is working as intended. 
			 
		 

		
			Existing Azure Entities - Virtual Network, Subnet, and Network Security Group
		

		
			We assume that these entities are already configured on Azure. Terraform relies on them and retrieves Important configuration details during the deployment. You can use PowerShell to retrieve all the needed entity values. Write down the required entity information for later use in the corresponding .auto.tfvars.json files so the Terraform provider can use this Information:
		 

		
			Get the Virtual Network to use:
		 

		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
			
				
					
						
							
								PS C:\_TMM-Team&gt; Get-AzVirtualNetwork -ResourceGroupName 'TMM-TEAM-RG-WEUR' | FT Name, Location 
								 
								Name          Location 
								----          -------- 
								West-EUR-vnet westeurope 
								 
								PS C:\_TMM-Team&gt;
							 
						
					
				
			
		

		
			Get the Subnet(s) to use:
		 

		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
			
				
					
						
							
								PS C:\_TMM-Team&gt; $vn = Get-AzVirtualNetwork -Name 'West-EUR-vnet' 
								PS C:\_TMM-Team&gt; Get-AzVirtualNetworkSubnetConfig -VirtualNetwork $vn | FT Name, AddressPrefix 
								 
								Name          AddressPrefix 
								----          ------------- 
								West-EUR-1-sn {10.53.16.0/24} 
								West-EUR-2-sn {10.53.17.0/25} 
								GatewaySubnet {10.53.17.128/26} 
								 
								PS C:\_TMM-Team&gt;
							 
						
					
				
			
		

		
			Get the Network Security Group to use:
		 

		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
			
				
					
						
							
								PS C:\_TMM-Team&gt; Get-AzNetworkSecurityGroup -ResourceGroupName 'TMM-TEAM-RG-WEUR' | FT Name, Location 
								 
								Name                Location 
								----                -------- 
								AZ-West-EUR-GK-nsg  westeurope 
								az-weur-cc-test-nsg westeurope 
								 
								PS C:\_TMM-Team&gt;
							 
						
					
				
			
		

		
			 
		 

		
			A Service Principal Name (SPN) with a Client Secret for Azure Authentication 
		

		
			A Service Principal is an application within Azure Active Directory whose authentication tokens can be used as the client_id, client_secret, and tenant_id fields needed by the Azure Terraform provider. The subscription_id field can be found in your Azure Account details. Terraform uses the SPN to connect to Azure and create everything needed. If you do not have an SPN configured, you can use e.g. Azure CLI on PowerShell to create one:
		 

		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
			
				
					
						
							
								PS C:\TMM-Team\TestWinRM&gt; az login 
								Select the account you want to log in with. For more information on login with Azure CLI, see https://go.microsoft.com/fwlink/?linkid=2271136 
								 
								Retrieving tenants and subscriptions for the selection... 
								 
								[Tenant and subscription selection] 
								 
								No     Subscription name    Subscription ID                       Tenant 
								-----  -------------------  ------------------------------------  ---------------- 
								[1] *  WWCO-US-East         dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13  Citrix (TM-WWCO) 
								 
								The default is marked with an *; the default tenant is 'Citrix (TM-WWCO)' and subscription is 'WWCO-US-East' (dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13). 
								 
								Select a subscription and tenant (Type a number or Enter for no changes): 1 
								 
								Tenant: Citrix (TM-WWCO) 
								Subscription: WWCO-US-East (dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13) 
								 
								[Announcements] 
								With the new Azure CLI login experience, you can select the subscription you want to use more easily. Learn more about it and its configuration at https://go.microsoft.com/fwlink/?linkid=2271236 
								 
								If you encounter any problem, please open an issue at https://aka.ms/azclibug 
								 
								[Warning] The login output has been updated. Please be aware that it no longer displays the full list of available subscriptions by default. 
								 
								PS C:\TMM-Team\TestWinRM&gt; az ad sp create-for-rbac 
								The output includes credentials that you must protect. Be sure that you do not include these credentials in your code or check the credentials into your source control. For more information, see https://aka.ms/azadsp-cli 
								{ 
								  "appId": "5f71XXXX-XXXX-XXXX-XXXX-XXXXXXXXd6e5", 
								  "displayName": "azure-cli-2025-03-14-12-05-23", 
								  "password": "jNk8XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXHb1G", 
								  "tenant": "1974XXXX-XXXX-XXXX-XXXX-XXXXXXXX474b" 
								} 
								PS C:\TMM-Team\TestWinRM&gt;
							 
						
					
				
			
		

		
			You can use PowerShell to retrieve all the needed entity values. Write down the required entity information for later use in the corresponding .auto.tfvars.json files so the Terraform provider can use this Information. 
			 
		 

		
			RECOMMENDED: A Service Principal in Citrix Cloud for Authentication 
		

		
			
				
					A Service Principal in Citrix Cloud acts as an API client to Citrix Cloud APIs.
				 

				
					
						Service principals have their own roles and permissions. These roles and permissions are distinct from the creator’s roles and permissions.
					
					
						Service principals are scoped to a single Citrix Cloud customer. To access more than one customer, you must create a distinct service principal within each customer.
					
					
						Service principals’ secrets expire on an expiration date of your choice.
					
					
						Service principals currently only support custom access. Full access will be available soon once all resource provider services support service principals.
					
				

				
					Select the Identity and Access Management option from the menu to create a Service Principal. 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, Service principals: 
						 
						 
					
					
						Click Create service principal: 
						 
						 
					
					
						Enter a Name and click Next: 
						 
						 
					
					
						Choose the correct Access settings and click Next: 
						 
						 
					
					
						Choose the Expiration and click Next: 
						 
						 
					
					
						Review the settings and and click Complete: 
						 
						 
					
					
						After the Service Principal 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 #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}					
					
						
							
								
									
										
											...
										 

										
											 "CC_APIKey-ClientID":"f4xxxxxx-xxxx-xxxx-xxxx-xxxxxx05c15",
										 

										
											 "CC_APIKey-ClientSecret":"VJCxxxx7RgA==",
										 

										
											 "CC_CustomerID": "uzxxxxxxxj"
										 

										
											...
										 
									
								
							
						
					
				

				
					 
				 

				
					Legacy: A Secure Client in Citrix Cloud for Authentication 
				

				
					The Secure Client in Citrix Cloud is the same as the SPN in Azure. 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 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: 
						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:
				 

				
					
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}					
					
						
							
								
									
										
											...
										 

										
											 "CC_APIKey-ClientID":"f4xxxxxx-xxxx-xxxx-xxxx-xxxxxx05c15",
										 

										
											 "CC_APIKey-ClientSecret":"VJCxxxx7RgA==",
										 

										
											 "CC_CustomerID": "uzxxxxxxxj"
										 

										
											...
										 
									
								
							
						

						
							 
						 
					
				

				
					Checking the available VM sizes and quotas in your Azure Subscription
				

				
					
						IMPORTANT: 
					 

					
						You need to determine the available VM sizes to choose the correct sizes before deploying the Virtual Machines, as not all Machine Sizes are available in all Azure locations. 
						 
					 
				

				
					Use PowerShell to list the available Machine Sizes for Windows Server-based VMs. 
					In this example, we plan to deploy "D2-based" VMs - change the parameters according to your needs:
				 

				
					
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}					
					
						
							
								
									
										PS C:\_TMM-Team&gt; $location="westeurope" 
										PS C:\_TMM-Team&gt; Get-AzVMSize -Location $location | Where Name -like 'Standard_D2*' | Select Name,NumberOfCores,MemoryInMB,OSDiskSizeInMB,ResourceDiskSizeInMB | Format-Table 
										 
										Name                 NumberOfCores MemoryInMB OSDiskSizeInMB ResourceDiskSizeInMB 
										----                 ------------- ---------- -------------- -------------------- 
										Standard_D2a_v4                  2       8192        1047552                51200 
										Standard_D2as_v4                 2       8192        1047552                16384 
										Standard_D2as_v5                 2       8192        1047552                    0 
										Standard_D2ads_v5                2       8192        1047552                76800 
										Standard_D2_v2                   2       7168        1047552               102400 
										Standard_D2_v2_Promo             2       7168        1047552               102400 
										Standard_D2_v3                   2       8192        1047552                51200 
										Standard_D2s_v3                  2       8192        1047552                16384 
										Standard_D2ds_v4                 2       8192        1047552                76800 
										Standard_D2ds_v5                 2       8192        1047552                76800 
										Standard_D2d_v4                  2       8192        1047552                76800 
										Standard_D2d_v5                  2       8192        1047552                76800 
										Standard_D2s_v4                  2       8192        1047552                    0 
										Standard_D2s_v5                  2       8192        1047552                    0 
										Standard_D2_v4                   2       8192        1047552                    0 
										Standard_D2_v5                   2       8192        1047552                    0 
										Standard_D2ls_v5                 2       4096        1047552                    0 
										Standard_D2lds_v5                2       4096        1047552                76800 
										Standard_D2                      2       7168        1047552               102400 
										Standard_D2plds_v5               2       4096        1047552                76800 
										Standard_D2pls_v5                2       4096        1047552                    0 
										Standard_D2pds_v5                2       8192        1047552                76800 
										Standard_D2ps_v5                 2       8192        1047552                    0 
										Standard_D2pls_v6                2       4096        1047552                    0 
										Standard_D2plds_v6               2       4096        1047552                    0 
										Standard_D2ps_v6                 2       8192        1047552                    0 
										Standard_D2pds_v6                2       8192        1047552                    0 
										Standard_D2ls_v6                 2       4096        1047552                    0 
										Standard_D2lds_v6                2       4096        1047552                    0 
										Standard_D2s_v6                  2       8192        1047552                    0 
										Standard_D2ds_v6                 2       8192        1047552                    0 
										Standard_D2as_v6                 2       8192        1047552                    0 
										Standard_D2ads_v6                2       8192        1047552                    0 
										Standard_D2als_v6                2       4096        1047552                    0 
										Standard_D2alds_v6               2       4096        1047552                    0 
										 
										PS C:\_TMM-Team&gt;
									 
								
							
						
					
				

				
					 
				 

				
					
						Caution:
					 

					
						Be sure that your subscription has no quota limitation on the chosen VM type and that you have enough resources on Azure to create all the Virtual Machines planned to put into the Machine Catalog by checking quotas. Otherwise, the creation of the Machine Catalog will fail if there are not enough compute resources available! 
						 
					 
				

				
					Use PowerShell to list the available Machine Sizes for Windows Server-based VMs. 
					In this example, we check the vCPU quotas for a "D2S_V5"-based VM - change the parameters according to your needs:
				 

				
					
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}					
					
						
							
								
									
										PS C:\_TMM-Team&gt; $Location = 'westeurope' 
										PS C:\_TMM-Team&gt; $VMSize = 'Standard_D2s_v5' 
										PS C:\_TMM-Team&gt; $SKU = Get-AzComputeResourceSku -Location $Location | where ResourceType -eq "virtualMachines" | select Name,Family 
										PS C:\_TMM-Team&gt; $VMFamily = ($SKU | where Name -eq $VMSize | select -Property Family).Family 
										PS C:\_TMM-Team&gt; Get-AzVMUsage -Location $Location | Where-Object { $_.Name.Value -eq $VMFamily } 
										 
										Name                       Current Value Limit  Unit 
										----                       ------------- -----  ---- 
										Standard DSv5 Family vCPUs            18   350 Count 
										 
										PS C:\_TMM-Team&gt;
									 
								
							
						
					
				

				
					This example shows we can create enough D2S_v5 VMs before exceeding the limit. The Azure Console or PowerShell can increase the vCPU or any other quota. More information about increasing vCPU quotas can be found here: https://learn.microsoft.com/en-us/azure/quotas/per-vm-quota-requests 
					 
				 

				
					Software Components
				

				
					Citrix Cloud Connector Installer: cwcconnector.exe.  
					Download the Citrix Cloud Connector Installer and save the Installer to the directory of Module 2 as Terraform looks for the file.
				 

				
					We assume all prerequisites are met so that we can start with the deployment. 
					 
				 

				
					Deploying Citrix DaaS on Microsoft Azure 
					 
				

				
					
						NOTE: 
					 

					
						We did not use loop constructs or count keywords—each resource is created explicitly—for easier reading and understanding. 
						 
					 
				

				
					The Terraform flow is split into different parts:
				 

				
					
						Module One (optional):

						
							
								Creating a Resource Group on Microsoft Azure - optional, depending on a set TF variable 
								 
							
						
					
					
						Module Two:
						
							
								Creating a Shared Image Gallery (SIG) in the adjacent Resource Group
							
							
								Creating an Image Definition of the pre-configured Windows Server Master Image in the SIG
							
							
								Creating the Shared Image 
								 
							
						
					
					
						Module Three:
						
							
								Creating 2 Cloud Connector VMs 
								 
							
						
					
					
						Module Four:
						
							
								Putting the just-created Cloud Connector-VMs into the Active Directory Domain using an Ansible Playbook
							
						
					
				

				
					
						Module Five:

						
							
								Creating a new Resource Location and the adjacent Zone In Citrix Cloud
							
							
								Uploading the Cloud Connector software installer to the Cloud Connector VMs
							
							
								Creating the configuration file cwc.json and uploading it to the Cloud Connector VMs
							
							
								Installing and configuring the Cloud Connector software using an Ansible Playbook
							
							
								Adding the Cloud Connectors to the newly created Resource Location 
								 
							
						
					
					
						Module Six: 
						
							
								Creating the Hypervisor Connection and Hypervisor Connection Resource Pool on Citrix Cloud
							
							
								Creating a Machine Catalog based on a Prepared Image
							
							
								Creating a Delivery Group based on the Machine Catalog 
								 
							
						
					
					
						Module Seven: 
						
							
								Creating a default Policy Set and assigning it to the Delivery Group and AD Use Groups
							
							
								Creating an Admin Scope
							
							
								Creating an Admin Role
							
							
								Creating AutoScale settings 
								 
							
						
					
					
						Module Eight: 
						
							
								Configuring Citrix Workspace App and Citrix Enterprise Browser using Terraform 
								 
							
						
					
					
						Optional Module: 
						
							
								Check WinRM connectivity
							
						
					
				

				
					 
				 

				
					Configuration using variables
				

				
					All needed configuration settings are stored in the corresponding variables that must be set. Some Configuration settings are propagated throughout the whole Terraform configuration...
				 

				
					You must start each module 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.
				 

				
					
						IMPORTANT:
					 

					
						Each module/step must be completed successfully before the next module can be started. 
						 
					 
				

				
					File System structure
				

				
					Root-Directory
				 

				
					Module 1: Create the Resource Group (optional)
				

				
					
						
							
								
									Filename
								 
							
							
								
									Purpose
								 
							
						
					
					
						
							
								
									CreateRG.tf
								 
							
							
								
									Resource configuration and primary flow definition
								 
							
						
						
							
								
									CreateRG-variables.tf
								 
							
							
								
									Definition of Variables
								 
							
						
						
							
								
									CreateRG.auto.tfvars.json
								 
							
							
								
									Setting the values of the Variables
								 
							
						
						
							
								
									Provider.tf
								 
							
							
								
									Provider definition and configuration
								 
							
						
						
							
								
									Provider-variables.tf
								 
							
							
								
									Definition of Variables
								 
							
						
						
							
								
									Provider.auto.tfvars.json
								 
							
							
								
									Setting the values of the Variables
								 
							
						
						
							
								
									Terraform.tfvars
								 
							
							
								
									Setting some Variables
								 
							
						
					
				

				
					 
				 

				
					Module 2: Create the Shared Image Gallery, the Image Definition, and the Image Version
				

				
					
						
							
								
									Filename
								 
							
							
								
									Purpose
								 
							
						
					
					
						
							
								
									CreateSIG.tf
								 
							
							
								
									Resource configuration and primary flow definition
								 
							
						
						
							
								
									CreateSIG-variables.tf
								 
							
							
								
									Definition of Variables
								 
							
						
						
							
								
									CreateSIG.auto.tfvars.json
								 
							
							
								
									Setting the values of the Variables
								 
							
						
						
							
								
									Provider.tf
								 
							
							
								
									Provider definition and configuration
								 
							
						
						
							
								
									Provider-variables.tf
								 
							
							
								
									Definition of Variables
								 
							
						
						
							
								
									Provider.auto.tfvars.json
								 
							
							
								
									Setting the values of the Variables
								 
							
						
						
							
								
									Assets-Directory
								 
							
							
								
									It contains the Ansible Playbooks used for Domain-join operations
								 
							
						
					
				

				
					 
				 

				
					Module 3: Create the 2 Cloud Connector Virtual Machines
				

				
					
						
							
								
									Filename
								 
							
							
								
									Purpose
								 
							
						
					
					
						
							
								
									CreateCCVMs.tf
								 
							
							
								
									Resource configuration and primary flow definition
								 
							
						
						
							
								
									CreateCCVMs-variables.tf
								 
							
							
								
									Definition of Variables
								 
							
						
						
							
								
									CreateCCVMs.auto.tfvars.json
								 
							
							
								
									Setting the values of the Variables
								 
							
						
						
							
								
									Provider.tf
								 
							
							
								
									Provider definition and configuration
								 
							
						
						
							
								
									Provider-variables.tf
								 
							
							
								
									Definition of Variables
								 
							
						
						
							
								
									Provider.auto.tfvars.json
								 
							
							
								
									Setting the values of the Variables
								 
							
						
						
							
								
									 
								 
							
							
								
									 
								 
							
						
					
				

				
					
				 

				
					 
				 

				
					Module 4: Put the Cloud Connector-VMs into the Active Directory Domain:
				

				
					
						
							
								
									Filename
								 
							
							
								
									Purpose
								 
							
						
					
					
						
							
								
									JoinDomain.tf
								 
							
							
								
									Resource configuration and primary flow definition
								 
							
						
						
							
								
									JoinDomain-variables.tf
								 
							
							
								
									Definition of Variables
								 
							
						
						
							
								
									JoinDomain.auto.tfvars.json
								 
							
							
								
									Setting the values of the Variables
								 
							
						
						
							
								
									Provider.tf
								 
							
							
								
									Provider definition and configuration
								 
							
						
						
							
								
									Provider-variables.tf
								 
							
							
								
									Definition of Variables
								 
							
						
						
							
								
									Provider.auto.tfvars.json
								 
							
							
								
									Setting the values of the Variables
								 
							
						
						
							
								
									Assets-Directory
								 
							
							
								
									It contains the necessary Ansible Playbooks 
								 
							
						
					
				

				
					 
				 

				
					Module 5: Install and Configure the Cloud Connector Software:
				

				
					
						
							
								
									Filename
								 
							
							
								
									Purpose
								 
							
						
					
					
						
							
								
									DeployCCs.tf
								 
							
							
								
									Resource configuration and primary flow definition
								 
							
						
						
							
								
									DeployCCs-variables.tf
								 
							
							
								
									Definition of Variables
								 
							
						
						
							
								
									DeployCCs.auto.tfvars.json
								 
							
							
								
									Setting the values of the Variables
								 
							
						
						
							
								
									Provider.tf
								 
							
							
								
									Provider definition and configuration
								 
							
						
						
							
								
									Provider-variables.tf
								 
							
							
								
									Definition of Variables
								 
							
						
						
							
								
									Provider.auto.tfvars.json
								 
							
							
								
									Setting the values of the Variables
								 
							
						
						
							
								
									Assets-Directory
								 
							
							
								
									It contains the necessary Ansible Playbooks, the Cloud Connector Installer, and the dynamically created Cloud Connector Configuration file 
								 
							
						
					
				

				
					 
				 

				
					Module 6: Deploy the Citrix Cloud Entities
				

				
					
						
							
								
									Filename
								 
							
							
								
									Purpose
								 
							
						
					
					
						
							
								
									DeployCitrixCloudEntities.tf
								 
							
							
								
									Resource configuration and primary flow definition
								 
							
						
						
							
								
									DeployCitrixCloudEntities-variables.tf
								 
							
							
								
									Definition of Variables
								 
							
						
						
							
								
									DeployCitrixCloudEntities.auto.tfvars.json
								 
							
							
								
									Setting the values of the Variables
								 
							
						
						
							
								
									Provider.tf
								 
							
							
								
									Provider definition and configuration
								 
							
						
						
							
								
									Provider-variables.tf
								 
							
							
								
									Definition of Variables
								 
							
						
						
							
								
									Provider.auto.tfvars.json
								 
							
							
								
									Setting the values of the Variables
								 
							
						
					
				

				
					 
				 

				
					Module 7: Create Admin Scopes, Admin Roles, and a Policy Set 
				

				
					
						
							
								
									Filename
								 
							
							
								
									Purpose
								 
							
						
					
					
						
							
								DeployCitrixPoliciesAndScopes.tf
								
									 
								 
							
							
								
									Resource configuration and primary flow definition
								 
							
						
						
							
								
									DeployCitrixPoliciesAndScopes-variables.tf
								 
							
							
								
									Definition of Variables
								 
							
						
						
							
								
									DeployCitrixPoliciesAndScopes.auto.tfvars.json
								 
							
							
								
									Setting the values of the Variables
								 
							
						
						
							
								
									Provider.tf
								 
							
							
								
									Provider definition and configuration
								 
							
						
						
							
								
									Provider-variables.tf
								 
							
							
								
									Definition of Variables
								 
							
						
						
							
								
									Provider.auto.tfvars.json
								 
							
							
								
									Setting the values of the Variables
								 
							
						
					
				

				
					 
				 

				
					Module 8: Configuring Citrix Workspace App and Citrix Enterprise Browser:
				

				
					
						
							
								
									Filename
								 
							
							
								
									Purpose
								 
							
						
					
					
						
							
								
									ConfiguringCWAAndCEBUsingGACS.tf
								 
							
							
								
									Resource configuration and primary flow definition
								 
							
						
						
							
								
									ConfiguringCWAAndCEBUsingGACS-variables.tf
								 
							
							
								
									Definition of Variables
								 
							
						
						
							
								
									ConfiguringCWAAndCEBUsingGACS.auto.tfvars.json
								 
							
							
								
									Setting the values of the Variables
								 
							
						
						
							
								
									Provider.tf
								 
							
							
								
									Provider definition and configuration
								 
							
						
						
							
								
									Provider-variables.tf
								 
							
							
								
									Definition of Variables
								 
							
						
						
							
								
									Provider.auto.tfvars.json
								 
							
							
								
									Setting the values of the Variables
								 
							
						
					
				

				
					 
					Optional Module: Checking WinRM Connectivity:
				

				
					
						
							
								
									Filename
								 
							
							
								
									Purpose
								 
							
						
					
					
						
							
								
									WinRM-test.tf
								 
							
							
								
									Resource configuration and primary flow definition
								 
							
						
						
							
								
									WinRM-test-variables.tf
								 
							
							
								
									Definition of Variables
								 
							
						
						
							
								
									WinRM-test.auto.tfvars.json
								 
							
							
								
									Setting the values of the Variables
								 
							
						
						
							
								
									Provider.tf
								 
							
							
								
									Provider definition and configuration
								 
							
						
						
							
								
									Provider-variables.tf
								 
							
							
								
									Definition of Variables
								 
							
						
						
							
								
									Provider.auto.tfvars.json
								 
							
							
								
									Setting the values of the Variables
								 
							
						
						
							
								
									DATA directory
								 
							
							
								
									Contains the PowerShell script for WinRM tests
								 
							
						
					
				

				
					 
				 

				
					
						CAUTION:
					 

					
						 All Terraform-related directories and files (.terraform, -terraform.lock.hcl, terraform.tfstate, terraform.tfstate) must not be changed or deleted - doing so might break the deployment! 
						 
					 
				

				
					Change the settings in the .json files to match your needs.
				 

				
					Module 1: Create the Resource Group (optional)
				

				
					In this module, you can decide if you want Terraform to create a new Resource Group on Azure to deploy all further components by setting the corresponding variable in the terraform.tfvars file:
				 

				
					
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}					
					
						
							
								
									
										
											...
										 

										
											CreateRG = true
										 

										
											...
										 
									
								
							
						
					
				

				
					The new Resource Group will be created if you set this variable to true.
				 

				
					This module is split into the following configuration parts:
				 

				
					
						Creating a Resource Group on Microsoft Azure (optional)
					
				

				
					Terraform automatically does all these steps.
				 

				
					
						IMPORTANT:
					 

					
						Please ensure you have configured the variables according to your needs using the corresponding .auto.tfvars.json file.
					 
				

				
					The configuration can be started by following the standard Terraform workflow:
				 

				
					
						terraform init,
					 

					
						terraform plan
					 
				

				
					and if no errors occur
				 

				
					
						terraform apply
					 
				

				
					
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}					
					
						
							
								
									
										PS C:\TMM-Team\CreateRG&gt; terraform init 
										Initializing the backend... 
										Initializing provider plugins... 
										- Finding latest version of hashicorp/http... 
										- Finding hashicorp/azuread versions matching "&gt;= 3.0.2"... 
										- Finding hashicorp/azurerm versions matching "&gt;= 4.6.0"... 
										- Finding citrix/citrix versions matching "1.0.13"... 
										- Installing hashicorp/http v3.4.5... 
										- Installed hashicorp/http v3.4.5 (signed by HashiCorp) 
										- Installing hashicorp/azuread v3.1.0... 
										- Installed hashicorp/azuread v3.1.0 (signed by HashiCorp) 
										- Installing hashicorp/azurerm v4.23.0... 
										- Installed hashicorp/azurerm v4.23.0 (signed by HashiCorp) 
										- Installing citrix/citrix v1.0.13... 
										- Installed citrix/citrix v1.0.13 (self-signed, key ID BD4BD0E690CB7D88) 
										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:\TMM-Team\CreateRG&gt; terraform plan 
										 
										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: 
										 
										  # azurerm_resource_group.TACG-AZ-V2-RG[0] will be created 
										  + resource "azurerm_resource_group" "TACG-AZ-V2-RG" { 
										      + id       = (known after apply) 
										      + location = "westeurope" 
										      + name     = "TMM-TF-WEUR" 
										      + tags     = { 
										          + "Environment"        = "TMM" 
										          + "Environment-Entity" = "GK-TF" 
										          + "Environment-Usage"  = "Prod" 
										        } 
										    } 
										 
										Plan: 1 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:\TMM-Team\CreateRG&gt; terraform apply 
										 
										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: 
										 
										  # azurerm_resource_group.TACG-AZ-V2-RG[0] will be created 
										  + resource "azurerm_resource_group" "TACG-AZ-V2-RG" { 
										      + id       = (known after apply) 
										      + location = "westeurope" 
										      + name     = "TMM-TF-WEUR" 
										      + tags     = { 
										          + "Environment"        = "TMM" 
										          + "Environment-Entity" = "GK-TF" 
										          + "Environment-Usage"  = "Prod" 
										        } 
										    } 
										 
										Plan: 1 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 
										 
										azurerm_resource_group.TACG-AZ-V2-RG[0]: Creating... 
										azurerm_resource_group.TACG-AZ-V2-RG[0]: Still creating... [10s elapsed] 
										azurerm_resource_group.TACG-AZ-V2-RG[0]: Still creating... [20s elapsed] 
										azurerm_resource_group.TACG-AZ-V2-RG[0]: Creation complete after 24s [id=/subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TF-WEUR] 
										 
										Apply complete! Resources: 1 added, 0 changed, 0 destroyed. 
										PS C:\TMM-Team\CreateRG&gt;
									 
								
							
						
					
				

				
					Terraform successfully created a Resource Group on Azure. 
					Now, the next step can be started.
				 

				
					Module 2: Creating a Shared Image Gallery, an Image Definition, and an Image Version
				

				
					Terraform creates a Shared Image Gallery, an Image Definition, and an Image Version based on a pre-configured Windows Server 2025 VM for further use in this Module.
				 

				
					
						IMPORTANT: 
					 

					
						Please make sure that the variable 
					 
				

				
					 "RG-Name": "TMM-TF-WEUR",
				 

				
					
						the adjacent .auto.tvars.json file reflects the correct name of the Resource Group where you want to put all resources.
					 
				

				
					This module is split into the following configuration parts:
				 

				
					
						Creating a Shared Image Gallery (SIG) in the adjacent Resource Group
					
					
						Creating an Image Definition of the pre-configured Windows Server Master Image in the SIG
					
					
						Creating the Shared Image 
					
				

				
					
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}					
					
						
							
								
									
										PS C:\TMM-Team\CreateSIG&gt; terraform init 
										Initializing the backend... 
										Initializing provider plugins... 
										- Finding citrix/citrix versions matching "1.0.13"... 
										- Finding hashicorp/azuread versions matching "&gt;= 3.0.2"... 
										- Finding hashicorp/azurerm versions matching "&gt;= 4.6.0"... 
										- Finding latest version of hashicorp/http... 
										- Installing citrix/citrix v1.0.13... 
										- Installed citrix/citrix v1.0.13 (self-signed, key ID BD4BD0E690CB7D88) 
										- Installing hashicorp/azuread v3.1.0... 
										- Installed hashicorp/azuread v3.1.0 (signed by HashiCorp) 
										- Installing hashicorp/azurerm v4.23.0... 
										- Installed hashicorp/azurerm v4.23.0 (signed by HashiCorp) 
										- Installing hashicorp/http v3.4.5... 
										- Installed hashicorp/http v3.4.5 (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:\TMM-Team\CreateSIG&gt; terraform plan 
										data.azurerm_resource_group.TMM-RG: Reading... 
										data.azurerm_resource_group.TMM-RG: Read complete after 0s [id=/subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TF-WEUR] 
										 
										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: 
										 
										  # azurerm_shared_image.TACG-AZ-V2-SIG-IDW2K25M will be created 
										  + resource "azurerm_shared_image" "TACG-AZ-V2-SIG-IDW2K25M" { 
										      + accelerated_network_support_enabled = true 
										      + architecture                        = "x64" 
										      + gallery_name                        = "TMM_TF_SIG" 
										      + hibernation_enabled                 = true 
										      + hyper_v_generation                  = "V2" 
										      + id                                  = (known after apply) 
										      + location                            = "westeurope" 
										      + name                                = "TMM_TF_CC_Img_W2K25" 
										      + os_type                             = "Windows" 
										      + resource_group_name                 = "TMM-TF-WEUR" 
										      + tags                                = { 
										          + "Environment"        = "TMM" 
										          + "Environment-Entity" = "GK-TF" 
										          + "Environment-Usage"  = "Prod" 
										        } 
										      + trusted_launch_enabled              = false 
										 
										      + identifier { 
										          + offer     = "WindowsServer" 
										          + publisher = "MicrosoftWindowsServer" 
										          + sku       = "2025-datacenter" 
										        } 
										    } 
										 
										  # azurerm_shared_image_gallery.TACG-AZ-V2-SIG will be created 
										  + resource "azurerm_shared_image_gallery" "TACG-AZ-V2-SIG" { 
										      + description         = "Shared Image Gallery for Terraform Demo" 
										      + id                  = (known after apply) 
										      + location            = "westeurope" 
										      + name                = "TMM_TF_SIG" 
										      + resource_group_name = "TMM-TF-WEUR" 
										      + tags                = { 
										          + "Environment"        = "TMM" 
										          + "Environment-Entity" = "GK-TF" 
										          + "Environment-Usage"  = "Prod" 
										        } 
										      + unique_name         = (known after apply) 
										    } 
										 
										  # azurerm_shared_image_version.TACG-AZ-V2-SIG-IDW2K25M-ImgVersion will be created 
										  + resource "azurerm_shared_image_version" "TACG-AZ-V2-SIG-IDW2K25M-ImgVersion" { 
										      + deletion_of_replicated_locations_enabled = false 
										      + exclude_from_latest                      = false 
										      + gallery_name                             = "TMM_TF_SIG" 
										      + id                                       = (known after apply) 
										      + image_name                               = "TMM_TF_CC_Img_W2K25" 
										      + location                                 = "westeurope" 
										      + managed_image_id                         = "/subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TEAM-RG-WEUR/providers/Microsoft.Compute/virtualMachines/az-weur-w2k25-m-notl-ss-vm" 
										      + name                                     = "1.1.0" 
										      + replication_mode                         = "Full" 
										      + resource_group_name                      = "TMM-TF-WEUR" 
										      + tags                                     = { 
										          + "Environment"        = "TMM" 
										          + "Environment-Entity" = "GK-TF" 
										          + "Environment-Usage"  = "Prod" 
										        } 
										 
										      + target_region { 
										          + exclude_from_latest_enabled = false 
										          + name                        = "westeurope" 
										          + regional_replica_count      = 1 
										          + storage_account_type        = "Standard_LRS" 
										        } 
										    } 
										 
										Plan: 3 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:\TMM-Team\CreateSIG&gt; terraform apply 
										data.azurerm_resource_group.TMM-RG: Reading... 
										data.azurerm_resource_group.TMM-RG: Read complete after 0s [id=/subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TF-WEUR] 
										 
										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: 
										 
										  # azurerm_shared_image.TACG-AZ-V2-SIG-IDW2K25M will be created 
										  + resource "azurerm_shared_image" "TACG-AZ-V2-SIG-IDW2K25M" { 
										      + accelerated_network_support_enabled = true 
										      + architecture                        = "x64" 
										      + gallery_name                        = "TMM_TF_SIG" 
										      + hibernation_enabled                 = true 
										      + hyper_v_generation                  = "V2" 
										      + id                                  = (known after apply) 
										      + location                            = "westeurope" 
										      + name                                = "TMM_TF_CC_Img_W2K25" 
										      + os_type                             = "Windows" 
										      + resource_group_name                 = "TMM-TF-WEUR" 
										      + tags                                = { 
										          + "Environment"        = "TMM" 
										          + "Environment-Entity" = "GK-TF" 
										          + "Environment-Usage"  = "Prod" 
										        } 
										      + trusted_launch_enabled              = false 
										 
										      + identifier { 
										          + offer     = "WindowsServer" 
										          + publisher = "MicrosoftWindowsServer" 
										          + sku       = "2025-datacenter" 
										        } 
										    } 
										 
										  # azurerm_shared_image_gallery.TACG-AZ-V2-SIG will be created 
										  + resource "azurerm_shared_image_gallery" "TACG-AZ-V2-SIG" { 
										      + description         = "Shared Image Gallery for Terraform Demo" 
										      + id                  = (known after apply) 
										      + location            = "westeurope" 
										      + name                = "TMM_TF_SIG" 
										      + resource_group_name = "TMM-TF-WEUR" 
										      + tags                = { 
										          + "Environment"        = "TMM" 
										          + "Environment-Entity" = "GK-TF" 
										          + "Environment-Usage"  = "Prod" 
										        } 
										      + unique_name         = (known after apply) 
										    } 
										 
										  # azurerm_shared_image_version.TACG-AZ-V2-SIG-IDW2K25M-ImgVersion will be created 
										  + resource "azurerm_shared_image_version" "TACG-AZ-V2-SIG-IDW2K25M-ImgVersion" { 
										      + deletion_of_replicated_locations_enabled = false 
										      + exclude_from_latest                      = false 
										      + gallery_name                             = "TMM_TF_SIG" 
										      + id                                       = (known after apply) 
										      + image_name                               = "TMM_TF_CC_Img_W2K25" 
										      + location                                 = "westeurope" 
										      + managed_image_id                         = "/subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TEAM-RG-WEUR/providers/Microsoft.Compute/virtualMachines/az-weur-w2k25-m-notl-ss-vm" 
										      + name                                     = "1.1.0" 
										      + replication_mode                         = "Full" 
										      + resource_group_name                      = "TMM-TF-WEUR" 
										      + tags                                     = { 
										          + "Environment"        = "TMM" 
										          + "Environment-Entity" = "GK-TF" 
										          + "Environment-Usage"  = "Prod" 
										        } 
										 
										      + target_region { 
										          + exclude_from_latest_enabled = false 
										          + name                        = "westeurope" 
										          + regional_replica_count      = 1 
										          + storage_account_type        = "Standard_LRS" 
										        } 
										    } 
										 
										Plan: 3 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 
										 
										azurerm_shared_image_gallery.TACG-AZ-V2-SIG: Creating... 
										azurerm_shared_image_gallery.TACG-AZ-V2-SIG: Still creating... [10s elapsed] 
										azurerm_shared_image_gallery.TACG-AZ-V2-SIG: Creation complete after 11s [id=/subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TF-WEUR/providers/Microsoft.Compute/galleries/TMM_TF_SIG] 
										azurerm_shared_image.TACG-AZ-V2-SIG-IDW2K25M: Creating... 
										azurerm_shared_image.TACG-AZ-V2-SIG-IDW2K25M: Still creating... [10s elapsed] 
										azurerm_shared_image.TACG-AZ-V2-SIG-IDW2K25M: Creation complete after 17s [id=/subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TF-WEUR/providers/Microsoft.Compute/galleries/TMM_TF_SIG/images/TMM_TF_CC_Img_W2K25] 
										azurerm_shared_image_version.TACG-AZ-V2-SIG-IDW2K25M-ImgVersion: Creating... 
										azurerm_shared_image_version.TACG-AZ-V2-SIG-IDW2K25M-ImgVersion: Still creating... [10s elapsed] 
										azurerm_shared_image_version.TACG-AZ-V2-SIG-IDW2K25M-ImgVersion: Still creating... [20s elapsed] 
										azurerm_shared_image_version.TACG-AZ-V2-SIG-IDW2K25M-ImgVersion: Still creating... [30s elapsed] 
										... 
										azurerm_shared_image_version.TACG-AZ-V2-SIG-IDW2K25M-ImgVersion: Still creating... [8m50s elapsed] 
										azurerm_shared_image_version.TACG-AZ-V2-SIG-IDW2K25M-ImgVersion: Still creating... [9m0s elapsed] 
										azurerm_shared_image_version.TACG-AZ-V2-SIG-IDW2K25M-ImgVersion: Creation complete after 9m4s [id=/subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TF-WEUR/providers/Microsoft.Compute/galleries/TMM_TF_SIG/images/TMM_TF_CC_Img_W2K25/versions/1.1.0] 
										 
										Apply complete! Resources: 3 added, 0 changed, 0 destroyed. 
										PS C:\TMM-Team\CreateSIG&gt;
									 
								
							
						
					
				

				
					Terraform successfully created the Shared Image Gallery, the Image Definition, and the Image Version. 
					Now, the next step can be started.
				 

				
					Module 3: Creating 2 Cloud Connector VMs
				

				
					In this Module, Terraform creates 2 Cloud Connector VMs. Therefore, it creates various Azure entities, such as the Network Interface with its IP configuration, the associations with a Network Security Group, and the VMs.
				 

				
					It is split into the following configuration parts:
				 

				
					
						Creating 2 Cloud Connector Virtual Machines
					
				

				
					
						Caution:
					 

					
						Citrix recommends to disable automatic system reboots due to pending Windows updates. 
						The Cloud Connector automatically detects the necessity or a pending reboot and ensures that the Cloud Connectors do not go dowm for reboot simultaneously. 
						Please disable the reboot behavior either by a Group Policy Object or by using a Terraform snippet - you can find an example in the Terraform code of this module. 
						 
					 
				

				
					 
				 

				
					
						IMPORTANT:
					 

					
						Please ensure you have configured the variables according to your needs using the corresponding .auto.tfvars.json file.
					 
				

				
					The configuration can be started by following the standard Terraform workflow:
				 

				
					
						terraform init,
					 

					
						terraform plan
					 
				

				
					and if no errors occur
				 

				
					
						terraform apply
					 
				

				
					 
				 

				
					
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}					
					
						
							
								
									
										PS C:\TMM-Team\CreateVMs&gt; terraform init 
										Initializing the backend... 
										Initializing provider plugins... 
										- Finding latest version of hashicorp/http... 
										- Finding hashicorp/azuread versions matching "&gt;= 3.0.2"... 
										- Finding hashicorp/azurerm versions matching "&gt;= 4.6.0"... 
										- Finding citrix/citrix versions matching "1.0.13"... 
										- Installing hashicorp/http v3.4.5... 
										- Installed hashicorp/http v3.4.5 (signed by HashiCorp) 
										- Installing hashicorp/azuread v3.1.0... 
										- Installed hashicorp/azuread v3.1.0 (signed by HashiCorp) 
										- Installing hashicorp/azurerm v4.23.0... 
										- Installed hashicorp/azurerm v4.23.0 (signed by HashiCorp) 
										- Installing citrix/citrix v1.0.13... 
										- Installed citrix/citrix v1.0.13 (self-signed, key ID BD4BD0E690CB7D88) 
										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:\TMM-Team\CreateVMs&gt; terraform plan 
										data.azurerm_virtual_network.TACG-TMM-VNet: Reading... 
										data.azurerm_network_security_group.TACG-TMM-NSG: Reading... 
										data.azurerm_virtual_network.TACG-TMM-VNet: Read complete after 0s [id=/subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TEAM-RG-WEUR/providers/Microsoft.Network/virtualNetworks/West-EUR-vnet] 
										data.azurerm_subnet.TACG-TMM-Subnet: Reading... 
										data.azurerm_network_security_group.TACG-TMM-NSG: Read complete after 0s [id=/subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TEAM-RG-WEUR/providers/Microsoft.Network/networkSecurityGroups/AZ-West-EUR-GK-nsg] 
										data.azurerm_subnet.TACG-TMM-Subnet: Read complete after 0s [id=/subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TEAM-RG-WEUR/providers/Microsoft.Network/virtualNetworks/West-EUR-vnet/subnets/West-EUR-1-sn] 
										 
										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: 
										 
										  # azurerm_network_interface.TACG-TMM-TF-CC1-NIC will be created 
										  + resource "azurerm_network_interface" "TACG-TMM-TF-CC1-NIC" { 
										      + accelerated_networking_enabled = true 
										      + applied_dns_servers            = (known after apply) 
										      + dns_servers                    = [ 
										          + "10.53.8.29", 
										          + "10.53.8.10", 
										        ] 
										      + id                             = (known after apply) 
										      + internal_domain_name_suffix    = (known after apply) 
										      + ip_forwarding_enabled          = false 
										      + location                       = "westeurope" 
										      + mac_address                    = (known after apply) 
										      + name                           = "TFT-az-weur-CC1-NIC" 
										      + private_ip_address             = (known after apply) 
										      + private_ip_addresses           = (known after apply) 
										      + resource_group_name            = "TMM-TEAM-RG-WEUR" 
										      + tags                           = { 
										          + "Environment"        = "TMM" 
										          + "Environment-Entity" = "GK-TF" 
										          + "Environment-Usage"  = "Prod" 
										        } 
										      + virtual_machine_id             = (known after apply) 
										 
										      + ip_configuration { 
										          + gateway_load_balancer_frontend_ip_configuration_id = (known after apply) 
										          + name                                               = "TFT-az-weur-CC1-IPC" 
										          + primary                                            = (known after apply) 
										          + private_ip_address                                 = "10.53.16.103" 
										          + private_ip_address_allocation                      = "Static" 
										          + private_ip_address_version                         = "IPv4" 
										          + subnet_id                                          = "/subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TEAM-RG-WEUR/providers/Microsoft.Network/virtualNetworks/West-EUR-vnet/subnets/West-EUR-1-sn" 
										        } 
										    } 
										 
										  # azurerm_network_interface.TACG-TMM-TF-CC2-NIC will be created 
										  + resource "azurerm_network_interface" "TACG-TMM-TF-CC2-NIC" { 
										      + accelerated_networking_enabled = true 
										      + applied_dns_servers            = (known after apply) 
										      + dns_servers                    = [ 
										          + "10.53.8.29", 
										          + "10.53.8.10", 
										        ] 
										      + id                             = (known after apply) 
										      + internal_domain_name_suffix    = (known after apply) 
										      + ip_forwarding_enabled          = false 
										      + location                       = "westeurope" 
										      + mac_address                    = (known after apply) 
										      + name                           = "TFT-az-weur-CC2-NIC" 
										      + private_ip_address             = (known after apply) 
										      + private_ip_addresses           = (known after apply) 
										      + resource_group_name            = "TMM-TEAM-RG-WEUR" 
										      + tags                           = { 
										          + "Environment"        = "TMM" 
										          + "Environment-Entity" = "GK-TF" 
										          + "Environment-Usage"  = "Prod" 
										        } 
										      + virtual_machine_id             = (known after apply) 
										 
										      + ip_configuration { 
										          + gateway_load_balancer_frontend_ip_configuration_id = (known after apply) 
										          + name                                               = "TFT-az-weur-CC2-IPC" 
										          + primary                                            = (known after apply) 
										          + private_ip_address                                 = "10.53.16.104" 
										          + private_ip_address_allocation                      = "Static" 
										          + private_ip_address_version                         = "IPv4" 
										          + subnet_id                                          = "/subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TEAM-RG-WEUR/providers/Microsoft.Network/virtualNetworks/West-EUR-vnet/subnets/West-EUR-1-sn" 
										        } 
										    } 
										 
										  # azurerm_network_interface_security_group_association.TACG-TMM-TF-CC1-NICNSG will be created 
										  + resource "azurerm_network_interface_security_group_association" "TACG-TMM-TF-CC1-NICNSG" { 
										      + id                        = (known after apply) 
										      + network_interface_id      = (known after apply) 
										      + network_security_group_id = "/subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TEAM-RG-WEUR/providers/Microsoft.Network/networkSecurityGroups/AZ-West-EUR-GK-nsg" 
										    } 
										 
										  # azurerm_network_interface_security_group_association.TACG-TMM-TF-CC2-NICNSG will be created 
										  + resource "azurerm_network_interface_security_group_association" "TACG-TMM-TF-CC2-NICNSG" { 
										      + id                        = (known after apply) 
										      + network_interface_id      = (known after apply) 
										      + network_security_group_id = "/subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TEAM-RG-WEUR/providers/Microsoft.Network/networkSecurityGroups/AZ-West-EUR-GK-nsg" 
										    } 
										 
										  # azurerm_windows_virtual_machine.TACG-TMM-CC1 will be created 
										  + resource "azurerm_windows_virtual_machine" "TACG-TMM-CC1" { 
										      + admin_password                                         = (sensitive value) 
										      + admin_username                                         = (sensitive value) 
										      + allow_extension_operations                             = true 
										      + bypass_platform_safety_checks_on_user_schedule_enabled = false 
										      + computer_name                                          = (known after apply) 
										      + disk_controller_type                                   = (known after apply) 
										      + enable_automatic_updates                               = false 
										      + extensions_time_budget                                 = "PT1H30M" 
										      + hotpatching_enabled                                    = false 
										      + id                                                     = (known after apply) 
										      + location                                               = "westeurope" 
										      + max_bid_price                                          = -1 
										      + name                                                   = "TFT-az-weur-CC1" 
										      + network_interface_ids                                  = (known after apply) 
										      + patch_assessment_mode                                  = "ImageDefault" 
										      + patch_mode                                             = "Manual" 
										      + platform_fault_domain                                  = -1 
										      + priority                                               = "Regular" 
										      + private_ip_address                                     = (known after apply) 
										      + private_ip_addresses                                   = (known after apply) 
										      + provision_vm_agent                                     = true 
										      + public_ip_address                                      = (known after apply) 
										      + public_ip_addresses                                    = (known after apply) 
										      + resource_group_name                                    = "TMM-TEAM-RG-WEUR" 
										      + secure_boot_enabled                                    = false 
										      + size                                                   = "Standard_D2s_v5" 
										      + source_image_id                                        = "/subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TF-WEUR/providers/Microsoft.Compute/galleries/TMM_TF_SIG/images/TMM_TF_CC_Img_W2K25/versions/1.1.0" 
										      + tags                                                   = { 
										          + "Environment"        = "TMM" 
										          + "Environment-Entity" = "GK-TF" 
										          + "Environment-Usage"  = "Prod" 
										        } 
										      + timezone                                               = "W. Europe Standard Time" 
										      + virtual_machine_id                                     = (known after apply) 
										      + vm_agent_platform_updates_enabled                      = false 
										 
										      + os_disk { 
										          + caching                   = "ReadWrite" 
										          + disk_size_gb              = (known after apply) 
										          + id                        = (known after apply) 
										          + name                      = "TFT-TMM-CC1-OsDisk" 
										          + storage_account_type      = "Standard_LRS" 
										          + write_accelerator_enabled = false 
										        } 
										 
										      + termination_notification (known after apply) 
										    } 
										 
										  # azurerm_windows_virtual_machine.TACG-TMM-CC2 will be created 
										  + resource "azurerm_windows_virtual_machine" "TACG-TMM-CC2" { 
										      + admin_password                                         = (sensitive value) 
										      + admin_username                                         = (sensitive value) 
										      + allow_extension_operations                             = true 
										      + bypass_platform_safety_checks_on_user_schedule_enabled = false 
										      + computer_name                                          = (known after apply) 
										      + disk_controller_type                                   = (known after apply) 
										      + enable_automatic_updates                               = false 
										      + extensions_time_budget                                 = "PT1H30M" 
										      + hotpatching_enabled                                    = false 
										      + id                                                     = (known after apply) 
										      + location                                               = "westeurope" 
										      + max_bid_price                                          = -1 
										      + name                                                   = "TFT-az-weur-CC2" 
										      + network_interface_ids                                  = (known after apply) 
										      + patch_assessment_mode                                  = "ImageDefault" 
										      + patch_mode                                             = "Manual" 
										      + platform_fault_domain                                  = -1 
										      + priority                                               = "Regular" 
										      + private_ip_address                                     = (known after apply) 
										      + private_ip_addresses                                   = (known after apply) 
										      + provision_vm_agent                                     = true 
										      + public_ip_address                                      = (known after apply) 
										      + public_ip_addresses                                    = (known after apply) 
										      + resource_group_name                                    = "TMM-TEAM-RG-WEUR" 
										      + secure_boot_enabled                                    = false 
										      + size                                                   = "Standard_D2s_v5" 
										      + source_image_id                                        = "/subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TF-WEUR/providers/Microsoft.Compute/galleries/TMM_TF_SIG/images/TMM_TF_CC_Img_W2K25/versions/1.1.0" 
										      + tags                                                   = { 
										          + "Environment"        = "TMM" 
										          + "Environment-Entity" = "GK-TF" 
										          + "Environment-Usage"  = "Prod" 
										        } 
										      + timezone                                               = "W. Europe Standard Time" 
										      + virtual_machine_id                                     = (known after apply) 
										      + vm_agent_platform_updates_enabled                      = false 
										 
										      + os_disk { 
										          + caching                   = "ReadWrite" 
										          + disk_size_gb              = (known after apply) 
										          + id                        = (known after apply) 
										          + name                      = "TFT-TMM-CC2-OsDisk" 
										          + storage_account_type      = "Standard_LRS" 
										          + write_accelerator_enabled = false 
										        } 
										 
										      + termination_notification (known after apply) 
										    } 
										 
										Plan: 6 to add, 0 to change, 0 to destroy. 
										 
										Changes to Outputs: 
										  + NSG_id    = "AZ-West-EUR-GK-nsg" 
										  + Subnet_id = "West-EUR-1-sn" 
										  + VNet_ID   = "/subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TEAM-RG-WEUR/providers/Microsoft.Network/virtualNetworks/West-EUR-vnet" 
										 
										────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 
										 
										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:\TMM-Team\CreateVMs&gt; terraform apply 
										data.azurerm_network_security_group.TACG-TMM-NSG: Reading... 
										data.azurerm_virtual_network.TACG-TMM-VNet: Reading... 
										data.azurerm_virtual_network.TACG-TMM-VNet: Read complete after 0s [id=/subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TEAM-RG-WEUR/providers/Microsoft.Network/virtualNetworks/West-EUR-vnet] 
										data.azurerm_subnet.TACG-TMM-Subnet: Reading... 
										data.azurerm_network_security_group.TACG-TMM-NSG: Read complete after 0s [id=/subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TEAM-RG-WEUR/providers/Microsoft.Network/networkSecurityGroups/AZ-West-EUR-GK-nsg] 
										data.azurerm_subnet.TACG-TMM-Subnet: Read complete after 0s [id=/subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TEAM-RG-WEUR/providers/Microsoft.Network/virtualNetworks/West-EUR-vnet/subnets/West-EUR-1-sn] 
										 
										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: 
										 
										  # azurerm_network_interface.TACG-TMM-TF-CC1-NIC will be created 
										  + resource "azurerm_network_interface" "TACG-TMM-TF-CC1-NIC" { 
										      + accelerated_networking_enabled = true 
										      + applied_dns_servers            = (known after apply) 
										      + dns_servers                    = [ 
										          + "10.53.8.29", 
										          + "10.53.8.10", 
										        ] 
										      + id                             = (known after apply) 
										      + internal_domain_name_suffix    = (known after apply) 
										      + ip_forwarding_enabled          = false 
										      + location                       = "westeurope" 
										      + mac_address                    = (known after apply) 
										      + name                           = "TFT-az-weur-CC1-NIC" 
										      + private_ip_address             = (known after apply) 
										      + private_ip_addresses           = (known after apply) 
										      + resource_group_name            = "TMM-TEAM-RG-WEUR" 
										      + tags                           = { 
										          + "Environment"        = "TMM" 
										          + "Environment-Entity" = "GK-TF" 
										          + "Environment-Usage"  = "Prod" 
										        } 
										      + virtual_machine_id             = (known after apply) 
										 
										      + ip_configuration { 
										          + gateway_load_balancer_frontend_ip_configuration_id = (known after apply) 
										          + name                                               = "TFT-az-weur-CC1-IPC" 
										          + primary                                            = (known after apply) 
										          + private_ip_address                                 = "10.53.16.103" 
										          + private_ip_address_allocation                      = "Static" 
										          + private_ip_address_version                         = "IPv4" 
										          + subnet_id                                          = "/subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TEAM-RG-WEUR/providers/Microsoft.Network/virtualNetworks/West-EUR-vnet/subnets/West-EUR-1-sn" 
										        } 
										    } 
										 
										  # azurerm_network_interface.TACG-TMM-TF-CC2-NIC will be created 
										  + resource "azurerm_network_interface" "TACG-TMM-TF-CC2-NIC" { 
										      + accelerated_networking_enabled = true 
										      + applied_dns_servers            = (known after apply) 
										      + dns_servers                    = [ 
										          + "10.53.8.29", 
										          + "10.53.8.10", 
										        ] 
										      + id                             = (known after apply) 
										      + internal_domain_name_suffix    = (known after apply) 
										      + ip_forwarding_enabled          = false 
										      + location                       = "westeurope" 
										      + mac_address                    = (known after apply) 
										      + name                           = "TFT-az-weur-CC2-NIC" 
										      + private_ip_address             = (known after apply) 
										      + private_ip_addresses           = (known after apply) 
										      + resource_group_name            = "TMM-TEAM-RG-WEUR" 
										      + tags                           = { 
										          + "Environment"        = "TMM" 
										          + "Environment-Entity" = "GK-TF" 
										          + "Environment-Usage"  = "Prod" 
										        } 
										      + virtual_machine_id             = (known after apply) 
										 
										      + ip_configuration { 
										          + gateway_load_balancer_frontend_ip_configuration_id = (known after apply) 
										          + name                                               = "TFT-az-weur-CC2-IPC" 
										          + primary                                            = (known after apply) 
										          + private_ip_address                                 = "10.53.16.104" 
										          + private_ip_address_allocation                      = "Static" 
										          + private_ip_address_version                         = "IPv4" 
										          + subnet_id                                          = "/subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TEAM-RG-WEUR/providers/Microsoft.Network/virtualNetworks/West-EUR-vnet/subnets/West-EUR-1-sn" 
										        } 
										    } 
										 
										  # azurerm_network_interface_security_group_association.TACG-TMM-TF-CC1-NICNSG will be created 
										  + resource "azurerm_network_interface_security_group_association" "TACG-TMM-TF-CC1-NICNSG" { 
										      + id                        = (known after apply) 
										      + network_interface_id      = (known after apply) 
										      + network_security_group_id = "/subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TEAM-RG-WEUR/providers/Microsoft.Network/networkSecurityGroups/AZ-West-EUR-GK-nsg" 
										    } 
										 
										  # azurerm_network_interface_security_group_association.TACG-TMM-TF-CC2-NICNSG will be created 
										  + resource "azurerm_network_interface_security_group_association" "TACG-TMM-TF-CC2-NICNSG" { 
										      + id                        = (known after apply) 
										      + network_interface_id      = (known after apply) 
										      + network_security_group_id = "/subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TEAM-RG-WEUR/providers/Microsoft.Network/networkSecurityGroups/AZ-West-EUR-GK-nsg" 
										    } 
										 
										  # azurerm_windows_virtual_machine.TACG-TMM-CC1 will be created 
										  + resource "azurerm_windows_virtual_machine" "TACG-TMM-CC1" { 
										      + admin_password                                         = (sensitive value) 
										      + admin_username                                         = (sensitive value) 
										      + allow_extension_operations                             = true 
										      + bypass_platform_safety_checks_on_user_schedule_enabled = false 
										      + computer_name                                          = (known after apply) 
										      + disk_controller_type                                   = (known after apply) 
										      + enable_automatic_updates                               = false 
										      + extensions_time_budget                                 = "PT1H30M" 
										      + hotpatching_enabled                                    = false 
										      + id                                                     = (known after apply) 
										      + location                                               = "westeurope" 
										      + max_bid_price                                          = -1 
										      + name                                                   = "TFT-az-weur-CC1" 
										      + network_interface_ids                                  = (known after apply) 
										      + patch_assessment_mode                                  = "ImageDefault" 
										      + patch_mode                                             = "Manual" 
										      + platform_fault_domain                                  = -1 
										      + priority                                               = "Regular" 
										      + private_ip_address                                     = (known after apply) 
										      + private_ip_addresses                                   = (known after apply) 
										      + provision_vm_agent                                     = true 
										      + public_ip_address                                      = (known after apply) 
										      + public_ip_addresses                                    = (known after apply) 
										      + resource_group_name                                    = "TMM-TEAM-RG-WEUR" 
										      + secure_boot_enabled                                    = false 
										      + size                                                   = "Standard_D2s_v5" 
										      + source_image_id                                        = "/subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TF-WEUR/providers/Microsoft.Compute/galleries/TMM_TF_SIG/images/TMM_TF_CC_Img_W2K25/versions/1.1.0" 
										      + tags                                                   = { 
										          + "Environment"        = "TMM" 
										          + "Environment-Entity" = "GK-TF" 
										          + "Environment-Usage"  = "Prod" 
										        } 
										      + timezone                                               = "W. Europe Standard Time" 
										      + virtual_machine_id                                     = (known after apply) 
										      + vm_agent_platform_updates_enabled                      = false 
										 
										      + os_disk { 
										          + caching                   = "ReadWrite" 
										          + disk_size_gb              = (known after apply) 
										          + id                        = (known after apply) 
										          + name                      = "TFT-TMM-CC1-OsDisk" 
										          + storage_account_type      = "Standard_LRS" 
										          + write_accelerator_enabled = false 
										        } 
										 
										      + termination_notification (known after apply) 
										    } 
										 
										  # azurerm_windows_virtual_machine.TACG-TMM-CC2 will be created 
										  + resource "azurerm_windows_virtual_machine" "TACG-TMM-CC2" { 
										      + admin_password                                         = (sensitive value) 
										      + admin_username                                         = (sensitive value) 
										      + allow_extension_operations                             = true 
										      + bypass_platform_safety_checks_on_user_schedule_enabled = false 
										      + computer_name                                          = (known after apply) 
										      + disk_controller_type                                   = (known after apply) 
										      + enable_automatic_updates                               = false 
										      + extensions_time_budget                                 = "PT1H30M" 
										      + hotpatching_enabled                                    = false 
										      + id                                                     = (known after apply) 
										      + location                                               = "westeurope" 
										      + max_bid_price                                          = -1 
										      + name                                                   = "TFT-az-weur-CC2" 
										      + network_interface_ids                                  = (known after apply) 
										      + patch_assessment_mode                                  = "ImageDefault" 
										      + patch_mode                                             = "Manual" 
										      + platform_fault_domain                                  = -1 
										      + priority                                               = "Regular" 
										      + private_ip_address                                     = (known after apply) 
										      + private_ip_addresses                                   = (known after apply) 
										      + provision_vm_agent                                     = true 
										      + public_ip_address                                      = (known after apply) 
										      + public_ip_addresses                                    = (known after apply) 
										      + resource_group_name                                    = "TMM-TEAM-RG-WEUR" 
										      + secure_boot_enabled                                    = false 
										      + size                                                   = "Standard_D2s_v5" 
										      + source_image_id                                        = "/subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TF-WEUR/providers/Microsoft.Compute/galleries/TMM_TF_SIG/images/TMM_TF_CC_Img_W2K25/versions/1.1.0" 
										      + tags                                                   = { 
										          + "Environment"        = "TMM" 
										          + "Environment-Entity" = "GK-TF" 
										          + "Environment-Usage"  = "Prod" 
										        } 
										      + timezone                                               = "W. Europe Standard Time" 
										      + virtual_machine_id                                     = (known after apply) 
										      + vm_agent_platform_updates_enabled                      = false 
										 
										      + os_disk { 
										          + caching                   = "ReadWrite" 
										          + disk_size_gb              = (known after apply) 
										          + id                        = (known after apply) 
										          + name                      = "TFT-TMM-CC2-OsDisk" 
										          + storage_account_type      = "Standard_LRS" 
										          + write_accelerator_enabled = false 
										        } 
										 
										      + termination_notification (known after apply) 
										    } 
										 
										Plan: 6 to add, 0 to change, 0 to destroy. 
										 
										Changes to Outputs: 
										  + NSG_id    = "AZ-West-EUR-GK-nsg" 
										  + Subnet_id = "West-EUR-1-sn" 
										  + VNet_ID   = "/subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TEAM-RG-WEUR/providers/Microsoft.Network/virtualNetworks/West-EUR-vnet" 
										 
										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 
										 
										azurerm_network_interface.TACG-TMM-TF-CC1-NIC: Creating... 
										azurerm_network_interface.TACG-TMM-TF-CC2-NIC: Creating... 
										azurerm_network_interface.TACG-TMM-TF-CC1-NIC: Still creating... [10s elapsed] 
										azurerm_network_interface.TACG-TMM-TF-CC2-NIC: Still creating... [10s elapsed] 
										azurerm_network_interface.TACG-TMM-TF-CC2-NIC: Creation complete after 12s [id=/subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TEAM-RG-WEUR/providers/Microsoft.Network/networkInterfaces/TFT-az-weur-CC2-NIC] 
										azurerm_network_interface_security_group_association.TACG-TMM-TF-CC2-NICNSG: Creating... 
										azurerm_network_interface.TACG-TMM-TF-CC1-NIC: Still creating... [20s elapsed] 
										azurerm_network_interface_security_group_association.TACG-TMM-TF-CC2-NICNSG: Still creating... [10s elapsed] 
										azurerm_network_interface.TACG-TMM-TF-CC1-NIC: Creation complete after 23s [id=/subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TEAM-RG-WEUR/providers/Microsoft.Network/networkInterfaces/TFT-az-weur-CC1-NIC] 
										azurerm_network_interface_security_group_association.TACG-TMM-TF-CC1-NICNSG: Creating... 
										azurerm_network_interface_security_group_association.TACG-TMM-TF-CC2-NICNSG: Creation complete after 11s [id=/subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TEAM-RG-WEUR/providers/Microsoft.Network/networkInterfaces/TFT-az-weur-CC2-NIC|/subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TEAM-RG-WEUR/providers/Microsoft.Network/networkSecurityGroups/AZ-West-EUR-GK-nsg] 
										azurerm_windows_virtual_machine.TACG-TMM-CC2: Creating... 
										azurerm_network_interface_security_group_association.TACG-TMM-TF-CC1-NICNSG: Still creating... [10s elapsed] 
										azurerm_windows_virtual_machine.TACG-TMM-CC2: Still creating... [10s elapsed] 
										azurerm_network_interface_security_group_association.TACG-TMM-TF-CC1-NICNSG: Creation complete after 11s [id=/subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TEAM-RG-WEUR/providers/Microsoft.Network/networkInterfaces/TFT-az-weur-CC1-NIC|/subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TEAM-RG-WEUR/providers/Microsoft.Network/networkSecurityGroups/AZ-West-EUR-GK-nsg] 
										azurerm_windows_virtual_machine.TACG-TMM-CC1: Creating... 
										azurerm_windows_virtual_machine.TACG-TMM-CC2: Still creating... [20s elapsed] 
										azurerm_windows_virtual_machine.TACG-TMM-CC1: Still creating... [10s elapsed] 
										azurerm_windows_virtual_machine.TACG-TMM-CC2: Still creating... [30s elapsed] 
										azurerm_windows_virtual_machine.TACG-TMM-CC1: Still creating... [20s elapsed] 
										azurerm_windows_virtual_machine.TACG-TMM-CC2: Still creating... [40s elapsed] 
										azurerm_windows_virtual_machine.TACG-TMM-CC1: Still creating... [30s elapsed] 
										... 
										azurerm_windows_virtual_machine.TACG-TMM-CC1: Still creating... [1m40s elapsed] 
										azurerm_windows_virtual_machine.TACG-TMM-CC2: Still creating... [2m0s elapsed] 
										azurerm_windows_virtual_machine.TACG-TMM-CC1: Still creating... [1m50s elapsed] 
										azurerm_windows_virtual_machine.TACG-TMM-CC2: Creation complete after 2m5s [id=/subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TEAM-RG-WEUR/providers/Microsoft.Compute/virtualMachines/TFT-az-weur-CC2] 
										azurerm_windows_virtual_machine.TACG-TMM-CC1: Still creating... [2m0s elapsed] 
										azurerm_windows_virtual_machine.TACG-TMM-CC1: Creation complete after 2m3s [id=/subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TEAM-RG-WEUR/providers/Microsoft.Compute/virtualMachines/TFT-az-weur-CC1] 
										 
										Apply complete! Resources: 6 added, 0 changed, 0 destroyed. 
										 
										Outputs: 
										 
										NSG_id = "AZ-West-EUR-GK-nsg" 
										Subnet_id = "West-EUR-1-sn" 
										VNet_ID = "/subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TEAM-RG-WEUR/providers/Microsoft.Network/virtualNetworks/West-EUR-vnet" 
										PS C:\TMM-Team\CreateVMs&gt;
									 
								
							
						
					
				

				
					Terraform successfully created the two Cloud Connector Virtual Machines. 
					Now, the next step can be started.
				 

				
					Module 4: Putting the 2 Cloud Connector VMs Into the Active Directory Domain
				

				
					Terraform puts the just-created 2 Cloud Connector VMs into the Active Directory Domain in this Module. 
				 

				
					Not all Terraform Providers have this ability, so we decided to show a Terraform Provider-agnostic way—we use an Ansible Playbook for this step.
				 

				
					Terraform calls Ansible and the corresponding Ansible Playbook directly from its code snippet.
				 

				
					 
				 

				
					The module is split into the following configuration parts:
				 

				
					
						Putting the just-created Cloud Connector-VMs into the Active Directory Domain using an Ansible Playbook
					
				

				
					
						IMPORTANT:
					 

					
						Ensure flawless communication between Terraform and the Ansible Control Server.
					 

					
						You can find more information about this and a description of installing Ansible and initial configurations in our Tech Zone Deployment Guide Installing and Configuring Ansible, Terraform, and Packer for Citrix Infrastructure-as-Code-based Environments
					 
				

				
					The Ansible Playbook is written in YAML and contains all necessary Information for joining the VMs Into the Active Directory Domain - for example: 
				 

				
					
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}					
					
						
							
								
									
										
											 
										 

										
											--- 
											- name: join host to domain with automatic reboot 
											  hosts: cloudconnector1-ip 
											 
											  tasks: 
											  - name: join host to domain with automatic reboot 
											    microsoft.ad.membership: 
											      dns_domain_name: wwco.net 
											      hostname: az-weur-tf-cc1 
											      domain_admin_user: XXXX@XXXX.XXX 
											      domain_admin_password: "XxXxXxXxXxXxXxXxX” 
											      domain_ou_path: "OU=Infra,OU=WestEUR,OU=Azure,OU=Cloud,OU=Compute,DC=wwco,DC=net" 
											      state: domain 
											      reboot: false 
											 
											 
										 
									
								
							
						
					
				

				
					
						NOTE: 
					 

					
						We would recommend testing the Ansible Playbooks before applying. 
						You can do this by using this command e.g., for the Domain-Join Playbook: 
						 
					 
				

				
					
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}					
					
						
							
								
									
										tmm-azadmin@az-weur-devops-automation-devops:/etc/ansible$ ansible-playbook /etc/ansible/Join-VMToDomain-CC1.microsoft.ad.ansible.yml --check -c winrm 
										 
										PLAY [join host to domain with automatic reboot] *********************************************************************** 
										 
										TASK [Gathering Facts] ************************************************************************************************* 
										ok: [10.53.16.101] 
										 
										TASK [join host to domain with automatic reboot] *********************************************************************** 
										ok: [10.53.16.101] 
										 
										PLAY RECAP ************************************************************************************************************* 
										10.53.16.101               : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 
										 
										tmm-azadmin@az-weur-devops-automation-devops:/etc/ansible$
									 
								
							
						
					
				

				
					Running the Playbook in check mode shows you if a smooth run would occur or errors can be expected.
				 

				
					
						IMPORTANT:
					 

					
						Please ensure you have configured the variables according to your needs using the corresponding .auto.tfvars.json file.
					 
				

				
					The configuration can be started by following the standard Terraform workflow:
				 

				
					
						terraform init,
					 

					
						terraform plan
					 
				

				
					and if no errors occur
				 

				
					
						terraform apply
					 
				

				
					 
				 

				
					
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}					
					
						
							
								
									
										PS C:\TMM-Team\UseAnsibleToJoinDomain&gt; terraform init 
										Initializing the backend... 
										Initializing provider plugins... 
										- Finding latest version of hashicorp/http... 
										- Finding citrix/citrix versions matching "1.0.13"... 
										- Finding hashicorp/azuread versions matching "&gt;= 3.0.2"... 
										- Finding hashicorp/azurerm versions matching "&gt;= 4.6.0"... 
										- Finding latest version of hashicorp/null... 
										- Installing hashicorp/null v3.2.3... 
										- Installed hashicorp/null v3.2.3 (signed by HashiCorp) 
										- Installing hashicorp/http v3.4.5... 
										- Installed hashicorp/http v3.4.5 (signed by HashiCorp) 
										- Installing citrix/citrix v1.0.13... 
										- Installed citrix/citrix v1.0.13 (self-signed, key ID BD4BD0E690CB7D88) 
										- Installing hashicorp/azuread v3.1.0... 
										- Installed hashicorp/azuread v3.1.0 (signed by HashiCorp) 
										- Installing hashicorp/azurerm v4.22.0... 
										- Installed hashicorp/azurerm v4.22.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:\TMM-Team\UseAnsibleToJoinDomain&gt; terraform plan 
										 
										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: 
										 
										  # null_resource.AddCC1ToDomain will be created 
										  + resource "null_resource" "AddCC1ToDomain" { 
										      + id = (known after apply) 
										    } 
										 
										  # null_resource.AddCC2ToDomain will be created 
										  + resource "null_resource" "AddCC2ToDomain" { 
										      + id = (known after apply) 
										    } 
										 
										  # null_resource.CopyPlaybookForCC1ToAnsibleServer will be created 
										  + resource "null_resource" "CopyPlaybookForCC1ToAnsibleServer" { 
										      + id = (known after apply) 
										    } 
										 
										  # null_resource.CopyPlaybookForCC2ToAnsibleServer will be created 
										  + resource "null_resource" "CopyPlaybookForCC2ToAnsibleServer" { 
										      + id = (known after apply) 
										    } 
										 
										Plan: 4 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:\TMM-Team\UseAnsibleToJoinDomain&gt; terraform apply 
										 
										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: 
										 
										  # null_resource.AddCC1ToDomain will be created 
										  + resource "null_resource" "AddCC1ToDomain" { 
										      + id = (known after apply) 
										    } 
										 
										  # null_resource.AddCC2ToDomain will be created 
										  + resource "null_resource" "AddCC2ToDomain" { 
										      + id = (known after apply) 
										    } 
										 
										  # null_resource.CopyPlaybookForCC1ToAnsibleServer will be created 
										  + resource "null_resource" "CopyPlaybookForCC1ToAnsibleServer" { 
										      + id = (known after apply) 
										    } 
										 
										  # null_resource.CopyPlaybookForCC2ToAnsibleServer will be created 
										  + resource "null_resource" "CopyPlaybookForCC2ToAnsibleServer" { 
										      + id = (known after apply) 
										    } 
										 
										Plan: 4 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.CopyPlaybookForCC1ToAnsibleServer: Creating... 
										null_resource.CopyPlaybookForCC2ToAnsibleServer: Creating... 
										null_resource.CopyPlaybookForCC2ToAnsibleServer: Provisioning with 'file'... 
										null_resource.CopyPlaybookForCC1ToAnsibleServer: Provisioning with 'file'... 
										null_resource.CopyPlaybookForCC2ToAnsibleServer: Creation complete after 1s [id=1021300872990083837] 
										null_resource.AddCC2ToDomain: Creating... 
										null_resource.AddCC2ToDomain: Provisioning with 'remote-exec'... 
										null_resource.AddCC2ToDomain (remote-exec): Connecting to remote host via SSH... 
										null_resource.AddCC2ToDomain (remote-exec):   Host: 10.53.16.10 
										null_resource.AddCC2ToDomain (remote-exec):   User: azXXXXXXXX 
										null_resource.AddCC2ToDomain (remote-exec):   Password: true 
										null_resource.AddCC2ToDomain (remote-exec):   Private key: false 
										null_resource.AddCC2ToDomain (remote-exec):   Certificate: false 
										null_resource.AddCC2ToDomain (remote-exec):   SSH Agent: false 
										null_resource.AddCC2ToDomain (remote-exec):   Checking Host Key: false 
										null_resource.AddCC2ToDomain (remote-exec):   Target Platform: unix 
										null_resource.CopyPlaybookForCC1ToAnsibleServer: Creation complete after 1s [id=4834864719047546946] 
										null_resource.AddCC2ToDomain (remote-exec): Connected! 
										null_resource.AddCC1ToDomain: Creating... 
										null_resource.AddCC1ToDomain: Provisioning with 'remote-exec'... 
										null_resource.AddCC1ToDomain (remote-exec): Connecting to remote host via SSH... 
										null_resource.AddCC1ToDomain (remote-exec):   Host: 10.53.16.10 
										null_resource.AddCC1ToDomain (remote-exec):   User: azXXXXXXXX 
										null_resource.AddCC1ToDomain (remote-exec):   Password: true 
										null_resource.AddCC1ToDomain (remote-exec):   Private key: false 
										null_resource.AddCC1ToDomain (remote-exec):   Certificate: false 
										null_resource.AddCC1ToDomain (remote-exec):   SSH Agent: false 
										null_resource.AddCC1ToDomain (remote-exec):   Checking Host Key: false 
										null_resource.AddCC1ToDomain (remote-exec):   Target Platform: unix 
										null_resource.AddCC1ToDomain (remote-exec): Connected! 
										 
										null_resource.AddCC2ToDomain (remote-exec): Using /etc/ansible/ansible.cfg as config file 
										null_resource.AddCC1ToDomain (remote-exec): Using /etc/ansible/ansible.cfg as config file 
										 
										null_resource.AddCC2ToDomain (remote-exec): PLAY [join host to domain with automatic reboot] ******************************* 
										 
										null_resource.AddCC2ToDomain (remote-exec): TASK [Gathering Facts] ********************************************************* 
										 
										null_resource.AddCC1ToDomain (remote-exec): PLAY [join host to domain with automatic reboot] ******************************* 
										 
										null_resource.AddCC1ToDomain (remote-exec): TASK [Gathering Facts] ********************************************************* 
										null_resource.AddCC2ToDomain (remote-exec): ok: [10.53.16.102] 
										 
										null_resource.AddCC2ToDomain (remote-exec): TASK [join host to domain with automatic reboot] ******************************* 
										null_resource.AddCC1ToDomain (remote-exec): ok: [10.53.16.101] 
										 
										null_resource.AddCC1ToDomain (remote-exec): TASK [join host to domain with automatic reboot] ******************************* 
										null_resource.AddCC2ToDomain: Still creating... [10s elapsed] 
										null_resource.AddCC1ToDomain: Still creating... [10s elapsed] 
										null_resource.AddCC2ToDomain (remote-exec): changed: [10.53.16.102] =&gt; {"changed": true, "reboot_required": true} 
										 
										null_resource.AddCC2ToDomain (remote-exec): PLAY RECAP ********************************************************************* 
										null_resource.AddCC2ToDomain (remote-exec): 10.53.16.102               : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 
										 
										null_resource.AddCC2ToDomain: Creation complete after 15s [id=4528100002845834178] 
										null_resource.AddCC1ToDomain (remote-exec): changed: [10.53.16.101] =&gt; {"changed": true, "reboot_required": true} 
										 
										null_resource.AddCC1ToDomain (remote-exec): PLAY RECAP ********************************************************************* 
										null_resource.AddCC1ToDomain (remote-exec): 10.53.16.101               : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 
										 
										null_resource.AddCC1ToDomain: Creation complete after 18s [id=2667672656305371565] 
										 
										Apply complete! Resources: 4 added, 0 changed, 0 destroyed. 
										PS C:\TMM-Team\UseAnsibleToJoinDomain&gt;
									 
								
							
						
					
				

				
					Terraform successfully put the Cloud Connectors into the Active Directory Domain. 
					Now, the next step can be started.
				 

				
					Module 5: Install and Configure the two Cloud Connector Software
				

				
					Terraform installs and configures the Cloud Connector software and all the needed entities in this module. It is split into the following configuration parts:
				 

				
					
						Creating a new Resource Location and the adjacent Zone in Citrix Cloud
					
					
						Uploading the Cloud Connector software installer to the Cloud Connector VMs
					
					
						Dynamically creating the configuration file cwc.json based on the Resource Location- and Zone-Information and uploading it to the Cloud Connector VMs
					
					
						Installing and configuring the Cloud Connector software using an Ansible Playbook
					
					
						Adding the Cloud Connectors to the newly created Resource Location
					
				

				
					
						IMPORTANT:
					 

					
						Ensure flawless communication between Terraform and the Ansible Control Server.
					 

					
						You can find more information about this and a description of installing Ansible and initial configurations in our Tech Zone Deployment Guide Installing and Configuring Ansible for use with the Citrix Terraform Provider
					 
				

				
					The Ansible Playbook is written in YAML and contains all the necessary information for deploying the Cloud Connector software - for example: 
				 

				
					
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}					
					
						
							
								
									
										
											---
										 
										- name: install citrix cloud connector 
										  hosts: cloudconnector1-ip 
										 
										  tasks: 
										  - name: install citrix cloud connector  
										    ansible.windows.win_package: 
										      path: C:\temp\cwcconnector.exe 
										      product_id: CWCConnector.exe 
										      arguments: 
										        - /q 
										        - /ParametersFilePath:C:\temp\cwc.json 
										      state: present 
										    become: true 
										    become_method: runas 
										    become_user: SYSTEM
									
								
							
						
					
				

				
					 
				 

				
					
						IMPORTANT:
					 

					
						Please ensure you have configured the variables according to your needs using the corresponding .auto.tfvars.json file.
					 
				

				
					The configuration can be started by following the standard Terraform workflow:
				 

				
					
						terraform init,
					 

					
						terraform plan
					 
				

				
					and if no errors occur
				 

				
					
						terraform apply
					 
				

				
					
						IMPORTANT:
					 

					
						In previous versions, the Terraform script will pause for some time after creating the Resource Location. 
						This is because of time constraints in the back-end. 
					 

					
						The creation of the Zone related to the Resource Location takes some time—we have seen delays of up to 8 minutes before the Zone is created. 
					 

					
						The actual version of the Terraform provider should take care of that and wait until the creation is finished. 
						The previous Wait commands are therefore commented out in the Terraform snippets. 
						If you want them to be used, just remove the comments.  
						 
					 
				

				
					 
				 

				
					
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}					
					
						
							
								
									
										PS C:\TMM-Team\DeployCCs&gt; terraform init 
										Initializing the backend... 
										Initializing provider plugins... 
										- Finding latest version of hashicorp/local... 
										- Finding latest version of hashicorp/null... 
										- Finding latest version of hashicorp/http... 
										- Finding hashicorp/azurerm versions matching "&gt;= 4.6.0"... 
										- Finding citrix/citrix versions matching "1.0.13"... 
										- Finding hashicorp/azuread versions matching "&gt;= 3.0.2"... 
										- Finding latest version of hashicorp/time... 
										- Installing hashicorp/null v3.2.3... 
										- Installed hashicorp/null v3.2.3 (signed by HashiCorp) 
										- Installing hashicorp/http v3.4.5... 
										- Installed hashicorp/http v3.4.5 (signed by HashiCorp) 
										- Installing hashicorp/azurerm v4.22.0... 
										- Installed hashicorp/azurerm v4.22.0 (signed by HashiCorp) 
										- Installing citrix/citrix v1.0.13... 
										- Installed citrix/citrix v1.0.13 (self-signed, key ID BD4BD0E690CB7D88) 
										- Installing hashicorp/azuread v3.1.0... 
										- Installed hashicorp/azuread v3.1.0 (signed by HashiCorp) 
										- Installing hashicorp/time v0.13.0... 
										- Installed hashicorp/time v0.13.0 (signed by HashiCorp) 
										- Installing hashicorp/local v2.5.2... 
										- Installed hashicorp/local v2.5.2 (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:\TMM-Team\DeployCCs&gt; terraform plan 
										 
										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_cloud_resource_location.CreateCCRLInCC will be created 
										  + resource "citrix_cloud_resource_location" "CreateCCRLInCC" { 
										      + id            = (known after apply) 
										      + internal_only = false 
										      + name          = "TF-Azure-WEUR" 
										      + time_zone     = "GMT Standard Time" 
										    } 
										 
										  # citrix_zone.CreateZoneInCC will be created 
										  + resource "citrix_zone" "CreateZoneInCC" { 
										      + description          = (known after apply) 
										      + id                   = (known after apply) 
										      + name                 = (known after apply) 
										      + resource_location_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             = "./assets/cwc.json" 
										      + id                   = (known after apply) 
										    } 
										 
										  # local_file.WriteRLDataToFile will be created 
										  + resource "local_file" "WriteRLDataToFile" { 
										      + content              = (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) 
										      + directory_permission = "0777" 
										      + file_permission      = "0777" 
										      + filename             = "./assets/RLData.txt" 
										      + id                   = (known after apply) 
										    } 
										 
										  # local_file.WriteZoneDataToFile will be created 
										  + resource "local_file" "WriteZoneDataToFile" { 
										      + content              = (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) 
										      + directory_permission = "0777" 
										      + file_permission      = "0777" 
										      + filename             = "./assets/ZoneData.txt" 
										      + id                   = (known after apply) 
										    } 
										 
										  # null_resource.CopyPlaybookForCC1ToAnsibleServer will be created 
										  + resource "null_resource" "CopyPlaybookForCC1ToAnsibleServer" { 
										      + id = (known after apply) 
										    } 
										 
										  # null_resource.CopyPlaybookForCC2ToAnsibleServer will be created 
										  + resource "null_resource" "CopyPlaybookForCC2ToAnsibleServer" { 
										      + id = (known after apply) 
										    } 
										 
										  # null_resource.InstallCCOnCC1 will be created 
										  + resource "null_resource" "InstallCCOnCC1" { 
										      + id = (known after apply) 
										    } 
										 
										  # null_resource.InstallCCOnCC2 will be created 
										  + resource "null_resource" "InstallCCOnCC2" { 
										      + id = (known after apply) 
										    } 
										 
										  # null_resource.UploadCCAndCWCToCC1 will be created 
										  + resource "null_resource" "UploadCCAndCWCToCC1" { 
										      + id = (known after apply) 
										    } 
										 
										  # null_resource.UploadCCAndCWCToCC2 will be created 
										  + resource "null_resource" "UploadCCAndCWCToCC2" { 
										      + id = (known after apply) 
										    } 
										 
										Plan: 11 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:\TMM-Team\DeployCCs&gt; terraform apply 
										 
										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_cloud_resource_location.CreateCCRLInCC will be created 
										  + resource "citrix_cloud_resource_location" "CreateCCRLInCC" { 
										      + id            = (known after apply) 
										      + internal_only = false 
										      + name          = "TF-Azure-WEUR" 
										      + time_zone     = "GMT Standard Time" 
										    } 
										 
										  # citrix_zone.CreateZoneInCC will be created 
										  + resource "citrix_zone" "CreateZoneInCC" { 
										      + description          = (known after apply) 
										      + id                   = (known after apply) 
										      + name                 = (known after apply) 
										      + resource_location_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             = "./assets/cwc.json" 
										      + id                   = (known after apply) 
										    } 
										 
										  # local_file.WriteRLDataToFile will be created 
										  + resource "local_file" "WriteRLDataToFile" { 
										      + content              = (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) 
										      + directory_permission = "0777" 
										      + file_permission      = "0777" 
										      + filename             = "./assets/RLData.txt" 
										      + id                   = (known after apply) 
										    } 
										 
										  # local_file.WriteZoneDataToFile will be created 
										  + resource "local_file" "WriteZoneDataToFile" { 
										      + content              = (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) 
										      + directory_permission = "0777" 
										      + file_permission      = "0777" 
										      + filename             = "./assets/ZoneData.txt" 
										      + id                   = (known after apply) 
										    } 
										 
										  # null_resource.CopyPlaybookForCC1ToAnsibleServer will be created 
										  + resource "null_resource" "CopyPlaybookForCC1ToAnsibleServer" { 
										      + id = (known after apply) 
										    } 
										 
										  # null_resource.CopyPlaybookForCC2ToAnsibleServer will be created 
										  + resource "null_resource" "CopyPlaybookForCC2ToAnsibleServer" { 
										      + id = (known after apply) 
										    } 
										 
										  # null_resource.InstallCCOnCC1 will be created 
										  + resource "null_resource" "InstallCCOnCC1" { 
										      + id = (known after apply) 
										    } 
										 
										  # null_resource.InstallCCOnCC2 will be created 
										  + resource "null_resource" "InstallCCOnCC2" { 
										      + id = (known after apply) 
										    } 
										 
										  # null_resource.UploadCCAndCWCToCC1 will be created 
										  + resource "null_resource" "UploadCCAndCWCToCC1" { 
										      + id = (known after apply) 
										    } 
										 
										  # null_resource.UploadCCAndCWCToCC2 will be created 
										  + resource "null_resource" "UploadCCAndCWCToCC2" { 
										      + id = (known after apply) 
										    } 
										 
										Plan: 11 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_cloud_resource_location.CreateCCRLInCC: Creating... 
										citrix_cloud_resource_location.CreateCCRLInCC: Creation complete after 0s [id=b70e021d-bf9a-4771-bddc-4dcec5fe92d6] 
										citrix_zone.CreateZoneInCC: Creating... 
										citrix_zone.CreateZoneInCC: Still creating... [10s elapsed] 
										citrix_zone.CreateZoneInCC: Still creating... [20s elapsed] 
										... 
										citrix_zone.CreateZoneInCC: Still creating... [2m30s elapsed] 
										citrix_zone.CreateZoneInCC: Still creating... [2m40s elapsed] 
										citrix_zone.CreateZoneInCC: Creation complete after 2m45s [id=649e6c0a-14db-481e-bf37-0c259002302e] 
										local_file.WriteRLDataToFile: Creating... 
										local_file.WriteZoneDataToFile: Creating... 
										local_file.CWC-Configuration: Creating... 
										local_file.WriteRLDataToFile: Creation complete after 0s [id=45259dcbdde180412b7a770eefe1a026003d3a6a] 
										local_file.CWC-Configuration: Creation complete after 0s [id=54ac8241e845c65e964d3d9d40145b9050409c2e] 
										local_file.WriteZoneDataToFile: Creation complete after 0s [id=a856abf469bebb6e6884c15e92dab1981acd6adb] 
										null_resource.UploadCCAndCWCToCC2: Creating... 
										null_resource.UploadCCAndCWCToCC2: Provisioning with 'file'... 
										null_resource.UploadCCAndCWCToCC1: Creating... 
										null_resource.UploadCCAndCWCToCC1: Provisioning with 'file'... 
										#&lt; CLIXML 
										&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
										&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
										&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadCCAndCWCToCC2: Provisioning with 'file'... 
										#&lt; CLIXML 
										&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadCCAndCWCToCC1: Provisioning with 'file'... 
										null_resource.UploadCCAndCWCToCC2: Still creating... [10s elapsed] 
										null_resource.UploadCCAndCWCToCC1: Still creating... [10s elapsed] 
										null_resource.UploadCCAndCWCToCC2: Still creating... [20s elapsed] 
										null_resource.UploadCCAndCWCToCC1: Still creating... [20s elapsed] 
										null_resource.UploadCCAndCWCToCC2: Still creating... [30s elapsed] 
										null_resource.UploadCCAndCWCToCC1: Still creating... [30s elapsed] 
										... 
										null_resource.UploadCCAndCWCToCC2: Still creating... [41m30s elapsed] 
										null_resource.UploadCCAndCWCToCC1: Still creating... [41m30s elapsed] 
										null_resource.UploadCCAndCWCToCC2: Still creating... [41m40s elapsed] 
										null_resource.UploadCCAndCWCToCC1: Still creating... [41m40s elapsed] 
										#&lt; CLIXML 
										&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
										&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadCCAndCWCToCC2: Creation complete after 41m48s [id=1216795657683854215] 
										null_resource.CopyPlaybookForCC2ToAnsibleServer: Creating... 
										null_resource.CopyPlaybookForCC2ToAnsibleServer: Provisioning with 'file'... 
										null_resource.CopyPlaybookForCC2ToAnsibleServer: Creation complete after 1s [id=8272431392781703777] 
										null_resource.InstallCCOnCC2: Creating... 
										null_resource.InstallCCOnCC2: Provisioning with 'remote-exec'... 
										null_resource.InstallCCOnCC2 (remote-exec): Connecting to remote host via SSH... 
										null_resource.InstallCCOnCC2 (remote-exec):   Host: 10.53.16.10 
										null_resource.InstallCCOnCC2 (remote-exec):   User: azXXXXXXXX 
										null_resource.InstallCCOnCC2 (remote-exec):   Password: true 
										null_resource.InstallCCOnCC2 (remote-exec):   Private key: false 
										null_resource.InstallCCOnCC2 (remote-exec):   Certificate: false 
										null_resource.InstallCCOnCC2 (remote-exec):   SSH Agent: false 
										null_resource.InstallCCOnCC2 (remote-exec):   Checking Host Key: false 
										null_resource.InstallCCOnCC2 (remote-exec):   Target Platform: unix 
										null_resource.InstallCCOnCC2 (remote-exec): Connected! 
										null_resource.InstallCCOnCC2 (remote-exec): Using /etc/ansible/ansible.cfg as config file 
										 
										null_resource.InstallCCOnCC2 (remote-exec): PLAY [install citrix cloud connector] ****************************************** 
										 
										null_resource.InstallCCOnCC2 (remote-exec): TASK [Gathering Facts] ********************************************************* 
										null_resource.UploadCCAndCWCToCC1: Still creating... [41m50s elapsed] 
										 
										null_resource.InstallCCOnCC2 (remote-exec): ok: [10.53.16.102] 
										 
										null_resource.InstallCCOnCC2 (remote-exec): TASK [install citrix cloud connector] ****************************************** 
										null_resource.InstallCCOnCC2: Still creating... [10s elapsed] 
										null_resource.UploadCCAndCWCToCC1: Still creating... [42m0s elapsed] 
										null_resource.InstallCCOnCC2: Still creating... [20s elapsed] 
										null_resource.UploadCCAndCWCToCC1: Still creating... [42m10s elapsed] 
										null_resource.InstallCCOnCC2: Still creating... [30s elapsed] 
										null_resource.UploadCCAndCWCToCC1: Still creating... [42m20s elapsed] 
										null_resource.InstallCCOnCC2 (remote-exec): changed: [10.53.16.102] =&gt; {"changed": true, "rc": 0, "reboot_required": false} 
										 
										null_resource.InstallCCOnCC2 (remote-exec): PLAY RECAP ********************************************************************* 
										null_resource.InstallCCOnCC2 (remote-exec): 10.53.16.102               : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 
										 
										null_resource.InstallCCOnCC2: Creation complete after 37s [id=5649037648781332493] 
										null_resource.UploadCCAndCWCToCC1: Still creating... [42m30s elapsed] 
										null_resource.UploadCCAndCWCToCC1: Still creating... [42m40s elapsed] 
										null_resource.UploadCCAndCWCToCC1: Still creating... [42m50s elapsed] 
										null_resource.UploadCCAndCWCToCC1: Still creating... [43m0s elapsed] 
										#&lt; CLIXML 
										&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
										&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadCCAndCWCToCC1: Creation complete after 43m6s [id=4009831087358643720] 
										null_resource.CopyPlaybookForCC1ToAnsibleServer: Creating... 
										null_resource.CopyPlaybookForCC1ToAnsibleServer: Provisioning with 'file'... 
										null_resource.CopyPlaybookForCC1ToAnsibleServer: Creation complete after 1s [id=5109499130471891394] 
										null_resource.InstallCCOnCC1: Creating... 
										null_resource.InstallCCOnCC1: Provisioning with 'remote-exec'... 
										null_resource.InstallCCOnCC1 (remote-exec): Connecting to remote host via SSH... 
										null_resource.InstallCCOnCC1 (remote-exec):   Host: 10.53.16.10 
										null_resource.InstallCCOnCC1 (remote-exec):   User: azXXXXXXXX 
										null_resource.InstallCCOnCC1 (remote-exec):   Password: true 
										null_resource.InstallCCOnCC1 (remote-exec):   Private key: false 
										null_resource.InstallCCOnCC1 (remote-exec):   Certificate: false 
										null_resource.InstallCCOnCC1 (remote-exec):   SSH Agent: false 
										null_resource.InstallCCOnCC1 (remote-exec):   Checking Host Key: false 
										null_resource.InstallCCOnCC1 (remote-exec):   Target Platform: unix 
										null_resource.InstallCCOnCC1 (remote-exec): Connected! 
										null_resource.InstallCCOnCC1 (remote-exec): Using /etc/ansible/ansible.cfg as config file 
										 
										null_resource.InstallCCOnCC1 (remote-exec): PLAY [install citrix cloud connector] ****************************************** 
										 
										null_resource.InstallCCOnCC1 (remote-exec): TASK [Gathering Facts] ********************************************************* 
										 
										null_resource.InstallCCOnCC1 (remote-exec): ok: [10.53.16.101] 
										 
										null_resource.InstallCCOnCC1 (remote-exec): TASK [install citrix cloud connector] ****************************************** 
										null_resource.InstallCCOnCC1: Still creating... [10s elapsed] 
										null_resource.InstallCCOnCC1: Still creating... [20s elapsed] 
										null_resource.InstallCCOnCC1: Still creating... [30s elapsed] 
										null_resource.InstallCCOnCC1: Still creating... [40s elapsed] 
										null_resource.InstallCCOnCC1 (remote-exec): changed: [10.53.16.101] =&gt; {"changed": true, "rc": 0, "reboot_required": false} 
										 
										null_resource.InstallCCOnCC1 (remote-exec): PLAY RECAP ********************************************************************* 
										null_resource.InstallCCOnCC1 (remote-exec): 10.53.16.101               : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 
										 
										null_resource.InstallCCOnCC1: Creation complete after 41s [id=1715104765887243575] 
										 
										Apply complete! Resources: 11 added, 0 changed, 0 destroyed. 
										PS C:\TMM-Team\DeployCCs&gt; 
										 
									 
								
							
						
					
				

				
					Terraform successfully installed and configured the Cloud Connectors and the adjacent entities.
				 

				
					Module 6: Create the Citrix Cloud Entities
				

				
					In this Module, Terraform creates all needed Citrix Cloud entities.  
					It is split into the following configuration parts:
				 

				
					
						Creating the Hypervisor Connection and Hypervisor Connection Resource Pool on Citrix Cloud
					
					
						Creating a Machine Catalog 
					
					
						Creating a Delivery Group based on the Machine Catalog
					
				

				
					We will provide two different Code snippets for creating the Machine Catalog:
				 

				
					
						Example One is based on a Citrix Cloud-Prepared Image
					
					
						Example Two is based on a Shared Image in the Azure Shared Image Gallery
					
				

				
					
						IMPORTANT:
					 

					
						Please ensure you have configured the variables according to your needs using the corresponding .auto.tfvars.json file.
					 
				

				
					The configuration can be started by following the standard Terraform workflow:
				 

				
					
						terraform init,
					 

					
						terraform plan
					 
				

				
					and if no errors occur
				 

				
					
						terraform apply
					 
				

				
					Deploying the Hosting Connection and the Hosting Connection Resource Pool:
				

				
					
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}					
					
						
							
								
									
										PS C:\TMM-Team\DeployCItrixCloudEntitites&gt; terraform init 
										Initializing the backend... 
										 
										Successfully configured the backend "local"! Terraform will automatically 
										use this backend unless the backend configuration changes. 
										Initializing provider plugins... 
										- Finding latest version of hashicorp/time... 
										- Finding latest version of hashicorp/http... 
										- Finding hashicorp/azurerm versions matching "&gt;= 4.6.0"... 
										- Finding citrix/citrix versions matching "1.0.13"... 
										- Finding hashicorp/azuread versions matching "&gt;= 3.0.2"... 
										- Installing hashicorp/azurerm v4.22.0... 
										- Installed hashicorp/azurerm v4.22.0 (signed by HashiCorp) 
										- Installing citrix/citrix v1.0.13... 
										- Installed citrix/citrix v1.0.13 (self-signed, key ID BD4BD0E690CB7D88) 
										- Installing hashicorp/azuread v3.1.0... 
										- Installed hashicorp/azuread v3.1.0 (signed by HashiCorp) 
										- Installing hashicorp/time v0.13.0... 
										- Installed hashicorp/time v0.13.0 (signed by HashiCorp) 
										- Installing hashicorp/http v3.4.5... 
										- Installed hashicorp/http v3.4.5 (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:\TMM-Team\DeployCItrixCloudEntitites&gt; terraform plan 
										data.azurerm_shared_image_gallery.GetAzureSIG: Reading... 
										data.azurerm_shared_image_gallery.GetAzureSIG: Read complete after 0s [id=/subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TEAM-RG-WEUR/providers/Microsoft.Compute/galleries/az_weur_acg] 
										data.citrix_zone.GetTFAzureZoneID: Reading... 
										data.citrix_zone.GetTFAzureZoneID: Read complete after 1s [id=649e6c0a-14db-481e-bf37-0c259002302e] 
										 
										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_azure_hypervisor.CreateAzureHostingConnection will be created 
										  + resource "citrix_azure_hypervisor" "CreateAzureHostingConnection" { 
										      + active_directory_id               = (sensitive value) 
										      + application_id                    = (sensitive value) 
										      + application_secret                = (sensitive value) 
										      + enable_azure_ad_device_management = false 
										      + id                                = (known after apply) 
										      + name                              = "TF-HC-TMM-Azure-WEUR-HypConn" 
										      + scopes                            = [] 
										      + subscription_id                   = (sensitive value) 
										      + tenants                           = (known after apply) 
										      + zone                              = "649e6c0a-14db-481e-bf37-0c259002302e" 
										    } 
										 
										  # citrix_azure_hypervisor_resource_pool.CreateAzureHostingConnectionPool will be created 
										  + resource "citrix_azure_hypervisor_resource_pool" "CreateAzureHostingConnectionPool" { 
										      + hypervisor                     = (known after apply) 
										      + id                             = (known after apply) 
										      + name                           = "TF-HC-TMM-Azure-WEUR-HypConn" 
										      + region                         = "westeurope" 
										      + subnets                        = [ 
										          + "West-EUR-1-sn", 
										        ] 
										      + virtual_network                = "West-EUR-vnet" 
										      + virtual_network_resource_group = "TMM-TEAM-RG-WEUR" 
										    } 
										 
										Plan: 2 to add, 0 to change, 0 to destroy. 
										 
										Changes to Outputs: 
										  + hypconnname = (known after apply) 
										  + hypname     = (known after apply) 
										 
										───────────────────────────────────────────────────────────────────────────────────────────────────── 
										 
										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:\TMM-Team\DeployCItrixCloudEntitites&gt; terraform apply 
										data.citrix_zone.GetTFAzureZoneID: Reading... 
										data.citrix_zone.GetTFAzureZoneID: Read complete after 0s [id=649e6c0a-14db-481e-bf37-0c259002302e] 
										data.azurerm_shared_image_gallery.GetAzureSIG: Reading... 
										data.azurerm_shared_image_gallery.GetAzureSIG: Read complete after 0s [id=/subscriptions/dd5bXXXX-XXXX-XXXX-XXXX-XXXXXXXX9e13/resourceGroups/TMM-TEAM-RG-WEUR/providers/Microsoft.Compute/galleries/az_weur_acg] 
										 
										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_azure_hypervisor.CreateAzureHostingConnection will be created 
										  + resource "citrix_azure_hypervisor" "CreateAzureHostingConnection" { 
										      + active_directory_id               = (sensitive value) 
										      + application_id                    = (sensitive value) 
										      + application_secret                = (sensitive value) 
										      + enable_azure_ad_device_management = false 
										      + id                                = (known after apply) 
										      + name                              = "TF-HC-TMM-Azure-WEUR-HypConn" 
										      + scopes                            = [] 
										      + subscription_id                   = (sensitive value) 
										      + tenants                           = (known after apply) 
										      + zone                              = "649e6c0a-14db-481e-bf37-0c259002302e" 
										    } 
										 
										  # citrix_azure_hypervisor_resource_pool.CreateAzureHostingConnectionPool will be created 
										  + resource "citrix_azure_hypervisor_resource_pool" "CreateAzureHostingConnectionPool" { 
										      + hypervisor                     = (known after apply) 
										      + id                             = (known after apply) 
										      + name                           = "TF-HC-TMM-Azure-WEUR-HypConn" 
										      + region                         = "westeurope" 
										      + subnets                        = [ 
										          + "West-EUR-1-sn", 
										        ] 
										      + virtual_network                = "West-EUR-vnet" 
										      + virtual_network_resource_group = "TMM-TEAM-RG-WEUR" 
										    } 
										 
										Plan: 2 to add, 0 to change, 0 to destroy. 
										 
										Changes to Outputs: 
										  + hypconnname = (known after apply) 
										  + hypname     = (known after apply) 
										 
										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_azure_hypervisor.CreateAzureHostingConnection: Creating... 
										citrix_azure_hypervisor.CreateAzureHostingConnection: Still creating... [10s elapsed] 
										citrix_azure_hypervisor.CreateAzureHostingConnection: Still creating... [20s elapsed] 
										citrix_azure_hypervisor.CreateAzureHostingConnection: Still creating... [30s elapsed] 
										citrix_azure_hypervisor.CreateAzureHostingConnection: Still creating... [40s elapsed] 
										citrix_azure_hypervisor.CreateAzureHostingConnection: Creation complete after 41s [id=9c87db11-446f-4971-ab3d-e41ac41b3b43] 
										citrix_azure_hypervisor_resource_pool.CreateAzureHostingConnectionPool: Creating... 
										citrix_azure_hypervisor_resource_pool.CreateAzureHostingConnectionPool: Still creating... [10s elapsed] 
										citrix_azure_hypervisor_resource_pool.CreateAzureHostingConnectionPool: Still creating... [20s elapsed] 
										citrix_azure_hypervisor_resource_pool.CreateAzureHostingConnectionPool: Still creating... [30s elapsed] 
										citrix_azure_hypervisor_resource_pool.CreateAzureHostingConnectionPool: Still creating... [40s elapsed] 
										citrix_azure_hypervisor_resource_pool.CreateAzureHostingConnectionPool: Still creating... [50s elapsed] 
										citrix_azure_hypervisor_resource_pool.CreateAzureHostingConnectionPool: Still creating... [1m0s elapsed] 
										citrix_azure_hypervisor_resource_pool.CreateAzureHostingConnectionPool: Still creating... [1m10s elapsed] 
										citrix_azure_hypervisor_resource_pool.CreateAzureHostingConnectionPool: Still creating... [1m20s elapsed] 
										citrix_azure_hypervisor_resource_pool.CreateAzureHostingConnectionPool: Still creating... [1m30s elapsed] 
										citrix_azure_hypervisor_resource_pool.CreateAzureHostingConnectionPool: Creation complete after 1m36s [id=98baa6e9-9bd5-4038-9eba-965dee61f594] 
										 
										Apply complete! Resources: 2 added, 0 changed, 0 destroyed. 
										 
										PS C:\TMM-Team\DeployCItrixCloudEntitites&gt;
									 
								
							
						
					
				

				
					Deploying the Machine Catalog based on a Prepared Image and the Delivery Group:
				

				
					
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}					
					
						
							
								
									
										PS C:\TMM-Team\DeployCItrixCloudEntitites&gt; terraform plan 
										data.citrix_image_definition.GetImageDefinition: Reading... 
										data.citrix_zone.GetTFAzureZoneID: Reading... 
										data.citrix_zone.GetTFAzureZoneID: Read complete after 1s [id=649e6c0a-14db-481e-bf37-0c259002302e] 
										citrix_azure_hypervisor.CreateAzureHostingConnection: Refreshing state... [id=9c87db11-446f-4971-ab3d-e41ac41b3b43] 
										data.citrix_image_definition.GetImageDefinition: Read complete after 1s [id=13e7550c-4da4-4b84-a5d1-c5a7f299fcf4] 
										data.citrix_image_version.GetImageDefinitionVersion: Reading... 
										citrix_azure_hypervisor_resource_pool.CreateAzureHostingConnectionPool: Refreshing state... [id=98baa6e9-9bd5-4038-9eba-965dee61f594] 
										data.citrix_image_version.GetImageDefinitionVersion: Read complete after 0s [id=b7d68084-d8d9-4ec2-94fa-d770ec63b901] 
										 
										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        = 60 
										          + disconnect_peak_idle_session_after_seconds            = 60 
										          + log_off_off_peak_disconnected_session_after_seconds   = 60 
										          + log_off_peak_disconnected_session_after_seconds       = 60 
										          + log_off_reminder_enabled                              = false 
										          + log_off_reminder_message                              = "" 
										          + log_off_reminder_title                                = "" 
										          + log_off_warning_message                               = "" 
										          + log_off_warning_title                                 = "" 
										          + 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_limit_seconds_to_force_log_off_user          = 0 
										          + off_peak_log_off_action                               = "Nothing" 
										          + off_peak_log_off_reminder_interval                    = 0 
										          + off_peak_log_off_timeout_minutes                      = 0 
										          + peak_autoscale_assigned_power_on_idle_action          = "Nothing" 
										          + peak_autoscale_assigned_power_on_idle_timeout_minutes = 0 
										          + 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_limit_seconds_to_force_log_off_user              = 0 
										          + peak_log_off_action                                   = "Nothing" 
										          + peak_log_off_reminder_interval                        = 0 
										          + peak_log_off_timeout_minutes                          = 0 
										          + power_off_delay_minutes                               = 30 
										          + power_time_schemes                                    = [ 
										              + { 
										                  + days_of_week          = [ 
										                      + "Friday", 
										                      + "Monday", 
										                      + "Thursday", 
										                      + "Tuesday", 
										                      + "Wednesday", 
										                    ] 
										                  + display_name          = "TF-AS-TMM-Azure-Weekday" 
										                  + peak_time_ranges      = [ 
										                      + "09:00-17:00", 
										                    ] 
										                  + pool_size_schedules   = [ 
										                      + { 
										                          + pool_size  = 1 
										                          + time_range = "09:00-17:00" 
										                        }, 
										                    ] 
										                  + pool_using_percentage = false 
										                }, 
										            ] 
										        } 
										      + built_in_scopes             = (known after apply) 
										      + default_desktop_icon        = "1" 
										      + description                 = "" 
										      + desktops                    = [ 
										          + { 
										              + description             = "Terraform-based Delivery Group running on Azure in WEUR" 
										              + enabled                 = true 
										              + published_name          = "TF-DG-TMM-Azure" 
										              + restricted_access_users = { 
										                  + allow_list = [ 
										                      + "WWCO\\PoliciesForWEUR-Team", 
										                    ] 
										                  + block_list = [] 
										                } 
										            }, 
										        ] 
										      + enabled                     = true 
										      + force_delete                = false 
										      + id                          = (known after apply) 
										      + inherited_scopes            = (known after apply) 
										      + minimum_functional_level    = "L7_20" 
										      + name                        = "TF-DG-TMM-Azure-WEUR" 
										      + reboot_schedules            = [ 
										          + { 
										              + days_in_week            = [ 
										                  + "Sunday", 
										                ] 
										              + frequency               = "Weekly" 
										              + frequency_factor        = 1 
										              + ignore_maintenance_mode = true 
										              + name                    = "TMM-GK-TF-RebootScheduleN" 
										              + natural_reboot_schedule = false 
										              + reboot_duration_minutes = 0 
										              + reboot_schedule_enabled = true 
										              + start_date              = "2024-01-01" 
										              + start_time              = "02:00" 
										                # (1 unchanged attribute hidden) 
										            }, 
										        ] 
										      + restricted_access_users     = { 
										          + allow_list = [ 
										              + "WWCO\\PoliciesForWEUR-Team", 
										            ] 
										          + block_list = [] 
										        } 
										      + scopes                      = [] 
										      + tenants                     = (known after apply) 
										      + total_machines              = (known after apply) 
										    } 
										 
										  # citrix_machine_catalog.CreateAzureMCSCatalog will be created 
										  + resource "citrix_machine_catalog" "CreateAzureMCSCatalog" { 
										      + allocation_type          = "Static" 
										      + built_in_scopes          = (known after apply) 
										      + delete_machine_accounts  = "None" 
										      + description              = "Terraform-based Machine Catalog on Azure in WEUR" 
										      + id                       = (known after apply) 
										      + inherited_scopes         = (known after apply) 
										      + minimum_functional_level = "L7_20" 
										      + name                     = "TF-MC-TMM-Azure-Win11" 
										      + persist_user_changes     = (known after apply) 
										      + provisioning_scheme      = { 
										          + azure_machine_config           = { 
										              + machine_profile   = { 
										                  + machine_profile_resource_group = "TMM-TEAM-RG-WEUR" 
										                  + machine_profile_vm_name        = "az-weur-w11-mi-ua-wem-vda-notl-tf" 
										                } 
										              + master_image_note = "" 
										              + prepared_image    = { 
										                  + image_definition = "13e7550c-4da4-4b84-a5d1-c5a7f299fcf4" 
										                  + image_version    = "b7d68084-d8d9-4ec2-94fa-d770ec63b901" 
										                } 
										              + service_offering  = "Standard_D2s_v5" 
										              + storage_type      = "Standard_LRS" 
										              + use_managed_disks = true 
										            } 
										          + hypervisor                     = "9c87db11-446f-4971-ab3d-e41ac41b3b43" 
										          + hypervisor_resource_pool       = "98baa6e9-9bd5-4038-9eba-965dee61f594" 
										          + identity_type                  = "ActiveDirectory" 
										          + machine_account_creation_rules = { 
										              + naming_scheme      = "TF-TMM-TF-W11-#" 
										              + naming_scheme_type = "Numeric" 
										            } 
										          + machine_domain_identity        = { 
										              + domain                   = "wwco.net" 
										              + domain_ou                = "OU=VDA,OU=West EUR,OU=Azure,OU=Cloud,OU=Compute,DC=wwco,DC=net" 
										              + service_account          = "XXXXXXXX@wwco.net" 
										              + service_account_password = (sensitive value) 
										            } 
										          + number_of_total_machines       = 1 
										        } 
										      + provisioning_type        = "MCS" 
										      + scopes                   = [] 
										      + session_support          = "SingleSession" 
										      + tenants                  = (known after apply) 
										      + zone                     = "649eXXXX-XXXX-XXXX-XXXX-XXXXXXXX302e" 
										    } 
										 
										 
										Plan: 2 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:\TMM-Team\DeployCItrixCloudEntitites&gt; terraform apply 
										data.citrix_zone.GetTFAzureZoneID: Reading... 
										data.citrix_image_definition.GetImageDefinition: Reading... 
										data.citrix_zone.GetTFAzureZoneID: Read complete after 0s [id=649e6c0a-14db-481e-bf37-0c259002302e] 
										citrix_azure_hypervisor.CreateAzureHostingConnection: Refreshing state... [id=9c87db11-446f-4971-ab3d-e41ac41b3b43] 
										data.citrix_image_definition.GetImageDefinition: Read complete after 0s [id=13e7550c-4da4-4b84-a5d1-c5a7f299fcf4] 
										data.citrix_image_version.GetImageDefinitionVersion: Reading... 
										time_sleep.wait_30_seconds: Refreshing state... [id=2025-03-07T13:33:31Z] 
										citrix_azure_hypervisor_resource_pool.CreateAzureHostingConnectionPool: Refreshing state... [id=98baa6e9-9bd5-4038-9eba-965dee61f594] 
										time_sleep.wait_30_seconds1: Refreshing state... [id=2025-03-07T13:35:37Z] 
										data.citrix_image_version.GetImageDefinitionVersion: Read complete after 1s [id=b7d68084-d8d9-4ec2-94fa-d770ec63b901] 
										 
										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        = 60 
										          + disconnect_peak_idle_session_after_seconds            = 60 
										          + log_off_off_peak_disconnected_session_after_seconds   = 60 
										          + log_off_peak_disconnected_session_after_seconds       = 60 
										          + log_off_reminder_enabled                              = false 
										          + log_off_reminder_message                              = "" 
										          + log_off_reminder_title                                = "" 
										          + log_off_warning_message                               = "" 
										          + log_off_warning_title                                 = "" 
										          + 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_limit_seconds_to_force_log_off_user          = 0 
										          + off_peak_log_off_action                               = "Nothing" 
										          + off_peak_log_off_reminder_interval                    = 0 
										          + off_peak_log_off_timeout_minutes                      = 0 
										          + peak_autoscale_assigned_power_on_idle_action          = "Nothing" 
										          + peak_autoscale_assigned_power_on_idle_timeout_minutes = 0 
										          + 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_limit_seconds_to_force_log_off_user              = 0 
										          + peak_log_off_action                                   = "Nothing" 
										          + peak_log_off_reminder_interval                        = 0 
										          + peak_log_off_timeout_minutes                          = 0 
										          + power_off_delay_minutes                               = 30 
										          + power_time_schemes                                    = [ 
										              + { 
										                  + days_of_week          = [ 
										                      + "Friday", 
										                      + "Monday", 
										                      + "Thursday", 
										                      + "Tuesday", 
										                      + "Wednesday", 
										                    ] 
										                  + display_name          = "TF-AS-TMM-Azure-Weekday" 
										                  + peak_time_ranges      = [ 
										                      + "09:00-17:00", 
										                    ] 
										                  + pool_size_schedules   = [ 
										                      + { 
										                          + pool_size  = 1 
										                          + time_range = "09:00-17:00" 
										                        }, 
										                    ] 
										                  + pool_using_percentage = false 
										                }, 
										            ] 
										        } 
										      + built_in_scopes             = (known after apply) 
										      + default_desktop_icon        = "1" 
										      + description                 = "" 
										      + desktops                    = [ 
										          + { 
										              + description             = "Terraform-based Delivery Group running on Azure in WEUR" 
										              + enabled                 = true 
										              + published_name          = "TF-DG-TMM-Azure" 
										              + restricted_access_users = { 
										                  + allow_list = [ 
										                      + "WWCO\\PoliciesForWEUR-Team", 
										                    ] 
										                  + block_list = [] 
										                } 
										            }, 
										        ] 
										      + enabled                     = true 
										      + force_delete                = false 
										      + id                          = (known after apply) 
										      + inherited_scopes            = (known after apply) 
										      + minimum_functional_level    = "L7_20" 
										      + name                        = "TF-DG-TMM-Azure-WEUR" 
										      + reboot_schedules            = [ 
										          + { 
										              + days_in_week            = [ 
										                  + "Sunday", 
										                ] 
										              + frequency               = "Weekly" 
										              + frequency_factor        = 1 
										              + ignore_maintenance_mode = true 
										              + name                    = "TMM-GK-TF-RebootScheduleN" 
										              + natural_reboot_schedule = false 
										              + reboot_duration_minutes = 0 
										              + reboot_schedule_enabled = true 
										              + start_date              = "2024-01-01" 
										              + start_time              = "02:00" 
										                # (1 unchanged attribute hidden) 
										            }, 
										        ] 
										      + restricted_access_users     = { 
										          + allow_list = [ 
										              + "WWCO\\PoliciesForWEUR-Team", 
										            ] 
										          + block_list = [] 
										        } 
										      + scopes                      = [] 
										      + tenants                     = (known after apply) 
										      + total_machines              = (known after apply) 
										    } 
										 
										  # citrix_machine_catalog.CreateAzureMCSCatalog will be created 
										  + resource "citrix_machine_catalog" "CreateAzureMCSCatalog" { 
										      + allocation_type          = "Static" 
										      + built_in_scopes          = (known after apply) 
										      + delete_machine_accounts  = "None" 
										      + description              = "Terraform-based Machine Catalog on Azure in WEUR" 
										      + id                       = (known after apply) 
										      + inherited_scopes         = (known after apply) 
										      + minimum_functional_level = "L7_20" 
										      + name                     = "TF-MC-TMM-Azure-Win11" 
										      + persist_user_changes     = (known after apply) 
										      + provisioning_scheme      = { 
										          + azure_machine_config           = { 
										              + machine_profile   = { 
										                  + machine_profile_resource_group = "TMM-TEAM-RG-WEUR" 
										                  + machine_profile_vm_name        = "az-weur-w11-mi-ua-wem-vda-notl-tf" 
										                } 
										              + master_image_note = "" 
										              + prepared_image    = { 
										                  + image_definition = "13e7550c-4da4-4b84-a5d1-c5a7f299fcf4" 
										                  + image_version    = "b7d68084-d8d9-4ec2-94fa-d770ec63b901" 
										                } 
										              + service_offering  = "Standard_D2s_v5" 
										              + storage_type      = "Standard_LRS" 
										              + use_managed_disks = true 
										            } 
										          + hypervisor                     = "9c87db11-446f-4971-ab3d-e41ac41b3b43" 
										          + hypervisor_resource_pool       = "98baa6e9-9bd5-4038-9eba-965dee61f594" 
										          + identity_type                  = "ActiveDirectory" 
										          + machine_account_creation_rules = { 
										              + naming_scheme      = "TF-TMM-TF-W11-#" 
										              + naming_scheme_type = "Numeric" 
										            } 
										          + machine_domain_identity        = { 
										              + domain                   = "wwco.net" 
										              + domain_ou                = "OU=VDA,OU=West EUR,OU=Azure,OU=Cloud,OU=Compute,DC=wwco,DC=net" 
										              + service_account          = "XXXXXXXX@wwco.net" 
										              + service_account_password = (sensitive value) 
										            } 
										          + number_of_total_machines       = 1 
										        } 
										      + provisioning_type        = "MCS" 
										      + scopes                   = [] 
										      + session_support          = "SingleSession" 
										      + tenants                  = (known after apply) 
										      + zone                     = "649e6c0a-14db-481e-bf37-0c259002302e" 
										    } 
										 
										  # time_sleep.wait_30_seconds_2 will be created 
										  + resource "time_sleep" "wait_30_seconds_2" { 
										      + create_duration = "30s" 
										      + id              = (known after apply) 
										    } 
										 
										Plan: 3 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_machine_catalog.CreateAzureMCSCatalog: Creating... 
										citrix_machine_catalog.CreateAzureMCSCatalog: Still creating... [10s elapsed] 
										citrix_machine_catalog.CreateAzureMCSCatalog: Still creating... [20s elapsed] 
										citrix_machine_catalog.CreateAzureMCSCatalog: Still creating... [30s elapsed] 
										... 
										citrix_machine_catalog.CreateAzureMCSCatalog: Still creating... [4m30s elapsed] 
										citrix_machine_catalog.CreateAzureMCSCatalog: Still creating... [4m40s elapsed] 
										citrix_machine_catalog.CreateAzureMCSCatalog: Still creating... [4m50s elapsed] 
										citrix_machine_catalog.CreateAzureMCSCatalog: Creation complete after 4m53s [id=923269ea-f99d-4cfd-b10c-ef37af90c4d2] 
										citrix_delivery_group.CreateDG: Creating... 
										citrix_delivery_group.CreateDG: Still creating... [10s elapsed] 
										citrix_delivery_group.CreateDG: Still creating... [20s elapsed] 
										citrix_delivery_group.CreateDG: Creation complete after 30s [id=64b5e7d5-6bf0-4389-8316-2ce7d1d72dd0] 
										 
										Apply complete! Resources: 2 added, 0 changed, 0 destroyed. 
										 
										PS C:\TMM-Team\DeployCitrixCloudEntitites&gt; 
										 
									 
								
							
						
					
				

				
					The Code Snippet for deploying the Machine Catalog based on an Azure Shared Image is in the Appendix section later in this guide. 
					Terraform successfully installed and configured the Cloud Connectors and the adjacent entities.
				 

				
					Module 7: Create Admin Scopes, Admin Roles, and a default Policy Set
				

				
					In this Module, Terraform creates examples of Admin Scopes, Admin Roles, and a default Policy Set. 
					If you want to assign the Scope and Role while creating the other Cloud entities, you must call this module before creating the Cloud Entities and put these into the Scope.
				 

				
					Every Cloud Entity now has a Scope property where you can assign the created Scope:
				 

				
					
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}					
					
						 
					
				

				
					
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}					
					
						
							
								
									
										
											
												
													
														...
													
												
											

											
												scopes = "${var.CC-GetScopeToBeUsed-Names}"
											

											
												...
											
										
									
								
							
						
					
				

				
					The Policy Set can be assigned only after the creation of the Delivery Group.
				 

				
					This Module is split into the following configuration parts:
				 

				
					
						Creating the Admin Scopes, then Admin Roles, and the Policy Set
					
					
						Assigning the Policy Set to the corresponding filters
					
				

				
					
						IMPORTANT:
					 

					
						Please ensure you have configured the variables according to your needs using the corresponding .auto.tfvars.json file.
					 
				

				
					The configuration can be started by following the standard Terraform workflow:
				 

				
					
						terraform init,
					 

					
						terraform plan
					 
				

				
					and if no errors occur
				 

				
					
						terraform apply
					 
				

				
					 
				 

				
					
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}					
					
						
							
								
									
										PS C:\TMM-Team\DeployCItrixPoliciesAndScopes&gt; terraform init 
										Initializing the backend... 
										Initializing provider plugins... 
										- Finding hashicorp/azuread versions matching "&gt;= 3.0.2"... 
										- Finding latest version of hashicorp/http... 
										- Finding hashicorp/azurerm versions matching "&gt;= 4.6.0"... 
										- Finding citrix/citrix versions matching "1.0.13"... 
										- Installing hashicorp/azuread v3.1.0... 
										- Installed hashicorp/azuread v3.1.0 (signed by HashiCorp) 
										- Installing hashicorp/http v3.4.5... 
										- Installed hashicorp/http v3.4.5 (signed by HashiCorp) 
										- Installing hashicorp/azurerm v4.22.0... 
										- Installed hashicorp/azurerm v4.22.0 (signed by HashiCorp) 
										- Installing citrix/citrix v1.0.13... 
										- Installed citrix/citrix v1.0.13 (self-signed, key ID BD4BD0E690CB7D88) 
										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:\TMM-Team\DeployCItrixPoliciesAndScopes&gt; terraform plan 
										data.citrix_machine_catalog.GetMachineCatalog: Reading... 
										data.citrix_admin_scope.GetDefaultMonitorScope: Reading... 
										data.citrix_delivery_group.GetDeliveryGroup: Reading... 
										data.citrix_admin_scope.GetDefaultMonitorScope: Read complete after 0s [id=00000000-0000-0000-0000-000000000000] 
										data.citrix_machine_catalog.GetMachineCatalog: Read complete after 3s [id=923269ea-f99d-4cfd-b10c-ef37af90c4d2] 
										data.citrix_delivery_group.GetDeliveryGroup: Read complete after 4s [id=64b5e7d5-6bf0-4389-8316-2ce7d1d72dd0] 
										 
										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_admin_role.CreateMonitorRoleExample will be created 
										  + resource "citrix_admin_role" "CreateMonitorRoleExample" { 
										      + can_launch_manage  = false 
										      + can_launch_monitor = true 
										      + description        = "Terraform-created Monitor Role example" 
										      + id                 = (known after apply) 
										      + is_built_in        = (known after apply) 
										      + name               = "TF-Role-Monitor" 
										      + permissions        = [ 
										          + "Applications_Read", 
										          + "Catalog_Read", 
										          + "DesktopGroup_Read", 
										          + "Director_Alerts_Read", 
										          + "Director_ClientDetails_Read", 
										          + "Director_ClientHelpDesk_Read", 
										          + "Director_Dashboard_Read", 
										          + "Director_HelpDesk_Read", 
										          + "Director_MachineDetails_Read", 
										          + "Director_Trends_Read", 
										          + "Director_UserDetails_Read", 
										          + "Hosts_Read", 
										          + "Logging_Read", 
										          + "Policies_Read", 
										          + "Setting_Read", 
										          + "Zone_Read", 
										        ] 
										    } 
										 
										  # citrix_admin_scope.CreateMonitorScopeExample will be created 
										  + resource "citrix_admin_scope" "CreateMonitorScopeExample" { 
										      + description     = "Terraform-created Monitor Scope example" 
										      + id              = (known after apply) 
										      + is_all_scope    = (known after apply) 
										      + is_built_in     = (known after apply) 
										      + is_tenant_scope = false 
										      + name            = "TF-Scope-Monitor" 
										      + tenant_id       = (known after apply) 
										      + tenant_name     = (known after apply) 
										    } 
										 
										  # citrix_policy_set.CreatePolicyExampleSet will be created 
										  + resource "citrix_policy_set" "CreatePolicyExampleSet" { 
										      + assigned        = (known after apply) 
										      + delivery_groups = [ 
										          + "64b5e7d5-6bf0-4389-8316-2ce7d1d72dd0", 
										        ] 
										      + description     = "Terraform-created Policy Set example" 
										      + id              = (known after apply) 
										      + name            = "PolSet-TF-TMM-Default" 
										      + policies        = [ 
										          + { 
										              + access_control_filters      = (known after apply) 
										              + client_ip_filters           = (known after apply) 
										              + client_name_filters         = (known after apply) 
										              + delivery_group_filters      = [ 
										                  + { 
										                      + allowed           = true 
										                      + delivery_group_id = "64b5e7d5-6bf0-4389-8316-2ce7d1d72dd0" 
										                      + enabled           = true 
										                      + id                = (known after apply) 
										                    }, 
										                ] 
										              + delivery_group_type_filters = (known after apply) 
										              + description                 = "Example of Printer-related policy set" 
										              + enabled                     = true 
										              + id                          = (known after apply) 
										              + name                        = "Printing" 
										              + ou_filters                  = (known after apply) 
										              + policy_settings             = [ 
										                  + { 
										                      + name        = "ClientPrinterAutoCreation" 
										                      + use_default = false 
										                      + value       = "DefaultPrinterOnly" 
										                    }, 
										                  + { 
										                      + name        = "UniversalPrintDriverUsage" 
										                      + use_default = false 
										                      + value       = "FallbackToSpecific" 
										                    }, 
										                ] 
										              + tag_filters                 = (known after apply) 
										              + user_filters                = [ 
										                  + { 
										                      + allowed = true 
										                      + enabled = true 
										                      + id      = (known after apply) 
										                      + sid     = "S-1-5-21-792391193-2421966673-1404948739-19612" 
										                    }, 
										                ] 
										            }, 
										          + { 
										              + access_control_filters      = (known after apply) 
										              + client_ip_filters           = [ 
										                  + { 
										                      + allowed    = true 
										                      + enabled    = true 
										                      + id         = (known after apply) 
										                      + ip_address = "10.53.16.0" 
										                    }, 
										                ] 
										              + client_name_filters         = (known after apply) 
										              + delivery_group_filters      = [ 
										                  + { 
										                      + allowed           = true 
										                      + delivery_group_id = "64b5e7d5-6bf0-4389-8316-2ce7d1d72dd0" 
										                      + enabled           = true 
										                      + id                = (known after apply) 
										                    }, 
										                ] 
										              + delivery_group_type_filters = (known after apply) 
										              + description                 = "Example of HDX Graphics-related policy set" 
										              + enabled                     = true 
										              + id                          = (known after apply) 
										              + name                        = "HDX Graphics" 
										              + ou_filters                  = (known after apply) 
										              + policy_settings             = [ 
										                  + { 
										                      + enabled     = true 
										                      + name        = "AllowVisuallyLosslessCompression" 
										                      + use_default = false 
										                    }, 
										                  + { 
										                      + enabled     = true 
										                      + name        = "UseHardwareEncodingForVideoCodec" 
										                      + use_default = false 
										                    }, 
										                  + { 
										                      + name        = "UseVideoCodecForCompression" 
										                      + use_default = false 
										                      + value       = "UseVideoCodecIfPreferred" 
										                    }, 
										                ] 
										              + tag_filters                 = (known after apply) 
										              + user_filters                = [ 
										                  + { 
										                      + allowed = true 
										                      + enabled = true 
										                      + id      = (known after apply) 
										                      + sid     = "S-1-5-21-792391193-2421966673-1404948739-19612" 
										                    }, 
										                ] 
										            }, 
										          + { 
										              + access_control_filters      = (known after apply) 
										              + client_ip_filters           = [ 
										                  + { 
										                      + allowed    = true 
										                      + enabled    = true 
										                      + id         = (known after apply) 
										                      + ip_address = "10.53.16.0" 
										                    }, 
										                ] 
										              + client_name_filters         = (known after apply) 
										              + delivery_group_filters      = [ 
										                  + { 
										                      + allowed           = true 
										                      + delivery_group_id = "64b5e7d5-6bf0-4389-8316-2ce7d1d72dd0" 
										                      + enabled           = true 
										                      + id                = (known after apply) 
										                    }, 
										                ] 
										              + delivery_group_type_filters = (known after apply) 
										              + description                 = "Example of Client Drive-related policy set" 
										              + enabled                     = true 
										              + id                          = (known after apply) 
										              + name                        = "Client Drives" 
										              + ou_filters                  = (known after apply) 
										              + policy_settings             = [ 
										                  + { 
										                      + enabled     = false 
										                      + name        = "ClientFloppyDrives" 
										                      + use_default = false 
										                    }, 
										                  + { 
										                      + enabled     = false 
										                      + name        = "ClientNetworkDrives" 
										                      + use_default = false 
										                    }, 
										                  + { 
										                      + enabled     = false 
										                      + name        = "ClientOpticalDrives" 
										                      + use_default = false 
										                    }, 
										                  + { 
										                      + enabled     = true 
										                      + name        = "AutoConnectDrives" 
										                      + use_default = false 
										                    }, 
										                  + { 
										                      + enabled     = true 
										                      + name        = "ClientDriveRedirection" 
										                      + use_default = false 
										                    }, 
										                  + { 
										                      + enabled     = true 
										                      + name        = "ClientFixedDrives" 
										                      + use_default = false 
										                    }, 
										                ] 
										              + tag_filters                 = (known after apply) 
										              + user_filters                = [ 
										                  + { 
										                      + allowed = true 
										                      + enabled = true 
										                      + id      = (known after apply) 
										                      + sid     = "S-1-5-21-792391193-2421966673-1404948739-19612" 
										                    }, 
										                ] 
										            }, 
										        ] 
										      + scopes          = [] 
										      + type            = "DeliveryGroupPolicies" 
										    } 
										 
										Plan: 3 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:\TMM-Team\DeployCItrixPoliciesAndScopes&gt; terraform apply 
										data.citrix_admin_scope.GetDefaultMonitorScope: Reading... 
										data.citrix_delivery_group.GetDeliveryGroup: Reading... 
										data.citrix_machine_catalog.GetMachineCatalog: Reading... 
										data.citrix_admin_scope.GetDefaultMonitorScope: Read complete after 1s [id=00000000-0000-0000-0000-000000000000] 
										data.citrix_delivery_group.GetDeliveryGroup: Read complete after 1s [id=64b5e7d5-6bf0-4389-8316-2ce7d1d72dd0] 
										data.citrix_machine_catalog.GetMachineCatalog: Read complete after 2s [id=923269ea-f99d-4cfd-b10c-ef37af90c4d2] 
										 
										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_admin_role.CreateMonitorRoleExample will be created 
										  + resource "citrix_admin_role" "CreateMonitorRoleExample" { 
										      + can_launch_manage  = false 
										      + can_launch_monitor = true 
										      + description        = "Terraform-created Monitor Role example" 
										      + id                 = (known after apply) 
										      + is_built_in        = (known after apply) 
										      + name               = "TF-Role-Monitor" 
										      + permissions        = [ 
										          + "Applications_Read", 
										          + "Catalog_Read", 
										          + "DesktopGroup_Read", 
										          + "Director_Alerts_Read", 
										          + "Director_ClientDetails_Read", 
										          + "Director_ClientHelpDesk_Read", 
										          + "Director_Dashboard_Read", 
										          + "Director_HelpDesk_Read", 
										          + "Director_MachineDetails_Read", 
										          + "Director_Trends_Read", 
										          + "Director_UserDetails_Read", 
										          + "Hosts_Read", 
										          + "Logging_Read", 
										          + "Policies_Read", 
										          + "Setting_Read", 
										          + "Zone_Read", 
										        ] 
										    } 
										 
										  # citrix_admin_scope.CreateMonitorScopeExample will be created 
										  + resource "citrix_admin_scope" "CreateMonitorScopeExample" { 
										      + description     = "Terraform-created Monitor Scope example" 
										      + id              = (known after apply) 
										      + is_all_scope    = (known after apply) 
										      + is_built_in     = (known after apply) 
										      + is_tenant_scope = false 
										      + name            = "TF-Scope-Monitor" 
										      + tenant_id       = (known after apply) 
										      + tenant_name     = (known after apply) 
										    } 
										 
										  # citrix_policy_set.CreatePolicyExampleSet will be created 
										  + resource "citrix_policy_set" "CreatePolicyExampleSet" { 
										      + assigned        = (known after apply) 
										      + delivery_groups = [ 
										          + "64b5e7d5-6bf0-4389-8316-2ce7d1d72dd0", 
										        ] 
										      + description     = "Terraform-created Policy Set example" 
										      + id              = (known after apply) 
										      + name            = "PolSet-TF-TMM-Default" 
										      + policies        = [ 
										          + { 
										              + access_control_filters      = (known after apply) 
										              + client_ip_filters           = (known after apply) 
										              + client_name_filters         = (known after apply) 
										              + delivery_group_filters      = [ 
										                  + { 
										                      + allowed           = true 
										                      + delivery_group_id = "64b5e7d5-6bf0-4389-8316-2ce7d1d72dd0" 
										                      + enabled           = true 
										                      + id                = (known after apply) 
										                    }, 
										                ] 
										              + delivery_group_type_filters = (known after apply) 
										              + description                 = "Example of Printer-related policy set" 
										              + enabled                     = true 
										              + id                          = (known after apply) 
										              + name                        = "Printing" 
										              + ou_filters                  = (known after apply) 
										              + policy_settings             = [ 
										                  + { 
										                      + name        = "ClientPrinterAutoCreation" 
										                      + use_default = false 
										                      + value       = "DefaultPrinterOnly" 
										                    }, 
										                  + { 
										                      + name        = "UniversalPrintDriverUsage" 
										                      + use_default = false 
										                      + value       = "FallbackToSpecific" 
										                    }, 
										                ] 
										              + tag_filters                 = (known after apply) 
										              + user_filters                = [ 
										                  + { 
										                      + allowed = true 
										                      + enabled = true 
										                      + id      = (known after apply) 
										                      + sid     = "S-1-5-21-792391193-2421966673-1404948739-19612" 
										                    }, 
										                ] 
										            }, 
										          + { 
										              + access_control_filters      = (known after apply) 
										              + client_ip_filters           = [ 
										                  + { 
										                      + allowed    = true 
										                      + enabled    = true 
										                      + id         = (known after apply) 
										                      + ip_address = "10.53.16.0" 
										                    }, 
										                ] 
										              + client_name_filters         = (known after apply) 
										              + delivery_group_filters      = [ 
										                  + { 
										                      + allowed           = true 
										                      + delivery_group_id = "64b5e7d5-6bf0-4389-8316-2ce7d1d72dd0" 
										                      + enabled           = true 
										                      + id                = (known after apply) 
										                    }, 
										                ] 
										              + delivery_group_type_filters = (known after apply) 
										              + description                 = "Example of HDX Graphics-related policy set" 
										              + enabled                     = true 
										              + id                          = (known after apply) 
										              + name                        = "HDX Graphics" 
										              + ou_filters                  = (known after apply) 
										              + policy_settings             = [ 
										                  + { 
										                      + enabled     = true 
										                      + name        = "AllowVisuallyLosslessCompression" 
										                      + use_default = false 
										                    }, 
										                  + { 
										                      + enabled     = true 
										                      + name        = "UseHardwareEncodingForVideoCodec" 
										                      + use_default = false 
										                    }, 
										                  + { 
										                      + name        = "UseVideoCodecForCompression" 
										                      + use_default = false 
										                      + value       = "UseVideoCodecIfPreferred" 
										                    }, 
										                ] 
										              + tag_filters                 = (known after apply) 
										              + user_filters                = [ 
										                  + { 
										                      + allowed = true 
										                      + enabled = true 
										                      + id      = (known after apply) 
										                      + sid     = "S-1-5-21-792391193-2421966673-1404948739-19612" 
										                    }, 
										                ] 
										            }, 
										          + { 
										              + access_control_filters      = (known after apply) 
										              + client_ip_filters           = [ 
										                  + { 
										                      + allowed    = true 
										                      + enabled    = true 
										                      + id         = (known after apply) 
										                      + ip_address = "10.53.16.0" 
										                    }, 
										                ] 
										              + client_name_filters         = (known after apply) 
										              + delivery_group_filters      = [ 
										                  + { 
										                      + allowed           = true 
										                      + delivery_group_id = "64b5e7d5-6bf0-4389-8316-2ce7d1d72dd0" 
										                      + enabled           = true 
										                      + id                = (known after apply) 
										                    }, 
										                ] 
										              + delivery_group_type_filters = (known after apply) 
										              + description                 = "Example of Client Drive-related policy set" 
										              + enabled                     = true 
										              + id                          = (known after apply) 
										              + name                        = "Client Drives" 
										              + ou_filters                  = (known after apply) 
										              + policy_settings             = [ 
										                  + { 
										                      + enabled     = false 
										                      + name        = "ClientFloppyDrives" 
										                      + use_default = false 
										                    }, 
										                  + { 
										                      + enabled     = false 
										                      + name        = "ClientNetworkDrives" 
										                      + use_default = false 
										                    }, 
										                  + { 
										                      + enabled     = false 
										                      + name        = "ClientOpticalDrives" 
										                      + use_default = false 
										                    }, 
										                  + { 
										                      + enabled     = true 
										                      + name        = "AutoConnectDrives" 
										                      + use_default = false 
										                    }, 
										                  + { 
										                      + enabled     = true 
										                      + name        = "ClientDriveRedirection" 
										                      + use_default = false 
										                    }, 
										                  + { 
										                      + enabled     = true 
										                      + name        = "ClientFixedDrives" 
										                      + use_default = false 
										                    }, 
										                ] 
										              + tag_filters                 = (known after apply) 
										              + user_filters                = [ 
										                  + { 
										                      + allowed = true 
										                      + enabled = true 
										                      + id      = (known after apply) 
										                      + sid     = "S-1-5-21-792391193-2421966673-1404948739-19612" 
										                    }, 
										                ] 
										            }, 
										        ] 
										      + scopes          = [] 
										      + type            = "DeliveryGroupPolicies" 
										    } 
										 
										Plan: 3 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_admin_scope.CreateMonitorScopeExample: Creating... 
										citrix_policy_set.CreatePolicyExampleSet: Creating... 
										citrix_admin_role.CreateMonitorRoleExample: Creating... 
										citrix_admin_role.CreateMonitorRoleExample: Creation complete after 1s [id=3d28e166-c727-42b5-8edc-75894db31bce] 
										citrix_admin_scope.CreateMonitorScopeExample: Still creating... [10s elapsed] 
										citrix_policy_set.CreatePolicyExampleSet: Still creating... [10s elapsed] 
										citrix_admin_scope.CreateMonitorScopeExample: Creation complete after 11s [id=7552997b-7fc4-47f9-84e6-fc0d3ebb16e0] 
										citrix_policy_set.CreatePolicyExampleSet: Still creating... [20s elapsed] 
										citrix_policy_set.CreatePolicyExampleSet: Still creating... [30s elapsed] 
										citrix_policy_set.CreatePolicyExampleSet: Still creating... [40s elapsed] 
										citrix_policy_set.CreatePolicyExampleSet: Still creating... [50s elapsed] 
										citrix_policy_set.CreatePolicyExampleSet: Still creating... [1m0s elapsed] 
										citrix_policy_set.CreatePolicyExampleSet: Creation complete after 1m5s [id=52cddb76-7f38-4f0e-ac02-e4176bd7dbac] 
										 
										Apply complete! Resources: 3 added, 0 changed, 0 destroyed. 
										 
										 
										PS C:\TMM-Team\DeployCItrixPoliciesAndScopes&gt;
									 
								
							
						
					
				

				
					Terraform successfully installed and configured the Admin Scopes, the Admin Roles, and the Policy Set.
				 

				
					Module 8: Configure Citrix Workspace App and Citrix Enterprise Browser using the Global App Configuration Service (GACS)
				

				
					Terraform deploys sample configurations in this Module for the Citrix Workspace App and the Citrix enterprise Browser. 
					Therefore, Terraform alters the adjacent settings In the Global App Configuration Service. 
				 

				
					Before running the Terraform script, no settings are configured: 
					
				 

				
					 
				 

				
					
						Note:
					 

					
						The Global App Configuration Service offers many settings - you can find an overview in the Appendix section or on Github. 
						 
					 
				

				
					 
				 

				
				

				
					
						IMPORTANT:
					 

					
						Please ensure you have configured the variables according to your needs using the corresponding .auto.tfvars.json file.
					 
				

				
					The configuration can be started by following the standard Terraform workflow:
				 

				
					
						terraform init,
					 

					
						terraform plan
					 
				

				
					and if no errors occur
				 

				
					
						terraform apply
					 
				

				
					 
				 

				
					
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}					
					
						
							
								
									
										PS C:\TMM-Team\ConfiguringCWAAndCEBUsingGACS&gt; terraform init 
										Initializing the backend... 
										Initializing provider plugins... 
										- Finding hashicorp/azuread versions matching "&gt;= 3.0.2"... 
										- Finding latest version of hashicorp/http... 
										- Finding hashicorp/azurerm versions matching "&gt;= 4.6.0"... 
										- Finding citrix/citrix versions matching "1.0.13"... 
										- Installing hashicorp/azurerm v4.23.0... 
										- Installed hashicorp/azurerm v4.23.0 (signed by HashiCorp) 
										- Installing citrix/citrix v1.0.13... 
										- Installed citrix/citrix v1.0.13 (self-signed, key ID BD4BD0E690CB7D88) 
										- Installing hashicorp/azuread v3.1.0... 
										- Installed hashicorp/azuread v3.1.0 (signed by HashiCorp) 
										- Installing hashicorp/http v3.4.5... 
										- Installed hashicorp/http v3.4.5 (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:\TMM-Team\ConfiguringCWAAndCEBUsingGACS&gt; terraform plan 
										 
										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_gac_settings.TMM-TF-GACS-Settings will be created 
										  + resource "citrix_gac_settings" "TMM-TF-GACS-Settings" { 
										      + app_settings       = { 
										          + android = [ 
										              + { 
										                  + category      = "advanced" 
										                  + settings      = [ 
										                      + { 
										                          + name         = "Enable clipboard" 
										                          + value_string = "true" 
										                        }, 
										                    ] 
										                  + user_override = false 
										                }, 
										            ] 
										          + html5   = [ 
										              + { 
										                  + category      = "toolbar" 
										                  + settings      = [ 
										                      + { 
										                          + name         = "Clipboard Option in Toolbar" 
										                          + value_string = "true" 
										                        }, 
										                      + { 
										                          + name         = "USB Devices Option in Toolbar" 
										                          + value_string = "true" 
										                        }, 
										                    ] 
										                  + user_override = false 
										                }, 
										              + { 
										                  + category      = "virtual channel" 
										                  + settings      = [ 
										                      + { 
										                          + name         = "Clipboard Operations Between VDA And Local Device" 
										                          + value_string = "true" 
										                        }, 
										                    ] 
										                  + user_override = false 
										                }, 
										            ] 
										          + ios     = [ 
										              + { 
										                  + category      = "advanced" 
										                  + settings      = [ 
										                      + { 
										                          + name         = "Fullscreen Window" 
										                          + value_string = "true" 
										                        }, 
										                      + { 
										                          + name         = "Touch Enable" 
										                          + value_string = "true" 
										                        }, 
										                    ] 
										                  + user_override = false 
										                }, 
										            ] 
										          + linux   = [ 
										              + { 
										                  + category      = "browser" 
										                  + settings      = [ 
										                      + { 
										                          + name         = "Enable Password Save" 
										                          + value_string = "true" 
										                        }, 
										                      + { 
										                          + name         = "Incognito Mode Availability" 
										                          + value_string = "Incognito mode available" 
										                        }, 
										                    ] 
										                  + user_override = false 
										                }, 
										            ] 
										          + macos   = [ 
										              + { 
										                  + category      = "browser" 
										                  + settings      = [ 
										                      + { 
										                          + name         = "Enable Citrix Enterprise Browser shortcut" 
										                          + value_string = "true" 
										                        }, 
										                      + { 
										                          + name         = "Enable Password Save" 
										                          + value_string = "true" 
										                        }, 
										                    ] 
										                  + user_override = false 
										                }, 
										            ] 
										          + windows = [ 
										              + { 
										                  + category      = "browser" 
										                  + settings      = [ 
										                      + { 
										                          + name         = "Incognito Mode Availability" 
										                          + value_string = "Incognito mode available" 
										                        }, 
										                      + { 
										                          + name         = "saving browser history disabled" 
										                          + value_string = "true" 
										                        }, 
										                    ] 
										                  + user_override = false 
										                }, 
										              + { 
										                  + category      = "dazzle" 
										                  + settings      = [ 
										                      + { 
										                          + name         = "Put shortcuts on desktop" 
										                          + value_string = "true" 
										                        }, 
										                    ] 
										                  + user_override = false 
										                }, 
										              + { 
										                  + category      = "ica client" 
										                  + settings      = [ 
										                      + { 
										                          + name         = "Allow Client Clipboard Redirection" 
										                          + value_string = "true" 
										                        }, 
										                    ] 
										                  + user_override = false 
										                }, 
										              + { 
										                  + category      = "root" 
										                  + settings      = [ 
										                      + { 
										                          + name         = "Hide advanced preferences" 
										                          + value_string = "true" 
										                        }, 
										                      + { 
										                          + name         = "Hide connection center" 
										                          + value_string = "true" 
										                        }, 
										                    ] 
										                  + user_override = false 
										                }, 
										            ] 
										        } 
										      + description        = "Terraform-created Global App Configuration Settings for TMM-Team" 
										      + name               = "GACS-TMM-GK-TF-Settings" 
										      + service_url        = "https://XXXXXXXX.cloud.com:443" 
										      + test_channel       = true 
										      + use_for_app_config = true 
										    } 
										 
										Plan: 1 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:\TMM-Team\ConfiguringCWAAndCEBUsingGACS&gt; terraform apply 
										 
										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_gac_settings.TMM-TF-GACS-Settings will be created 
										  + resource "citrix_gac_settings" "TMM-TF-GACS-Settings" { 
										      + app_settings       = { 
										          + android = [ 
										              + { 
										                  + category      = "advanced" 
										                  + settings      = [ 
										                      + { 
										                          + name         = "Enable clipboard" 
										                          + value_string = "true" 
										                        }, 
										                    ] 
										                  + user_override = false 
										                }, 
										            ] 
										          + html5   = [ 
										              + { 
										                  + category      = "toolbar" 
										                  + settings      = [ 
										                      + { 
										                          + name         = "Clipboard Option in Toolbar" 
										                          + value_string = "true" 
										                        }, 
										                      + { 
										                          + name         = "USB Devices Option in Toolbar" 
										                          + value_string = "true" 
										                        }, 
										                    ] 
										                  + user_override = false 
										                }, 
										              + { 
										                  + category      = "virtual channel" 
										                  + settings      = [ 
										                      + { 
										                          + name         = "Clipboard Operations Between VDA And Local Device" 
										                          + value_string = "true" 
										                        }, 
										                    ] 
										                  + user_override = false 
										                }, 
										            ] 
										          + ios     = [ 
										              + { 
										                  + category      = "advanced" 
										                  + settings      = [ 
										                      + { 
										                          + name         = "Fullscreen Window" 
										                          + value_string = "true" 
										                        }, 
										                      + { 
										                          + name         = "Touch Enable" 
										                          + value_string = "true" 
										                        }, 
										                    ] 
										                  + user_override = false 
										                }, 
										            ] 
										          + linux   = [ 
										              + { 
										                  + category      = "browser" 
										                  + settings      = [ 
										                      + { 
										                          + name         = "Enable Password Save" 
										                          + value_string = "true" 
										                        }, 
										                      + { 
										                          + name         = "Incognito Mode Availability" 
										                          + value_string = "Incognito mode available" 
										                        }, 
										                    ] 
										                  + user_override = false 
										                }, 
										            ] 
										          + macos   = [ 
										              + { 
										                  + category      = "browser" 
										                  + settings      = [ 
										                      + { 
										                          + name         = "Enable Citrix Enterprise Browser shortcut" 
										                          + value_string = "true" 
										                        }, 
										                      + { 
										                          + name         = "Enable Password Save" 
										                          + value_string = "true" 
										                        }, 
										                    ] 
										                  + user_override = false 
										                }, 
										            ] 
										          + windows = [ 
										              + { 
										                  + category      = "browser" 
										                  + settings      = [ 
										                      + { 
										                          + name         = "Incognito Mode Availability" 
										                          + value_string = "Incognito mode available" 
										                        }, 
										                      + { 
										                          + name         = "saving browser history disabled" 
										                          + value_string = "true" 
										                        }, 
										                    ] 
										                  + user_override = false 
										                }, 
										              + { 
										                  + category      = "dazzle" 
										                  + settings      = [ 
										                      + { 
										                          + name         = "Put shortcuts on desktop" 
										                          + value_string = "true" 
										                        }, 
										                    ] 
										                  + user_override = false 
										                }, 
										              + { 
										                  + category      = "ica client" 
										                  + settings      = [ 
										                      + { 
										                          + name         = "Allow Client Clipboard Redirection" 
										                          + value_string = "true" 
										                        }, 
										                    ] 
										                  + user_override = false 
										                }, 
										              + { 
										                  + category      = "root" 
										                  + settings      = [ 
										                      + { 
										                          + name         = "Hide advanced preferences" 
										                          + value_string = "true" 
										                        }, 
										                      + { 
										                          + name         = "Hide connection center" 
										                          + value_string = "true" 
										                        }, 
										                    ] 
										                  + user_override = false 
										                }, 
										            ] 
										        } 
										      + description        = "Terraform-created Global App Configuration Settings for TMM-Team" 
										      + name               = "GACS-TMM-GK-TF-Settings" 
										      + service_url        = "https://XXXXX.cloud.com:443" 
										      + test_channel       = true 
										      + use_for_app_config = true 
										    } 
										 
										Plan: 1 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_gac_settings.TMM-TF-GACS-Settings: Creating... 
										citrix_gac_settings.TMM-TF-GACS-Settings: Creation complete after 2s [name=GACS-TMM-GK-TF-Settings] 
										 
										Apply complete! Resources: 1 added, 0 changed, 0 destroyed. 
										PS C:\TMM-Team\ConfiguringCWAAndCEBUsingGACS&gt;
									 
								
							
						
					
				

				
					Terraform has successfully altered configuration settings for the Citrix Workspace App using the Global App Configuration Service: 
					
				 

				
					The automated deployment of Citrix DaaS on Microsoft Azure Is now complete.
				 

				
					This concludes our Guide "Using Citrix Automation with Terraform and Ansible to deploy Citrix DaaS on Microsoft Azure (2025 Update)".
				 

				
					Appendix
				

				
					
						Disclaimer
					 

					
						EXCEPT WHERE EXPRESSLY PROVIDED OTHERWISE BY CLOUD SOFTWARE GROUP, THE CONTENT IN THIS DOCUMENT IS PROVIDED “AS IS” AND CLOUD SOFTWARE GROUP HEREBY DISCLAIMS ALL EXPRESS OR IMPLIED REPRESENTATIONS, WARRANTIES, GUARANTIES, AND CONDITIONS, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES OR CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD TO BE LEGALLY INVALID. CLOUD SOFTWARE GROUP MAKES NO REPRESENTATIONS, WARRANTIES, GUARANTIES, OR CONDITIONS AS TO THE QUALITY, SUITABILITY, TRUTH, ACCURACY, OR COMPLETENESS OF ANY OF THE CONTENT CONTAINED ON THE WEBSITE.
					 

					
						The above disclaimer applies to any damages, liability, or injuries caused by any failure of performance, error, omission, interruption, deletion, defect, delay in operation or transmission, computer virus, communication line failure, theft or destruction of or unauthorized access to, alteration of, or use, whether for breach of contract, tort, negligence or any other cause of action. 
						 
					 
				

				
					Module 1: Create the Resource Group (optional)
				

				
					These are the Terraform configuration files for Module 1 (excerpts):
				 

				
					provider.tf
				

				
					
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}					
					
						
							
								
									
										
											# Create a Citrix Cloud Resource Location and DaaS Deployment on Microsoft Azure V2
										 

										
											## Definition of all required Terraform providers
										 

										
											terraform {
										 

										
											    required_version = "&gt;= 1.9.7"
										 

										
											 
										 

										
											  required_providers {
										 

										
											    azurerm = {
										 

										
											      source  = "hashicorp/azurerm"
										 

										
											      version = "&gt;=4.6.0"
										 

										
											    }
										 

										
											 
										 

										
											    citrix = {
										 

										
											      source  = "citrix/citrix"
										 

										
											      version = "&gt;=1.0.13"
										 

										
											    }
										 

										
											  }
										 

										
											} 
										 

										
											 
										 

										
											## Configure all Providers
										 

										
											provider "azurerm" {
										 

										
											  features {}
										 

										
											  client_id       = "${var.azurerm-clientid}"
										 

										
											  client_secret   = "${var.azurerm-clientsecret}"
										 

										
											  tenant_id       = "${var.azurerm-tenantid}"
										 

										
											  subscription_id = "${var.azurerm-subscriptionid}"
										 

										
											}
										 

										
											 
										 

										
											provider "citrix" {
										 

										
											    cvad_config = {
										 

										
											                     customer_id   = "${var.cc-customerid}"  
										 

										
											                     client_id     = "${var.cc-apikey-clientId}"  
										 

										
											                     client_secret = "${var.cc-apikey-clientSecret}"  
										 

										
											                  }
										 

										
											}
										 
									
								
							
						

						
							 
						 

						
							CreateRG.tf
						

						
							
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}							
							
								
									
										
											
												
													# Create a Citrix Cloud Resource Location and DaaS Deployment on Microsoft Azure V2
												 

												
													## Create Pre-requisites
												 

												
													### Create a Resource Group on Azure
												 

												
													#### https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/resource_group
												 

												
													resource "azurerm_resource_group" "TACG-AZ-V2-RG" {
												 

												
													  count = var.CreateRG ? 1 : 0
												 

												
													  location = "${var.RG-DeployLocation}"
												 

												
													  name     = "${var.RG-Name}"
												 

												
													 
												 

												
													  tags                           = {
												 

												
													                                        Environment = "${var.TAG-Environment}",
												 

												
													                                        Environment-Entity = "${var.TAG-Environment-Entity}",
												 

												
													                                        Environment-Usage = "${var.TAG-Environment-Usage}",
												 

												
													  }
												 

												
													}
												 
											
										
									
								
							
						

						
							 
						 

						
							Module 2: Create the Shared Image Gallery, the Image Definition, and the Image Version
						

						
							These are the Terraform configuration files for Module 2 (excerpts):
						 

						
							CreateSIG.tf
						

						
							
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}							
							
								
									
										
											
												
													# Create a Citrix Cloud Resource Location and DaaS Deployment on Microsoft Azure V2
												 

												
													## Create Pre-requisites
												 

												
													### Create a Shared Image Gallery
												 

												
													#### https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/shared_image_gallery
												 

												
													 
												 

												
													data "azurerm_resource_group" "TMM-RG" {
												 

												
													  name                = "${var.RG-Name}"
												 

												
													}
												 

												
													 
												 

												
													resource "azurerm_shared_image_gallery" "TACG-AZ-V2-SIG" {
												 

												
													  name                = "${var.SIG-Name}"
												 

												
													  description         = "${var.SIG-Description}"
												 

												
													  resource_group_name = data.azurerm_resource_group.TMM-RG.name
												 

												
													  location            = data.azurerm_resource_group.TMM-RG.location
												 

												
													  tags                           = {
												 

												
													                                        Environment = "${var.TAG-Environment}",
												 

												
													                                        Environment-Entity = "${var.TAG-Environment-Entity}",
												 

												
													                                        Environment-Usage = "${var.TAG-Environment-Usage}",
												 

												
													  }
												 

												
													}
												 

												
													 
												 

												
													### Create an Image Definition for the Master Image
												 

												
													#### https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/shared_image
												 

												
													resource "azurerm_shared_image" "TACG-AZ-V2-SIG-IDW2K25M" {
												 

												
													  depends_on = [ azurerm_shared_image_gallery.TACG-AZ-V2-SIG ]
												 

												
													  name                = "${var.SIG-IDW2K25M-Name}"
												 

												
													  gallery_name        = azurerm_shared_image_gallery.TACG-AZ-V2-SIG.name
												 

												
													  resource_group_name = data.azurerm_resource_group.TMM-RG.name
												 

												
													  location            = data.azurerm_resource_group.TMM-RG.location
												 

												
													  os_type             = "${var.SIG-IDW2K25M-OsType}"
												 

												
													  hyper_v_generation  = "V2" 
												 

												
													  hibernation_enabled = true
												 

												
													  accelerated_network_support_enabled = true
												 

												
													  trusted_launch_enabled = false
												 

												
													  
												 

												
													  identifier {
												 

												
													    publisher = "${var.SIG-IDW2K25M-Publisher}"
												 

												
													    offer     = "${var.SIG-IDW2K25M-Offer}"
												 

												
													    sku       = "${var.SIG-IDW2K25M-SKU}"
												 

												
													  }
												 

												
													 
												 

												
													  tags                           = {
												 

												
													                                        Environment = "${var.TAG-Environment}",
												 

												
													                                        Environment-Entity = "${var.TAG-Environment-Entity}",
												 

												
													                                        Environment-Usage = "${var.TAG-Environment-Usage}",
												 

												
													  }
												 

												
													}
												 

												
													 
												 

												
													resource "azurerm_shared_image_version" "TACG-AZ-V2-SIG-IDW2K25M-ImgVersion" {
												 

												
													  depends_on = [ azurerm_shared_image.TACG-AZ-V2-SIG-IDW2K25M ]
												 

												
													  name                = "${var.SIG-IDW2K25M-Version}"
												 

												
													  gallery_name        = azurerm_shared_image_gallery.TACG-AZ-V2-SIG.name
												 

												
													  image_name          = "${var.SIG-IDW2K25M-Name}"
												 

												
													  resource_group_name = data.azurerm_resource_group.TMM-RG.name
												 

												
													  location            = data.azurerm_resource_group.TMM-RG.location
												 

												
													  managed_image_id    = "${var.SIG-IDW2K25M-ID}"
												 

												
													 
												 

												
													  target_region {
												 

												
													    name                   = data.azurerm_resource_group.TMM-RG.location
												 

												
													    regional_replica_count = 1
												 

												
													    storage_account_type   = "Standard_LRS"
												 

												
													  }
												 

												
													 
												 

												
													  tags                           = {
												 

												
													                                        Environment = "${var.TAG-Environment}",
												 

												
													                                        Environment-Entity = "${var.TAG-Environment-Entity}",
												 

												
													                                        Environment-Usage = "${var.TAG-Environment-Usage}",
												 

												
													  }
												 

												
													}
												 

												
													 
												 
											
										
									
								
							
						

						
							 
						 

						
							Module 3: Create the 2 Cloud Connector Virtual Machines
						 

						
							These are the Terraform configuration files for Module 3 (excerpts):
						 

						
							CreateCCVMs.tf
						

						
							 
						 

						
							
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}							
							
								
									
										
											
												
													# Create a Citrix Cloud Resource Location and DaaS Deployment on Microsoft Azure V2
												 

												
													## Create Pre-requisites
												 

												
													### We are using existing entitites like the Resource Group, NSGs, Firewall Rules, etc
												 

												
													#### Get existing entity data
												 

												
													data "azurerm_virtual_network" "TACG-TMM-VNet" {
												 

												
													  name                = "${var.TACG-TMM-VNet-Name}"
												 

												
													  resource_group_name = "${var.TACG-TMM-ResourceGroup-Name}"
												 

												
													}
												 

												
													 
												 

												
													data "azurerm_subnet" "TACG-TMM-Subnet" {
												 

												
													  name                 = "${var.TACG-TMM-Subnet-Name}"
												 

												
													  virtual_network_name = data.azurerm_virtual_network.TACG-TMM-VNet.name
												 

												
													  resource_group_name = "${var.TACG-TMM-ResourceGroup-Name}"
												 

												
													}
												 

												
													 
												 

												
													data "azurerm_network_security_group" "TACG-TMM-NSG" {
												 

												
													  name                 = "${var.TACG-TMM-NSG-Name}"
												 

												
													  resource_group_name = "${var.TACG-TMM-ResourceGroup-Name}"
												 

												
													}
												 

												
													#### Create a new inbound rule in NSG to allow WinRM Communication
												 

												
													resource "azurerm_network_security_rule" "EnableWinRMInNSG" {
												 

												
													  depends_on = [ data.azurerm_network_security_group.TACG-TMM-NSG ]
												 

												
													   count = var.TACG-TMM-NSGRule-CreateRule ? 1 : 0
												 

												
													  name                        = "${var.TACG-TMM-NSGRule-Name}"
												 

												
													  priority                    = "${var.TACG-TMM-NSGRule-Priority}"
												 

												
													  direction                   = "Inbound"
												 

												
													  access                      = "Allow"
												 

												
													  protocol                    = "Tcp"
												 

												
													  source_port_range           = "*"
												 

												
													  destination_port_range      = "${var.TACG-TMM-NSGRule-InboundPorts}"
												 

												
													  source_address_prefix       = "VirtualNetwork"
												 

												
													  destination_address_prefix  = "VirtualNetwork"
												 

												
													  resource_group_name         = "${var.TACG-TMM-ResourceGroup-Name}"
												 

												
													  network_security_group_name = data.azurerm_network_security_group.TACG-TMM-NSG.name
												 

												
													}
												 

												
													 
												 

												
													#### Create a dedicated Network Interface and IP configuration for the CC1-VM
												 

												
													#### https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/network_interface
												 

												
													##### Set the values in the corresponding .auto.tfvars.json file
												 

												
													resource "azurerm_network_interface" "TACG-TMM-TF-CC1-NIC" {
												 

												
													  depends_on = [ data.azurerm_subnet.TACG-TMM-Subnet ]
												 

												
													  name                           = "${var.TACG-TMM-CC1-NIC-Name}"
												 

												
													  location                       = "${var.TACG-TMM-ResourceGroup-Location}"
												 

												
													  resource_group_name            = "${var.TACG-TMM-ResourceGroup-Name}"
												 

												
													  accelerated_networking_enabled = true
												 

												
													  dns_servers                    = data.azurerm_virtual_network.TACG-TMM-VNet.dns_servers
												 

												
													  tags                           = {
												 

												
													                                        Environment = "${var.TAG-Environment}",
												 

												
													                                        Environment-Entity = "${var.TAG-Environment-Entity}",
												 

												
													                                        Environment-Usage = "${var.TAG-Environment-Usage}",
												 

												
													  }
												 

												
													 
												 

												
													  ip_configuration {
												 

												
													    name                          = "${var.TACG-TMM-CC1-IPC-Name}"
												 

												
													    subnet_id                     = data.azurerm_subnet.TACG-TMM-Subnet.id
												 

												
													    private_ip_address_allocation = "${var.TACG-TMM-CC1-IPC-Private_IP_Address_Allocation}" 
												 

												
													    private_ip_address            = "${var.TACG-TMM-CC1-IPC-Private_IP_Address}"
												 

												
													  }
												 

												
													}
												 

												
													 
												 

												
													#### Create a dedicated Network Interface and IP configuration for the CC2-VM
												 

												
													#### https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/network_interface
												 

												
													##### Set the values in the corresponding .auto.tfvars.json file
												 

												
													resource "azurerm_network_interface" "TACG-TMM-TF-CC2-NIC" {
												 

												
													  depends_on = [ data.azurerm_subnet.TACG-TMM-Subnet ]
												 

												
													  name                           = "${var.TACG-TMM-CC2-NIC-Name}"
												 

												
													  location                       = "${var.TACG-TMM-ResourceGroup-Location}"
												 

												
													  resource_group_name            = "${var.TACG-TMM-ResourceGroup-Name}"
												 

												
													  accelerated_networking_enabled = true
												 

												
													  dns_servers                    = data.azurerm_virtual_network.TACG-TMM-VNet.dns_servers
												 

												
													  tags                           = {
												 

												
													                                        Environment = "${var.TAG-Environment}",
												 

												
													                                        Environment-Entity = "${var.TAG-Environment-Entity}",
												 

												
													                                        Environment-Usage = "${var.TAG-Environment-Usage}",
												 

												
													  }
												 

												
													 
												 

												
													  ip_configuration {
												 

												
													    name                          = "${var.TACG-TMM-CC2-IPC-Name}"
												 

												
													    subnet_id                     = data.azurerm_subnet.TACG-TMM-Subnet.id
												 

												
													    private_ip_address_allocation = "${var.TACG-TMM-CC2-IPC-Private_IP_Address_Allocation}" 
												 

												
													    private_ip_address            = "${var.TACG-TMM-CC2-IPC-Private_IP_Address}"
												 

												
													  }
												 

												
													}
												 

												
													 
												 

												
													#### Associate the CC1-Network Interface with an existing NSG
												 

												
													#### https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/network_security_group
												 

												
													##### Set the values in the corresponding .auto.tfvars.json file
												 

												
													resource "azurerm_network_interface_security_group_association" "TACG-TMM-TF-CC1-NICNSG" {
												 

												
													  depends_on = [ azurerm_network_interface.TACG-TMM-TF-CC1-NIC, data.azurerm_network_security_group.TACG-TMM-NSG ]
												 

												
													  network_interface_id      = azurerm_network_interface.TACG-TMM-TF-CC1-NIC.id
												 

												
													  network_security_group_id = data.azurerm_network_security_group.TACG-TMM-NSG.id
												 

												
													}
												 

												
													 
												 

												
													#### Associate the CC2-Network Interface with an existing NSG
												 

												
													#### https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/network_security_group
												 

												
													##### Set the values in the corresponding .auto.tfvars.json file
												 

												
													resource "azurerm_network_interface_security_group_association" "TACG-TMM-TF-CC2-NICNSG" {
												 

												
													  depends_on = [ azurerm_network_interface.TACG-TMM-TF-CC2-NIC, data.azurerm_network_security_group.TACG-TMM-NSG ]
												 

												
													  network_interface_id      = azurerm_network_interface.TACG-TMM-TF-CC2-NIC.id
												 

												
													  network_security_group_id = data.azurerm_network_security_group.TACG-TMM-NSG.id
												 

												
													}
												 

												
													 
												 

												
													resource "azurerm_windows_virtual_machine" "TACG-TMM-CC1" {
												 

												
													  depends_on = [ azurerm_network_interface_security_group_association.TACG-TMM-TF-CC1-NICNSG ]
												 

												
													  name                  = "${var.TACG-TMM-CC1-VM-Name}"
												 

												
													  location              = "${var.TACG-TMM-ResourceGroup-Location}"
												 

												
													  resource_group_name   = "${var.TACG-TMM-ResourceGroup-Name}"
												 

												
													  network_interface_ids = [azurerm_network_interface.TACG-TMM-TF-CC1-NIC.id]
												 

												
													  size                  = "${var.TACG-TMM-CCX-VM-Size}"
												 

												
													  admin_username        = "${var.TACG-TMM-CCX-VM-Loc-UN}"
												 

												
													  admin_password        = "${var.TACG-TMM-CCX-VM-Loc-PW}"
												 

												
													  provision_vm_agent    = true
												 

												
													  timezone              = "W. Europe Standard Time"
												 

												
													  patch_mode            = "AutomaticByOS"
												 

												
													  enable_automatic_updates = true
												 

												
													  # Reboot setting is set to Never, because of Cloud COntrollers should not go down simultaneously due to a pending reboot!
												 

												
													  reboot_setting        = "Never"
												 

												
													  hotpatching_enabled   = false
												 

												
													  secure_boot_enabled   = false
												 

												
													 
												 

												
													 
												 

												
													 
												 

												
													  os_disk {
												 

												
													    name                 = "${var.TACG-TMM-CC1-VM-OSDrive-Name}"
												 

												
													    caching              = "${var.TACG-TMM-CCX-VM-OSDrive-Cache}"
												 

												
													    storage_account_type = "${var.TACG-TMM-CCX-VM-OSDrive-StorageType}"
												 

												
													  }
												 

												
													 
												 

												
													  tags                           = {
												 

												
													                                        Environment = "${var.TAG-Environment}",
												 

												
													                                        Environment-Entity = "${var.TAG-Environment-Entity}",
												 

												
													                                        Environment-Usage = "${var.TAG-Environment-Usage}",
												 

												
													  }
												 

												
													} 
												 

												
													 
												 

												
													#### Create the CC2-VM #### https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/virtual_machine
												 

												
													##### Set the values in the corresponding .auto.tfvars.json file
												 

												
													resource "azurerm_windows_virtual_machine" "TACG-TMM-CC2" {
												 

												
													  depends_on = [ azurerm_network_interface_security_group_association.TACG-TMM-TF-CC2-NICNSG ]
												 

												
													  name                  = "${var.TACG-TMM-CC2-VM-Name}"
												 

												
													  location              = "${var.TACG-TMM-ResourceGroup-Location}"
												 

												
													  resource_group_name   = "${var.TACG-TMM-ResourceGroup-Name}"
												 

												
													  network_interface_ids = [azurerm_network_interface.TACG-TMM-TF-CC2-NIC.id]
												 

												
													  size                  = "${var.TACG-TMM-CCX-VM-Size}"
												 

												
													  admin_username        = "${var.TACG-TMM-CCX-VM-Loc-UN}"
												 

												
													  admin_password        = "${var.TACG-TMM-CCX-VM-Loc-PW}"
												 

												
													  provision_vm_agent    = true
												 

												
													  timezone              = "W. Europe Standard Time"
												 

												
													  patch_mode            = "AutomaticByOS"
												 

												
													  enable_automatic_updates = true
												 

												
													  # Reboot setting is set to Never, because of Cloud Controllers should not go down simultaneously due to a pending reboot!
												 

												
													  reboot_setting        = "Never"
												 

												
													  hotpatching_enabled   = false
												 

												
													  secure_boot_enabled   = false
												 

												
													 
												 

												
													  
												 

												
													  os_disk {
												 

												
													    name                 = "${var.TACG-TMM-CC2-VM-OSDrive-Name}"
												 

												
													    caching              = "${var.TACG-TMM-CCX-VM-OSDrive-Cache}"
												 

												
													    storage_account_type = "${var.TACG-TMM-CCX-VM-OSDrive-StorageType}"
												 

												
													  }
												 

												
													 
												 

												
													  tags                           = {
												 

												
													                                        Environment = "${var.TAG-Environment}",
												 

												
													                                        Environment-Entity = "${var.TAG-Environment-Entity}",
												 

												
													                                        Environment-Usage = "${var.TAG-Environment-Usage}",
												 

												
													  }
												 

												
													}
												 

												
													 
												 
											
										
									
								
							
						

						
							 
						 

						
							Module 4: Put the Cloud Connector-VMs into the Active Directory Domain
						

						
							These are the Terraform configuration files for Module 4 (excerpts):
						 

						
							JoinDomain.tf
						

						
							
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}							
							
								
									
										
											
												
													# Create a Citrix Cloud Resource Location and DaaS Deployment on Microsoft Azure V2
												 

												
													## Create Pre-requisites
												 

												
													### We are using existing entitites like the Resource Group, NSGs, Firewall Rules, etc
												 

												
													#### Call Ansible to Join VM to Domain
												 

												
													##### Copy Ansible Playbooks to Ansible Server
												 

												
													 
												 

												
													###### ***** IMPORTANT: You must set the permissions to the destination directory accordingly (sudo) chmod 777 otherwise the provisioning will fail *****
												 

												
													resource "null_resource" "CopyPlaybookForCC1ToAnsibleServer" {
												 

												
													connection {
												 

												
													    type        = "${var.TACG-TMM-Ansible-Connection-Type}"
												 

												
													    user        = "${var.TACG-TMM-Ansible-Connection-User}"
												 

												
													    password    = "${var.TACG-TMM-Ansible-Connection-Password}"
												 

												
													    host        = "${var.TACG-TMM-Ansible-Connection-HostIP}"
												 

												
													 }
												 

												
													 
												 

												
													provisioner "file" {
												 

												
													  source      = "${var.TACG-TMM-Ansible-Playbook-CC1-Source}"
												 

												
													  destination = "${var.TACG-TMM-Ansible-Playbook-CC1-Destination}"
												 

												
													 }
												 

												
													}
												 

												
													 
												 

												
													resource "null_resource" "CopyPlaybookForCC2ToAnsibleServer" {
												 

												
													connection {
												 

												
													    type        = "${var.TACG-TMM-Ansible-Connection-Type}"
												 

												
													    user        = "${var.TACG-TMM-Ansible-Connection-User}"
												 

												
													    password    = "${var.TACG-TMM-Ansible-Connection-Password}"
												 

												
													    host        = "${var.TACG-TMM-Ansible-Connection-HostIP}"
												 

												
													 }
												 

												
													 
												 

												
													provisioner "file" {
												 

												
													  source      = "${var.TACG-TMM-Ansible-Playbook-CC2-Source}"
												 

												
													  destination = "${var.TACG-TMM-Ansible-Playbook-CC2-Destination}"
												 

												
													 }
												 

												
													}
												 

												
													 
												 

												
													##### Connect to Ansible Interpreter and Add CC1 To Domain
												 

												
													resource "null_resource" "AddCC1ToDomain" {
												 

												
													  depends_on = [ null_resource.CopyPlaybookForCC1ToAnsibleServer ]
												 

												
													  
												 

												
													connection {
												 

												
													    type      = "${var.TACG-TMM-Ansible-Connection-Type}"
												 

												
													    user      = "${var.TACG-TMM-Ansible-Connection-User}"
												 

												
													    password  = "${var.TACG-TMM-Ansible-Connection-Password}"
												 

												
													    host      = "${var.TACG-TMM-Ansible-Connection-HostIP}"
												 

												
													 }
												 

												
													 
												 

												
													provisioner "remote-exec" {
												 

												
													     inline = "${var.TACG-TMM-Ansible-CMDForCC1}"
												 

												
													 }
												 

												
													}
												 

												
													 
												 

												
													##### Connect to Ansible Interpreter and Add CC2 To Domain
												 

												
													resource "null_resource" "AddCC2ToDomain" {
												 

												
													  depends_on = [ null_resource.CopyPlaybookForCC2ToAnsibleServer ]
												 

												
													 
												 

												
													connection {
												 

												
													    type      = "${var.TACG-TMM-Ansible-Connection-Type}"
												 

												
													    user      = "${var.TACG-TMM-Ansible-Connection-User}"
												 

												
													    password  = "${var.TACG-TMM-Ansible-Connection-Password}"
												 

												
													    host      = "${var.TACG-TMM-Ansible-Connection-HostIP}"
												 

												
													 }
												 

												
													 
												 

												
													provisioner "remote-exec" {
												 

												
													     inline = "${var.TACG-TMM-Ansible-CMDForCC2}"
												 

												
													 }
												 

												
													} 
												 
											
										
									
								
							
						

						
							 
						 

						
							Join-VMToDomain-CC1.microsoft.ad.ansible.yml
						

						
							
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}							
							
								
									
										
											
												
													---
												 

												
													- name: join host to domain 
												 

												
													  hosts: cloudconnector1-ip
												 

												
													 
												 

												
													  tasks:
												 

												
													  - name: join host to domain 
												 

												
													    microsoft.ad.membership:
												 

												
													      dns_domain_name: wwco.net
												 

												
													      hostname: az-weur-tf-cc1
												 

												
													      domain_admin_user: XXXXXXXX@XXXXXXXX
												 

												
													      domain_admin_password: "XXXXXXXXXXXXXXXXXXXXXX"
												 

												
													      domain_ou_path: "OU=Infra,OU=West EUR,OU=Azure,OU=Cloud,OU=Compute,DC=wwco,DC=net"
												 

												
													      state: domain
												 

												
													      reboot: false
												 
											
										
									
								
							
						

						
							 
						 

						
							Module 5: Install and Configure the Cloud Connector Software
						

						
							These are the Terraform configuration files for Module 5 (excerpts):
						 

						
							DeployCCs.tf
						

						
							
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}							
							
								
									
										
											
												
													# Create a Citrix Cloud Resource Location and DaaS Deployment on Microsoft Azure V2
												 

												
													## Create the Resource Location and install the Cloud Connectors create the Resource Location
												 

												
													### Set local variables and scripts
												 

												
													#### Create a Resource Location in Citrix Cloud
												 

												
													#### https://github.com/citrix/terraform-provider-citrix/tree/main/docs/resources
												 

												
													##### Set the values in the corresponding .auto.tfvars.json file
												 

												
													resource "citrix_cloud_resource_location" "CreateCCRLInCC" {
												 

												
													    name                 = "${var.TACG-TMM-CC-RL-Name}"
												 

												
													}
												 

												
													 
												 

												
													#### Create a Zone in the just created Resource Location in Citrix Cloud
												 

												
													resource "citrix_zone" "CreateZoneInCC" {
												 

												
													  depends_on = [ citrix_cloud_resource_location.CreateCCRLInCC ]
												 

												
													    resource_location_id = citrix_cloud_resource_location.CreateCCRLInCC.id
												 

												
													}
												 

												
													 
												 

												
													#### Wait 10mins until Background Processes have completed
												 

												
													/*
												 

												
													resource "time_sleep" "Wait10Minutes" {
												 

												
													  depends_on = [ citrix_zone.CreateZoneInCC ]
												 

												
													  create_duration         = "600s"
												 

												
													}
												 

												
													*/
												 

												
													 
												 

												
													resource "local_file" "WriteRLDataToFile" {
												 

												
													  # depends_on = [ time_sleep.Wait10Minutes ]
												 

												
													    content  = citrix_cloud_resource_location.CreateCCRLInCC.id
												 

												
													    filename = "${path.module}/assets/RLData.txt"
												 

												
													}
												 

												
													 
												 

												
													resource "local_file" "WriteZoneDataToFile" {
												 

												
													  depends_on = [ time_sleep.Wait10Minutes ]
												 

												
													    content  = citrix_zone.CreateZoneInCC.id
												 

												
													    filename = "${path.module}/assets/ZoneData.txt"
												 

												
													}
												 

												
													 
												 

												
													#### Create JSON-file for Cloud Connector Installer
												 

												
													resource "local_file" "CWC-Configuration" {
												 

												
													 # depends_on = [ time_sleep.Wait10Minutes ]
												 

												
													  content  = jsonencode(
												 

												
													        {
												 

												
													        "customerName"          = "${var.cc-customerid}",
												 

												
													        "clientId"              = "${var.cc-apikey-clientId}",
												 

												
													        "clientSecret"          = "${var.cc-apikey-clientSecret}",
												 

												
													        "resourceLocationId"    = citrix_cloud_resource_location.CreateCCRLInCC.id
												 

												
													        "acceptTermsOfService"  = true
												 

												
													        }
												 

												
													      )
												 

												
													  filename = "${path.module}/assets/cwc.json"
												 

												
													}
												 

												
													 
												 

												
													#### Upload Cloud Connector Installer and Configuration file to CC1
												 

												
													##### Set the Provisioner-Connection
												 

												
													resource "null_resource" "UploadCCAndCWCToCC1" {
												 

												
													  depends_on = [ local_file.CWC-Configuration ]
												 

												
													  connection {
												 

												
													    type            = "${var.Provisioner_Type}"
												 

												
													    user            = "${var.Provisioner_Admin-Username}"
												 

												
													    password        = "${var.Provisioner_Admin-Password}"
												 

												
													    host            = "${var.CC1-VM-IP}"
												 

												
													    timeout         = "${var.Provisioner_Timeout}"
												 

												
													  }
												 

												
													 
												 

												
													###### Upload Cloud Connector Configuration to CC1
												 

												
													  provisioner "file" {
												 

												
													    source      = "${path.module}/assets/cwc.json"
												 

												
													    destination = "C:/temp/cwc.json"
												 

												
													    
												 

												
													  }
												 

												
													 
												 

												
													  ###### Upload Cloud Connector Installer to CC1
												 

												
													  provisioner "file" {
												 

												
													    source      = "${path.module}/assets/cwcconnector.exe"
												 

												
													    destination = "C:/temp/cwcconnector.exe"
												 

												
													    
												 

												
													  }
												 

												
													}
												 

												
													 
												 

												
													#### Upload Cloud Connector Installer and Configuration file to CC2
												 

												
													##### Set the Provisioner-Connection
												 

												
													resource "null_resource" "UploadCCAndCWCToCC2" {
												 

												
													  depends_on = [ local_file.CWC-Configuration ]
												 

												
													  connection {
												 

												
													    type            = "${var.Provisioner_Type}"
												 

												
													    user            = "${var.Provisioner_Admin-Username}"
												 

												
													    password        = "${var.Provisioner_Admin-Password}"
												 

												
													    host            = "${var.CC2-VM-IP}"
												 

												
													    timeout         = "${var.Provisioner_Timeout}"
												 

												
													  }
												 

												
													 
												 

												
													###### Upload Cloud Connector Configuration to CC2
												 

												
													  provisioner "file" {
												 

												
													    source      = "${path.module}/assets/cwc.json"
												 

												
													    destination = "C:/temp/cwc.json"
												 

												
													    
												 

												
													  }
												 

												
													 
												 

												
													  ###### Upload Cloud Connector Installer to CC2
												 

												
													  provisioner "file" {
												 

												
													    source      = "${path.module}/assets/cwcconnector.exe"
												 

												
													    destination = "C:/temp/cwcconnector.exe"
												 

												
													    
												 

												
													  }
												 

												
													}
												 

												
													 
												 

												
													###### ***** IMPORTANT: You must set the permissions to the destination directory accordingly (sudo) chmod 777 otherwise the provisioning will fail *****
												 

												
													resource "null_resource" "CopyPlaybookForCC1ToAnsibleServer" {
												 

												
													  depends_on = [ null_resource.UploadCCAndCWCToCC1 ]
												 

												
													connection {
												 

												
													  type        = "${var.TACG-TMM-Ansible-Connection-Type}"
												 

												
													  user        = "${var.TACG-TMM-Ansible-Connection-User}"
												 

												
													  password    = "${var.TACG-TMM-Ansible-Connection-Password}"
												 

												
													  host        = "${var.TACG-TMM-Ansible-Connection-HostIP}"
												 

												
													 }
												 

												
													 
												 

												
													provisioner "file" {
												 

												
													  source      = "${var.TACG-TMM-Ansible-Playbook-CC1-Source}"
												 

												
													  destination = "${var.TACG-TMM-Ansible-Playbook-CC1-Destination}"
												 

												
													 }
												 

												
													}
												 

												
													 
												 

												
													resource "null_resource" "CopyPlaybookForCC2ToAnsibleServer" {
												 

												
													  depends_on = [ null_resource.UploadCCAndCWCToCC2 ]
												 

												
													connection {
												 

												
													  type        = "${var.TACG-TMM-Ansible-Connection-Type}"
												 

												
													  user        = "${var.TACG-TMM-Ansible-Connection-User}"
												 

												
													  password    = "${var.TACG-TMM-Ansible-Connection-Password}"
												 

												
													  host        = "${var.TACG-TMM-Ansible-Connection-HostIP}"
												 

												
													 }
												 

												
													 
												 

												
													provisioner "file" {
												 

												
													  source      = "${var.TACG-TMM-Ansible-Playbook-CC2-Source}"
												 

												
													  destination = "${var.TACG-TMM-Ansible-Playbook-CC2-Destination}"
												 

												
													 }
												 

												
													}
												 

												
													 
												 

												
													##### Connect to Ansible Interpreter and Install CC on CC1
												 

												
													resource "null_resource" "InstallCCOnCC1" {
												 

												
													  depends_on = [ null_resource.CopyPlaybookForCC1ToAnsibleServer ]
												 

												
													  
												 

												
													connection {
												 

												
													  type      = "${var.TACG-TMM-Ansible-Connection-Type}"
												 

												
													  user      = "${var.TACG-TMM-Ansible-Connection-User}"
												 

												
													  password  = "${var.TACG-TMM-Ansible-Connection-Password}"
												 

												
													  host      = "${var.TACG-TMM-Ansible-Connection-HostIP}"
												 

												
													 }
												 

												
													 
												 

												
													provisioner "remote-exec" {
												 

												
													     inline = "${var.TACG-TMM-Ansible-CMDForCC1}"
												 

												
													 }
												 

												
													}
												 

												
													 
												 

												
													##### Connect to Ansible Interpreter and and Install CC on CC2
												 

												
													resource "null_resource" "InstallCCOnCC2" {
												 

												
													  depends_on = [ null_resource.CopyPlaybookForCC2ToAnsibleServer ]
												 

												
													 
												 

												
													connection {
												 

												
													  type      = "${var.TACG-TMM-Ansible-Connection-Type}"
												 

												
													  user      = "${var.TACG-TMM-Ansible-Connection-User}"
												 

												
													  password  = "${var.TACG-TMM-Ansible-Connection-Password}"
												 

												
													  host      = "${var.TACG-TMM-Ansible-Connection-HostIP}"
												 

												
													 }
												 

												
													 
												 

												
													provisioner "remote-exec" {
												 

												
													     inline = "${var.TACG-TMM-Ansible-CMDForCC2}"
												 

												
													 }
												 

												
													}
												 
											
										
									
								
							
						

						
							 
						 

						
							InstallCConCC1VM.ansible.yml
						

						
							
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}							
							
								
									
										
											
												
													 
												 

												
													---
												 

												
													- name: install citrix cloud connector
												 

												
													  hosts: cloudconnector1-ip
												 

												
													 
												 

												
													  tasks:
												 

												
													  - name: install citrix cloud connector
												 

												
													    ansible.windows.win_package:
												 

												
													      path: C:\temp\cwcconnector.exe
												 

												
													      product_id: CWCConnector.exe
												 

												
													      arguments:
												 

												
													        - /q
												 

												
													        - /ParametersFilePath:C:\temp\cwc.json
												 

												
													      state: present
												 

												
													    become: true
												 

												
													    become_method: runas
												 

												
													    become_user: SYSTEM
												 
											
										
									
								

								
									 
								 

								
									Module 6: Deploy the Citrix Cloud Entities
								

								
									These are the Terraform configuration files for Module 6 (excerpts):
								 

								
									DeployCitrixCloudEntities.tf
								

								
									
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}									
									
										
											
												
													
														
															# Create a Citrix Cloud Resource Location and DaaS Deployment on Microsoft Azure V2
														 

														
															## Create all relevant Citrix Cloud Entities
														 

														
															### Set local variables and scripts
														 

														
															locals {}
														 

														
															 
														 

														
															### Get existing entity data
														 

														
															#### Retrieving the ZoneID
														 

														
															data "citrix_zone" "GetTFAzureZoneID" {
														 

														
															  name = "${var.CC-Azure-ZoneID}"
														 

														
															}
														 

														
															 
														 

														
															#### Retrieving the Image Definition ID
														 

														
															data "citrix_image_definition" "GetImageDefinition" {
														 

														
															  name = "${var.CC-ImageDefinition-Name}"
														 

														
															  #name = "Example Image Definition"
														 

														
															} 
														 

														
															 
														 

														
															#### Retrieving the Image Version ID
														 

														
															data "citrix_image_version" "GetImageDefinitionVersion" {
														 

														
															  version_number = "${var.CC-ImageDefinition-Version}"
														 

														
															  image_definition = data.citrix_image_definition.GetImageDefinition.id
														 

														
															}
														 

														
															 
														 

														
															### Creating the Hypervisor Connection
														 

														
															resource "citrix_azure_hypervisor" "CreateAzureHostingConnection" {
														 

														
															  depends_on = [ data.citrix_zone.GetTFAzureZoneID ]
														 

														
															    name                = "${var.CC-Azure-HypConn-Name}"
														 

														
															    zone                = data.citrix_zone.GetTFAzureZoneID.id
														 

														
															    active_directory_id = "${var.azurerm-tenantid}"
														 

														
															    subscription_id     = "${var.azurerm-subscriptionid}"
														 

														
															    application_secret  = "${var.azurerm-clientsecret}"
														 

														
															    application_id      = "${var.azurerm-clientid}"
														 

														
															} 
														 

														
															 
														 

														
															#### Sleep 30s to let Background processes settle
														 

														
															/*
														 

														
															resource "time_sleep" "wait_30_seconds" {
														 

														
															  depends_on = [ citrix_azure_hypervisor.CreateAzureHostingConnection ]
														 

														
															  create_duration = "30s"
														 

														
															}  
														 

														
															*/
														 

														
															 
														 

														
															#### Creating the Hypervisor Resource Pool
														 

														
															resource "citrix_azure_hypervisor_resource_pool" "CreateAzureHostingConnectionPool" {
														 

														
															  # depends_on = [ time_sleep.wait_30_seconds]
														 

														
															  name                           = "${var.CC-Azure-HypConn-Name}"
														 

														
															  hypervisor                     = citrix_azure_hypervisor.CreateAzureHostingConnection.id
														 

														
															  region                         = "${var.TACG-TMM-ResourceGroup-Location}"
														 

														
															  virtual_network_resource_group = "${var.TACG-TMM-ResourceGroup-Name}"
														 

														
															  virtual_network                = "${var.CC-Azure-VNet-Name}"
														 

														
															  subnets                        = var.CC-Azure-Subnets
														 

														
															}
														 

														
															 
														 

														
															#### Sleep 30s to let Background processes settle
														 

														
															/*
														 

														
															resource "time_sleep" "wait_30_seconds1" {
														 

														
															  depends_on = [ citrix_azure_hypervisor_resource_pool.CreateAzureHostingConnectionPool ]
														 

														
															  create_duration = "30s"
														 

														
															}
														 

														
															*/
														 

														
															 
														 

														
															#### Creating the Machine Catalog
														 

														
															resource "citrix_machine_catalog" "CreateAzureMCSCatalog" {
														 

														
															 # depends_on            = [ time_sleep.wait_30_seconds1 ]
														 

														
															    name                        = "${var.CC-Azure-MC-Name}"
														 

														
															    description                 = "${var.CC-Azure-MC-Description}"
														 

														
															    allocation_type             = "${var.CC-Azure-MC-AllocationType}"
														 

														
															    session_support             = "${var.CC-Azure-MC-SessionType}"
														 

														
															    provisioning_type           = "MCS"
														 

														
															    #zone                = data.local_file.LoadZoneID.content
														 

														
															    zone                = data.citrix_zone.GetTFAzureZoneID.id
														 

														
															    
														 

														
															    
														 

														
															    provisioning_scheme         =   {
														 

														
															        hypervisor               = citrix_azure_hypervisor.CreateAzureHostingConnection.id
														 

														
															        hypervisor_resource_pool = citrix_azure_hypervisor_resource_pool.CreateAzureHostingConnectionPool.id
														 

														
															        identity_type            = "${var.CC-Azure-MC-IDPType}"
														 

														
															        
														 

														
															 
														 

														
															        machine_domain_identity  = {
														 

														
															            domain                   = "${var.CC-Azure-MC-Domain}"
														 

														
															            domain_ou                = "${var.CC-Azure-MC-DomainOU}"
														 

														
															            service_account          = "${var.CC-Azure-MC-DomainAdmin-Username-UPN}"
														 

														
															            service_account_password = "${var.CC-Azure-MC-DomainAdmin-Password}"
														 

														
															        }
														 

														
															 
														 

														
															        azure_machine_config = {
														 

														
															            storage_type             = "Standard_LRS"
														 

														
															            use_managed_disks        = true
														 

														
															            service_offering         = "${var.CC-Azure-MC-VMSize}"
														 

														
															 
														 

														
															             prepared_image = {
														 

														
															                image_definition = data.citrix_image_definition.GetImageDefinition.id
														 

														
															                image_version    = data.citrix_image_version.GetImageDefinitionVersion.id
														 

														
															 
														 

														
															                
														 

														
															            }  
														 

														
															 
														 

														
															 machine_profile = {
														 

														
															              machine_profile_resource_group = "${var.TACG-TMM-ResourceGroup-Name}"
														 

														
															              machine_profile_vm_name        = "${var.CC-Azure-MC-VMProfileName}"
														 

														
															            }
														 

														
															 
														 

														
															        }
														 

														
															 
														 

														
															       number_of_total_machines                =  1
														 

														
															    
														 

														
															       machine_account_creation_rules          = {
														 

														
															            naming_scheme      = "${var.CC-Azure-MC-NamingScheme-Name}"
														 

														
															            naming_scheme_type = "${var.CC-Azure-MC-NamingScheme-Type}"
														 

														
															        }
														 

														
															    }
														 

														
															}
														 

														
															 
														 

														
															#### Sleep 60s to let CC Background processes settle
														 

														
															/*
														 

														
															resource "time_sleep" "wait_30_seconds_2" {
														 

														
															  depends_on = [ citrix_machine_catalog.CreateAzureMCSCatalog ]
														 

														
															  create_duration = "30s"
														 

														
															}  
														 

														
															*/
														 

														
															 
														 

														
															#### Create the Delivery Group based on the Machine Catalog
														 

														
															resource "citrix_delivery_group" "CreateDG" {
														 

														
															  # depends_on = [ time_sleep.wait_30_seconds_2]
														 

														
															    name                                    = "${var.CC-Azure-DG-Name}"
														 

														
															    associated_machine_catalogs             = [
														 

														
															        {
														 

														
															            machine_catalog                 = citrix_machine_catalog.CreateAzureMCSCatalog.id
														 

														
															            machine_count                   = "${var.CC-Azure-MC-Machine_Count}"
														 

														
															        }
														 

														
															    ]
														 

														
															    desktops                                = [
														 

														
															        {
														 

														
															            published_name                  = "${var.CC-Azure-DG-PublishedDesktopName}"
														 

														
															            description                     = "${var.CC-Azure-DG-Description}"
														 

														
															            restricted_access_users         = {
														 

														
															                                               allow_list = [ "WWCO\\GroupName" ]
														 

														
															                                              }
														 

														
															            enabled                         = true
														 

														
															        }
														 

														
															        
														 

														
															    ] 
														 

														
															    autoscale_settings                      = {
														 

														
															            autoscale_enabled                                   = true
														 

														
															            disconnect_peak_idle_session_after_seconds          = 60
														 

														
															            disconnect_off_peak_idle_session_after_seconds      = 60
														 

														
															            log_off_off_peak_disconnected_session_after_seconds = 60
														 

														
															            log_off_peak_disconnected_session_after_seconds     = 60
														 

														
															 
														 

														
															            power_time_schemes              = [
														 

														
															                                              {
														 

														
															                                               days_of_week = [
														 

														
															                                                              "Monday",
														 

														
															                                                              "Tuesday",
														 

														
															                                                              "Wednesday",
														 

														
															                                                              "Thursday",
														 

														
															                                                              "Friday"
														 

														
															                                                              ]
														 

														
															                name                        = "${var.CC-Azure-DG-AS-Name}"
														 

														
															                display_name                = "${var.CC-Azure-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 = [ "WWCO\\Groupname" ]
														 

														
															                                              }
														 

														
															    reboot_schedules                        = [
														 

														
															                                               {
														 

														
															                                                 name = "TMM-GK-TF-RebootScheduleN"
														 

														
															                                                 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
														 

														
															                                              }
														 

														
															  ]
														 

														
															}
														 
													
												
											
										
									
								

								
									 
								 

								
									Module 7: Create Admin Scopes, Admin Roles, and a Policy Set
								

								
									These are the Terraform configuration files for Module 7 (excerpts):
								 

								
									DeployCitrixPoliciesAndScopes.tf
								

								
									 
								 

								
									
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}									
									
										
											
												
													
														
															# Create a Citrix Cloud Resource Location and DaaS Deployment on Microsoft Azure V2
														 

														
															# Terraform deployment of Scopes, Roles, and Policies
														 

														
															## Creating all Citrix Cloud-related entities
														 

														
															### Retrieve default Monitor Scope id for later use
														 

														
															data "citrix_admin_scope" "GetDefaultScope" {
														 

														
															    name = "All"
														 

														
															}
														 

														
															 
														 

														
															### Retrieve Machine Catalog ID for later use
														 

														
															data "citrix_machine_catalog" "GetMachineCatalog" {
														 

														
															    name = "${var.CC-Azure-MC-Name}"
														 

														
															}
														 

														
															 
														 

														
															### Retrieve MDelivery Group ID for later use
														 

														
															data "citrix_delivery_group" "GetDeliveryGroup" {
														 

														
															    name = "${var.CC-Azure-DG-Name}"
														 

														
															}
														 

														
															 
														 

														
															output "MC" {
														 

														
															  value = data.citrix_machine_catalog.GetMachineCatalog.id
														 

														
															}
														 

														
															 
														 

														
															output "DG" {
														 

														
															  value = data.citrix_delivery_group.GetDeliveryGroup.id
														 

														
															}
														 

														
															 
														 

														
															### Creating Monitor examples
														 

														
															#### Create an Monitor Scope example
														 

														
															resource "citrix_admin_scope" "CreateDefaultScopeExample" {
														 

														
															    name        = var.CreateMonitorScopeExample-Name
														 

														
															    description = var.CreateMonitorScopeExample-Description
														 

														
															}
														 

														
															 
														 

														
															#### Create an Monitor Role example
														 

														
															resource "citrix_admin_role" "CreateMonitorRoleExample" {
														 

														
															    name        = var.CreateMonitorRoleExample-Name
														 

														
															    description = var.CreateMonitorRoleExample-Description
														 

														
															    can_launch_manage = false
														 

														
															    can_launch_monitor = true
														 

														
															    permissions = [
														 

														
															        "Applications_Read",
														 

														
															        "Catalog_Read",
														 

														
															        "DesktopGroup_Read",
														 

														
															        "Director_Alerts_Read",
														 

														
															        "Director_ClientDetails_Read",
														 

														
															        "Director_ClientHelpDesk_Read",
														 

														
															        "Director_Dashboard_Read",
														 

														
															        "Director_HelpDesk_Read",
														 

														
															        "Director_MachineDetails_Read",
														 

														
															        "Director_Trends_Read",
														 

														
															        "Director_UserDetails_Read",
														 

														
															        "Hosts_Read",
														 

														
															        "Logging_Read",
														 

														
															        "Policies_Read",
														 

														
															        "Setting_Read",
														 

														
															        "Zone_Read"
														 

														
															    ]
														 

														
															}
														 

														
															 
														 

														
															#### Create a Policy example set
														 

														
															resource "citrix_policy_set" "CreatePolicyExampleSet" {
														 

														
															    name           = "${var.CreatePolicyExampleSet-Name}"
														 

														
															    description    = "${var.CreatePolicyExampleSet-Description}"
														 

														
															    policies = [
														 

														
															        {
														 

														
															            name = "Printing"
														 

														
															            description = "Example of Printer-related policy set"
														 

														
															            enabled = true
														 

														
															            policy_settings = [
														 

														
															                {
														 

														
															                    name = "ClientPrinterAutoCreation"
														 

														
															                    value = "DefaultPrinterOnly"
														 

														
															                    use_default = false
														 

														
															                },
														 

														
															                {
														 

														
															                    name = "UniversalPrintDriverUsage"
														 

														
															                    value = "FallbackToSpecific"
														 

														
															                    use_default = false
														 

														
															                },
														 

														
															            ]
														 

														
															            delivery_group_filters = [
														 

														
															                {
														 

														
															                    delivery_group_id   = data.citrix_delivery_group.GetDeliveryGroup.id 
														 

														
															                    enabled = true
														 

														
															                    allowed = true
														 

														
															                },
														 

														
															            ]
														 

														
															            user_filters = [
														 

														
															                {
														 

														
															                    sid     = "${var.Policy-Filter-UserSID}"
														 

														
															                    enabled = true
														 

														
															                    allowed = true
														 

														
															                },
														 

														
															            ]
														 

														
															        },
														 

														
															        {
														 

														
															            name = "HDX Graphics"
														 

														
															            description = "Example of HDX Graphics-related policy set"
														 

														
															            enabled = true
														 

														
															            policy_settings = [
														 

														
															                {
														 

														
															                    name = "AllowVisuallyLosslessCompression"
														 

														
															                    enabled = true
														 

														
															                    use_default = false
														 

														
															                },
														 

														
															                {
														 

														
															                    name = "UseVideoCodecForCompression"
														 

														
															                    value = "UseVideoCodecIfPreferred"
														 

														
															                    use_default = false
														 

														
															                },
														 

														
															                {
														 

														
															                    name = "UseHardwareEncodingForVideoCodec"
														 

														
															                    enabled = true
														 

														
															                    use_default = false
														 

														
															                },
														 

														
															            ]
														 

														
															            delivery_group_filters = [
														 

														
															                {
														 

														
															                    delivery_group_id   = data.citrix_delivery_group.GetDeliveryGroup.id 
														 

														
															                    enabled = true
														 

														
															                    allowed = true
														 

														
															                },
														 

														
															            ]
														 

														
															            user_filters = [
														 

														
															                {
														 

														
															                    sid     = "${var.Policy-Filter-UserSID}"
														 

														
															                    enabled = true
														 

														
															                    allowed = true
														 

														
															                },
														 

														
															            ]
														 

														
															            client_ip_filters = [
														 

														
															                {
														 

														
															                    ip_address = "10.53.16.0"
														 

														
															                    enabled    = true
														 

														
															                    allowed    = true
														 

														
															                },
														 

														
															            ]
														 

														
															        },
														 

														
															       {
														 

														
															            name = "Client Drives"
														 

														
															            description = "Example of Client Drive-related policy set"
														 

														
															            enabled = true
														 

														
															            policy_settings = [
														 

														
															                {
														 

														
															                    name = "AutoConnectDrives"
														 

														
															                    enabled = true
														 

														
															                    use_default = false
														 

														
															                },
														 

														
															                {
														 

														
															                    name = "ClientDriveRedirection"
														 

														
															                    enabled = true
														 

														
															                    use_default = false
														 

														
															                },
														 

														
															                {
														 

														
															                    name = "ClientFixedDrives"
														 

														
															                    enabled = true
														 

														
															                    use_default = false
														 

														
															                },
														 

														
															                {
														 

														
															                    name = "ClientFloppyDrives"
														 

														
															                    enabled = false
														 

														
															                    use_default = false
														 

														
															                },
														 

														
															                {
														 

														
															                    name = "ClientOpticalDrives"
														 

														
															                    enabled = false
														 

														
															                    use_default = false
														 

														
															                },
														 

														
															                {
														 

														
															                    name = "ClientNetworkDrives"
														 

														
															                    enabled = false
														 

														
															                    use_default = false
														 

														
															                },
														 

														
															            ]
														 

														
															            delivery_group_filters = [
														 

														
															                {
														 

														
															                    delivery_group_id   = data.citrix_delivery_group.GetDeliveryGroup.id 
														 

														
															                    enabled = true
														 

														
															                    allowed = true
														 

														
															                },
														 

														
															            ]
														 

														
															            user_filters = [
														 

														
															                {
														 

														
															                    sid     = "${var.Policy-Filter-UserSID}"
														 

														
															                    enabled = true
														 

														
															                    allowed = true
														 

														
															                },
														 

														
															            ]
														 

														
															            client_ip_filters = [
														 

														
															                {
														 

														
															                    ip_address = "10.53.16.0"
														 

														
															                    enabled    = true
														 

														
															                    allowed    = true
														 

														
															                },
														 

														
															            ]
														 

														
															        } 
														 

														
															    ]
														 

														
															     delivery_groups = [ data.citrix_delivery_group.GetDeliveryGroup.id ]
														 

														
															}
														 
													
												
											
										
									
								

								
									 
								 

								
									Module 8: Configure Citrix Workspace App and Citrix Enterprise Browser using the Global App Configuration Service (GACS)
								

								
									These are the Terraform configuration files for Module 8 (excerpts):
								 

								
									ConfiguringCWAAndCEBUsingGACS.tf
								

								
									 
								 

								
									
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}									
									
										
											
												
													
														
															# Configuring Citrix Cloud Global App Configuration Service
														 

														
															## Create Settings
														 

														
															resource "citrix_gac_settings" "TMM-TF-GACS-Settings" {
														 

														
															    service_url = "${var.GACS-ServiceURL}"
														 

														
															    name        = "${var.GACS-Settings-Name}"
														 

														
															    description = "${var.GACS-Settings-Description}"
														 

														
															    test_channel = true
														 

														
															 
														 

														
															    app_settings = {
														 

														
															        windows = [
														 

														
															            {
														 

														
															                user_override = false,
														 

														
															                category = "root",
														 

														
															                settings = [
														 

														
															                    {
														 

														
															                        name = "Hide advanced preferences",
														 

														
															                        value_string = "true"
														 

														
															                    },
														 

														
															                    {
														 

														
															                        name = "Hide connection center",
														 

														
															                        value_string = "true"
														 

														
															                    }  
														 

														
															                ]
														 

														
															            },
														 

														
															            {
														 

														
															                user_override = false,
														 

														
															                category = "browser",
														 

														
															                settings = [
														 

														
															                    {
														 

														
															                        name = "Incognito Mode Availability",
														 

														
															                        value_string = "Incognito mode available"
														 

														
															                    },
														 

														
															                    {
														 

														
															                        name = "saving browser history disabled",
														 

														
															                        value_string = "true"
														 

														
															                    }
														 

														
															                ]
														 

														
															            },
														 

														
															            {
														 

														
															                user_override = false,
														 

														
															                category = "dazzle",
														 

														
															                settings = [
														 

														
															                    {
														 

														
															                        name = "Put shortcuts on desktop",
														 

														
															                        value_string = "true"
														 

														
															                    }
														 

														
															                ]
														 

														
															            },
														 

														
															            {
														 

														
															                user_override = false,
														 

														
															                category = "ica client",
														 

														
															                settings = [
														 

														
															                    {
														 

														
															                        name = "Allow Client Clipboard Redirection",
														 

														
															                        value_string = "true"
														 

														
															                    }
														 

														
															                ]
														 

														
															            }
														 

														
															        ],
														 

														
															        html5 = [
														 

														
															            {
														 

														
															                category = "virtual channel",
														 

														
															                user_override = false,
														 

														
															                settings = [
														 

														
															                    {
														 

														
															                        name = "Clipboard Operations Between VDA And Local Device",
														 

														
															                        value_string = "true"
														 

														
															                    }  
														 

														
															                ]
														 

														
															            },
														 

														
															            {
														 

														
															                category = "toolbar",
														 

														
															                user_override = false,
														 

														
															                settings = [
														 

														
															                    {
														 

														
															                        name = "Clipboard Option in Toolbar",
														 

														
															                        value_string = "true"
														 

														
															                    },
														 

														
															                    {
														 

														
															                        name = "USB Devices Option in Toolbar",
														 

														
															                        value_string = "true"
														 

														
															                    }  
														 

														
															                ]
														 

														
															            }
														 

														
															        ],
														 

														
															        ios = [
														 

														
															            {
														 

														
															                category = "advanced",
														 

														
															                user_override = false,
														 

														
															                settings = [
														 

														
															                    {
														 

														
															                        name = "Touch Enable",
														 

														
															                        value_string = "true"
														 

														
															                    },
														 

														
															                    {
														 

														
															                        name = "Fullscreen Window",
														 

														
															                        value_string = "true"
														 

														
															                    }  
														 

														
															                ]
														 

														
															            }
														 

														
															        ],
														 

														
															        android = [
														 

														
															            {
														 

														
															                category = "advanced",
														 

														
															                user_override = false,
														 

														
															                settings = [
														 

														
															                    {
														 

														
															                        name = "Enable clipboard",
														 

														
															                        value_string = "true"
														 

														
															                    }  
														 

														
															                ]
														 

														
															            }
														 

														
															        ],
														 

														
															        macos = [
														 

														
															            {
														 

														
															                category = "browser",
														 

														
															                user_override = false,
														 

														
															                settings = [
														 

														
															                    {
														 

														
															                        name = "Enable Password Save",
														 

														
															                        value_string = "true"
														 

														
															                    },
														 

														
															                    {
														 

														
															                        name = "Enable Citrix Enterprise Browser shortcut",
														 

														
															                        value_string = "true"
														 

														
															                    }
														 

														
															                ]
														 

														
															            }
														 

														
															        ],
														 

														
															        linux = [
														 

														
															            {
														 

														
															                category = "browser",
														 

														
															                user_override = false,
														 

														
															                settings = [
														 

														
															                    {
														 

														
															                        name = "Incognito Mode Availability",
														 

														
															                        value_string = "Incognito mode available"
														 

														
															                    },
														 

														
															                    {
														 

														
															                        name = "Enable Password Save",
														 

														
															                        value_string = "true"
														 

														
															                    }
														 

														
															                ]
														 

														
															            }
														 

														
															        ] 
														 

														
															    }
														 

														
															 }
														 
													
												
											
										
									
								

								
									 
								 

								
									gac_settings.md (Example)
								

								
									You can find the current settings here.
								 

								
									
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}									
									
										
											
												
													
														
															 
														 

														
															---
														 

														
															# generated by https://github.com/hashicorp/terraform-plugin-docs
														 

														
															page_title: "citrix_gac_settings Resource - citrix"
														 

														
															subcategory: "Citrix Cloud"
														 

														
															description: |-
														 

														
															  Manages the Global App Configuration settings for a service url.
														 

														
															---
														 

														
															 
														 

														
															# citrix_gac_settings (Resource)
														 

														
															 
														 

														
															Manages the Global App Configuration settings for a service url.
														 

														
															 
														 

														
															## Global App Configuration
														 

														
															 
														 

														
															The Global App Configuration (GAC) Service is designed to allow administrators to easily configure:
														 

														
															- The workspace service URL to the email domain for discovery
														 

														
															- Settings for Citrix Workspace apps  
														 

														
															 
														 

														
															At this time, the citrix terraform provider only supports GAC settings configuration for Citrix Workspace apps.
														 

														
															 
														 

														
															Reference Links:
														 

														
															- [Prerequisites to use Global App Configuration Service](https://developer-docs.citrix.com/en-us/server-integration/global-app-configuration-service/getting-started#prerequisites)
														 

														
															- [GAC Supported settings and their values per platform](https://developer-docs.citrix.com/en-us/server-integration/global-app-configuration-service/getting-started#supported-settings-and-their-values-per-platform) - Please note that only settings objects with value type of integer, boolean, strings and list of strings is currently supported by the citrix terraform provider.
														 

														
															- [Global App Configuration Service for StoreFront stores](https://developer-docs.citrix.com/en-us/server-integration/global-app-configuration-service/getting-started#global-apps-configuration-service-for-storefront-stores)
														 

														
															 
														 

														
															## Schema
														 

														
															 
														 

														
															### Required
														 

														
															 
														 

														
															- `app_settings` (Attributes) Defines the device platform and the associated settings. Currently, only settings objects with value type of integer, boolean, strings and list of strings is supported. (see [below for nested schema](#nestedatt--app_settings))
														 

														
															- `description` (String) Description of the settings record.
														 

														
															- `name` (String) Name of the settings record.
														 

														
															- `service_url` (String) Citrix workspace application store url for which settings are to be configured. The value is case sensitive and requires the protocol ("https" or "http") and port number.
														 

														
															 
														 

														
															### Optional
														 

														
															 
														 

														
															- `use_for_app_config` (Boolean) Defines whether to use the settings for app configuration or not. Defaults to `true`.
														 

														
															 
														 

														
															&lt;a id="nestedatt--app_settings"&gt;&lt;/a&gt;
														 

														
															### Nested Schema for `app_settings`
														 

														
															 
														 

														
															Optional:
														 

														
															 
														 

														
															- `android` (Attributes Set) Settings to be applied for users using android platform. (see [below for nested schema](#nestedatt--app_settings--android))
														 

														
															- `chromeos` (Attributes Set) Settings to be applied for users using chrome os platform. (see [below for nested schema](#nestedatt--app_settings--chromeos))
														 

														
															- `html5` (Attributes Set) Settings to be applied for users using html5. (see [below for nested schema](#nestedatt--app_settings--html5))
														 

														
															- `ios` (Attributes Set) Settings to be applied for users using ios platform. (see [below for nested schema](#nestedatt--app_settings--ios))
														 

														
															- `linux` (Attributes Set) Settings to be applied for users using linux platform. (see [below for nested schema](#nestedatt--app_settings--linux))
														 

														
															- `macos` (Attributes Set) Settings to be applied for users using mac os platform. (see [below for nested schema](#nestedatt--app_settings--macos))
														 

														
															- `windows` (Attributes Set) Settings to be applied for users using windows platform. (see [below for nested schema](#nestedatt--app_settings--windows))
														 

														
															 
														 

														
															&lt;a id="nestedatt--app_settings--android"&gt;&lt;/a&gt;
														 

														
															### Nested Schema for `app_settings.android`
														 

														
															 
														 

														
															Required:
														 

														
															 
														 

														
															- `category` (String) Defines the category of the setting.
														 

														
															- `settings` (Attributes List) A list of name value pairs for the settings. Please refer to the following [table](https://developer-docs.citrix.com/en-us/server-integration/global-app-configuration-service/getting-started#supported-settings-and-their-values-per-platform) for the supported settings name and their values per platform. (see [below for nested schema](#nestedatt--app_settings--android--settings))
														 

														
															- `user_override` (Boolean) Defines if users can modify or change the value of as obtained settings from the Global App Citrix Workspace configuration service.
														 

														
															 
														 

														
															&lt;a id="nestedatt--app_settings--android--settings"&gt;&lt;/a&gt;
														 

														
															### Nested Schema for `app_settings.android.settings`
														 

														
															 
														 

														
															Required:
														 

														
															 
														 

														
															- `name` (String) Name of the setting.
														 

														
															 
														 

														
															Optional:
														 

														
															 
														 

														
															- `value_list` (List of String) List value (if any) associated with the setting.
														 

														
															- `value_string` (String) String value (if any) associated with the setting.
														 

														
															 
															 
														 

														
															&lt;a id="nestedatt--app_settings--chromeos"&gt;&lt;/a&gt;
														 

														
															### Nested Schema for `app_settings.chromeos`
														 

														
															 
														 

														
															Required:
														 

														
															 
														 

														
															- `category` (String) Defines the category of the setting.
														 

														
															- `settings` (Attributes List) A list of name value pairs for the settings. Please refer to the following [table](https://developer-docs.citrix.com/en-us/server-integration/global-app-configuration-service/getting-started#supported-settings-and-their-values-per-platform) for the supported settings name and their values per platform. (see [below for nested schema](#nestedatt--app_settings--chromeos--settings))
														 

														
															- `user_override` (Boolean) Defines if users can modify or change the value of as obtained settings from the Global App Citrix Workspace configuration service.
														 

														
															 
														 

														
															&lt;a id="nestedatt--app_settings--chromeos--settings"&gt;&lt;/a&gt;
														 

														
															### Nested Schema for `app_settings.chromeos.settings`
														 

														
															 
														 

														
															Required:
														 

														
															 
														 

														
															- `name` (String) Name of the setting.
														 

														
															 
														 

														
															Optional:
														 

														
															 
														 

														
															- `value_list` (List of String) List value (if any) associated with the setting.
														 

														
															- `value_string` (String) String value (if any) associated with the setting.
														 

														
															 
															 
														 

														
															&lt;a id="nestedatt--app_settings--html5"&gt;&lt;/a&gt;
														 

														
															### Nested Schema for `app_settings.html5`
														 

														
															 
														 

														
															Required:
														 

														
															 
														 

														
															- `category` (String) Defines the category of the setting.
														 

														
															- `settings` (Attributes List) A list of name value pairs for the settings. Please refer to the following [table](https://developer-docs.citrix.com/en-us/server-integration/global-app-configuration-service/getting-started#supported-settings-and-their-values-per-platform) for the supported settings name and their values per platform. (see [below for nested schema](#nestedatt--app_settings--html5--settings))
														 

														
															- `user_override` (Boolean) Defines if users can modify or change the value of as obtained settings from the Global App Citrix Workspace configuration service.
														 

														
															 
														 

														
															&lt;a id="nestedatt--app_settings--html5--settings"&gt;&lt;/a&gt;
														 

														
															### Nested Schema for `app_settings.html5.settings`
														 

														
															 
														 

														
															Required:
														 

														
															 
														 

														
															- `name` (String) Name of the setting.
														 

														
															 
														 

														
															Optional:
														 

														
															 
														 

														
															- `value_list` (List of String) List value (if any) associated with the setting.
														 

														
															- `value_string` (String) String value (if any) associated with the setting.
														 

														
															 
															 
														 

														
															&lt;a id="nestedatt--app_settings--ios"&gt;&lt;/a&gt;
														 

														
															### Nested Schema for `app_settings.ios`
														 

														
															 
														 

														
															Required:
														 

														
															 
														 

														
															- `category` (String) Defines the category of the setting
														 

														
															- `settings` (Attributes List) A list of name value pairs for the settings. Please refer to the following [table](https://developer-docs.citrix.com/en-us/server-integration/global-app-configuration-service/getting-started#supported-settings-and-their-values-per-platform) for the supported settings name and their values per platform. (see [below for nested schema](#nestedatt--app_settings--ios--settings))
														 

														
															- `user_override` (Boolean) Defines if users can modify or change the value of as obtained settings from the Global App Citrix Workspace configuration service.
														 

														
															 
														 

														
															&lt;a id="nestedatt--app_settings--ios--settings"&gt;&lt;/a&gt;
														 

														
															### Nested Schema for `app_settings.ios.settings`
														 

														
															 
														 

														
															Required:
														 

														
															 
														 

														
															- `name` (String) Name of the setting.
														 

														
															 
														 

														
															Optional:
														 

														
															 
														 

														
															- `value_string` (String) String value (if any) associated with the setting.
														 

														
															 
															 
														 

														
															&lt;a id="nestedatt--app_settings--linux"&gt;&lt;/a&gt;
														 

														
															### Nested Schema for `app_settings.linux`
														 

														
															 
														 

														
															Required:
														 

														
															 
														 

														
															- `category` (String) Defines the category of the setting.
														 

														
															- `settings` (Attributes List) A list of name value pairs for the settings. Please refer to the following [table](https://developer-docs.citrix.com/en-us/server-integration/global-app-configuration-service/getting-started#supported-settings-and-their-values-per-platform) for the supported settings name and their values per platform. (see [below for nested schema](#nestedatt--app_settings--linux--settings))
														 

														
															- `user_override` (Boolean) Defines if users can modify or change the value of as obtained settings from the Global App Citrix Workspace configuration service.
														 

														
															 
														 

														
															&lt;a id="nestedatt--app_settings--linux--settings"&gt;&lt;/a&gt;
														 

														
															### Nested Schema for `app_settings.linux.settings`
														 

														
															 
														 

														
															Required:
														 

														
															 
														 

														
															- `name` (String) Name of the setting.
														 

														
															 
														 

														
															Optional:
														 

														
															 
														 

														
															- `auto_launch_protocols_from_origins` (Attributes List) A list of protocols that can launch an external application from the listed origins without prompting the user. (see [below for nested schema](#nestedatt--app_settings--linux--settings--auto_launch_protocols_from_origins))
														 

														
															- `extension_install_allow_list` (Attributes List) An allowed list of extensions that users can add to the Citrix Enterprise Browser. This list uses the Chrome Web Store. (see [below for nested schema](#nestedatt--app_settings--linux--settings--extension_install_allow_list))
														 

														
															- `managed_bookmarks` (Attributes List) A list of bookmarks to push to the Citrix Enterprise Browser. (see [below for nested schema](#nestedatt--app_settings--linux--settings--managed_bookmarks))
														 

														
															- `value_list` (List of String) List value (if any) associated with the setting.
														 

														
															- `value_string` (String) String value (if any) associated with the setting.
														 

														
															 
														 

														
															&lt;a id="nestedatt--app_settings--linux--settings--auto_launch_protocols_from_origins"&gt;&lt;/a&gt;
														 

														
															### Nested Schema for `app_settings.linux.settings.value_string`
														 

														
															 
														 

														
															Required:
														 

														
															 
														 

														
															- `protocol` (String) Auto launch protocol
														 

														
															 
														 

														
															Optional:
														 

														
															 
														 

														
															- `allowed_origins` (List of String) List of origins urls
														 

														
															 
														 

														
															&lt;a id="nestedatt--app_settings--linux--settings--extension_install_allow_list"&gt;&lt;/a&gt;
														 

														
															### Nested Schema for `app_settings.linux.settings.value_string`
														 

														
															 
														 

														
															Required:
														 

														
															 
														 

														
															- `id` (String) Id of the allowed extensions.
														 

														
															- `install_link` (String) Install link for the allowed extensions.
														 

														
															- `name` (String) Name of the allowed extensions.
														 

														
															 
														 

														
															&lt;a id="nestedatt--app_settings--linux--settings--managed_bookmarks"&gt;&lt;/a&gt;
														 

														
															### Nested Schema for `app_settings.linux.settings.value_string`
														 

														
															 
														 

														
															Required:
														 

														
															 
														 

														
															- `name` (String) Name for the bookmark
														 

														
															- `url` (String) URL for the bookmark
														 

														
															 
															 
															 
														 

														
															&lt;a id="nestedatt--app_settings--macos"&gt;&lt;/a&gt;
														 

														
															### Nested Schema for `app_settings.macos`
														 

														
															 
														 

														
															Required:
														 

														
															 
														 

														
															- `category` (String) Defines the category of the setting.
														 

														
															- `settings` (Attributes List) A list of name value pairs for the settings. Please refer to the following [table](https://developer-docs.citrix.com/en-us/server-integration/global-app-configuration-service/getting-started#supported-settings-and-their-values-per-platform) for the supported settings name and their values per platform. (see [below for nested schema](#nestedatt--app_settings--macos--settings))
														 

														
															- `user_override` (Boolean) Defines if users can modify or change the value of as obtained settings from the Global App Citrix Workspace configuration service.
														 

														
															 
														 

														
															&lt;a id="nestedatt--app_settings--macos--settings"&gt;&lt;/a&gt;
														 

														
															### Nested Schema for `app_settings.macos.settings`
														 

														
															 
														 

														
															Required:
														 

														
															 
														 

														
															- `name` (String) Name of the setting.
														 

														
															 
														 

														
															Optional:
														 

														
															 
														 

														
															- `auto_launch_protocols_from_origins` (Attributes List) Specify a list of protocols that can launch an external application from the listed origins without prompting the user. (see [below for nested schema](#nestedatt--app_settings--macos--settings--auto_launch_protocols_from_origins))
														 

														
															- `enterprise_browser_sso` (Attributes) Enables Single Sign-on (SSO) for all the web and SaaS apps for the selected Operating System for the IdP domains added as long as the same IdP is used to sign in to the Citrix Workspace app and the relevant web or SaaS app. (see [below for nested schema](#nestedatt--app_settings--macos--settings--enterprise_browser_sso))
														 

														
															- `extension_install_allow_list` (Attributes List) Array of objects of type ExtensionInstallAllowlist. For example: {id:"extension_id1",name:"extension_name1",install link:"chrome store url for the extension"} (see [below for nested schema](#nestedatt--app_settings--macos--settings--extension_install_allow_list))
														 

														
															- `managed_bookmarks` (Attributes List) Array of objects of type ManagedBookmarks. For example: {name:"bookmark_name1",url:"bookmark_url1"} (see [below for nested schema](#nestedatt--app_settings--macos--settings--managed_bookmarks))
														 

														
															- `value_list` (List of String) List value (if any) associated with the setting.
														 

														
															- `value_string` (String) String value (if any) associated with the setting.
														 

														
															 
														 

														
															&lt;a id="nestedatt--app_settings--macos--settings--auto_launch_protocols_from_origins"&gt;&lt;/a&gt;
														 

														
															### Nested Schema for `app_settings.macos.settings.value_string`
														 

														
															 
														 

														
															Required:
														 

														
															 
														 

														
															- `protocol` (String) Auto launch protocol
														 

														
															 
														 

														
															Optional:
														 

														
															 
														 

														
															- `allowed_origins` (List of String) List of origins urls
														 

														
															 
														 

														
															&lt;a id="nestedatt--app_settings--macos--settings--enterprise_browser_sso"&gt;&lt;/a&gt;
														 

														
															### Nested Schema for `app_settings.macos.settings.value_string`
														 

														
															 
														 

														
															Required:
														 

														
															 
														 

														
															- `citrix_enterprise_browser_sso_domains` (List of String) List of IdP domains for which SSO is enabled.
														 

														
															- `citrix_enterprise_browser_sso_enabled` (Boolean) Enables Single Sign-on (SSO) for all the web and SaaS apps.
														 

														
															 
														 

														
															&lt;a id="nestedatt--app_settings--macos--settings--extension_install_allow_list"&gt;&lt;/a&gt;
														 

														
															### Nested Schema for `app_settings.macos.settings.value_string`
														 

														
															 
														 

														
															Required:
														 

														
															 
														 

														
															- `id` (String) Id of the allowed extensions.
														 

														
															- `install_link` (String) Install link for the allowed extensions.
														 

														
															- `name` (String) Name of the allowed extensions.
														 

														
															 
														 

														
															&lt;a id="nestedatt--app_settings--macos--settings--managed_bookmarks"&gt;&lt;/a&gt;
														 

														
															### Nested Schema for `app_settings.macos.settings.value_string`
														 

														
															 
														 

														
															Required:
														 

														
															 
														 

														
															- `name` (String) Name for the bookmark
														 

														
															- `url` (String) URL for the bookmark
														 

														
															 
															 
															 
														 

														
															&lt;a id="nestedatt--app_settings--windows"&gt;&lt;/a&gt;
														 

														
															### Nested Schema for `app_settings.windows`
														 

														
															 
														 

														
															Required:
														 

														
															 
														 

														
															- `category` (String) Defines the category of the setting.
														 

														
															- `settings` (Attributes List) A list of name value pairs for the settings. Please refer to [table](https://developer-docs.citrix.com/en-us/server-integration/global-app-configuration-service/getting-started#supported-settings-and-their-values-per-platform) for the supported settings name and their values per platform. (see [below for nested schema](#nestedatt--app_settings--windows--settings))
														 

														
															- `user_override` (Boolean) Defines if users can modify or change the value of as obtained settings from the Global App Citrix Workspace configuration service.
														 

														
															 
														 

														
															&lt;a id="nestedatt--app_settings--windows--settings"&gt;&lt;/a&gt;
														 

														
															### Nested Schema for `app_settings.windows.settings`
														 

														
															 
														 

														
															Required:
														 

														
															 
														 

														
															- `name` (String) Name of the setting.
														 

														
															 
														 

														
															Optional:
														 

														
															 
														 

														
															- `auto_launch_protocols_from_origins` (Attributes List) A list of protocols that can launch an external application from the listed origins without prompting the user. (see [below for nested schema](#nestedatt--app_settings--windows--settings--auto_launch_protocols_from_origins))
														 

														
															- `enterprise_browser_sso` (Attributes) Enables Single Sign-on (SSO) for all the web and SaaS apps for the selected Operating System for the IdP domains added as long as the same IdP is used to sign in to the Citrix Workspace app and the relevant web or SaaS app. (see [below for nested schema](#nestedatt--app_settings--windows--settings--enterprise_browser_sso))
														 

														
															- `extension_install_allow_list` (Attributes List) An allowed list of extensions that users can add to the Citrix Enterprise Browser. This list uses the Chrome Web Store. (see [below for nested schema](#nestedatt--app_settings--windows--settings--extension_install_allow_list))
														 

														
															- `local_app_allow_list` (Attributes List) List of App Object to allow list for Local App Discovery. (see [below for nested schema](#nestedatt--app_settings--windows--settings--local_app_allow_list))
														 

														
															- `managed_bookmarks` (Attributes List) A list of bookmarks to push to the Citrix Enterprise Browser. (see [below for nested schema](#nestedatt--app_settings--windows--settings--managed_bookmarks))
														 

														
															- `value_list` (List of String) List value (if any) associated with the setting.
														 

														
															- `value_string` (String) String value (if any) associated with the setting.
														 

														
															 
														 

														
															&lt;a id="nestedatt--app_settings--windows--settings--auto_launch_protocols_from_origins"&gt;&lt;/a&gt;
														 

														
															### Nested Schema for `app_settings.windows.settings.value_string`
														 

														
															 
														 

														
															Required:
														 

														
															 
														 

														
															- `protocol` (String) Auto launch protocol
														 

														
															 
														 

														
															Optional:
														 

														
															 
														 

														
															- `allowed_origins` (List of String) List of origins urls
														 

														
															 
														 

														
															&lt;a id="nestedatt--app_settings--windows--settings--enterprise_browser_sso"&gt;&lt;/a&gt;
														 

														
															### Nested Schema for `app_settings.windows.settings.value_string`
														 

														
															 
														 

														
															Required:
														 

														
															 
														 

														
															- `citrix_enterprise_browser_sso_domains` (List of String) List of IdP domains for which SSO is enabled.
														 

														
															- `citrix_enterprise_browser_sso_enabled` (Boolean) Enables Single Sign-on (SSO) for all the web and SaaS apps.
														 

														
															 
														 

														
															&lt;a id="nestedatt--app_settings--windows--settings--extension_install_allow_list"&gt;&lt;/a&gt;
														 

														
															### Nested Schema for `app_settings.windows.settings.value_string`
														 

														
															 
														 

														
															Required:
														 

														
															 
														 

														
															- `id` (String) Id of the allowed extensions.
														 

														
															- `install_link` (String) Install link for the allowed extensions.
														 

														
															- `name` (String) Name of the allowed extensions.
														 

														
															 
														 

														
															&lt;a id="nestedatt--app_settings--windows--settings--local_app_allow_list"&gt;&lt;/a&gt;
														 

														
															### Nested Schema for `app_settings.windows.settings.value_string`
														 

														
															 
														 

														
															Required:
														 

														
															 
														 

														
															- `arguments` (String) Arguments for Local App Discovery.
														 

														
															- `name` (String) Name for Local App Discovery.
														 

														
															- `path` (String) Path for Local App Discovery.
														 

														
															 
														 

														
															&lt;a id="nestedatt--app_settings--windows--settings--managed_bookmarks"&gt;&lt;/a&gt;
														 

														
															### Nested Schema for `app_settings.windows.settings.value_string`
														 

														
															 
														 

														
															Required:
														 

														
															 
														 

														
															- `name` (String) Name for the bookmark
														 

														
															- `url` (String) URL for the bookmark
														 

														
															 
														 

														
															## Import
														 

														
															 
														 

														
															Import is supported using the following syntax:
														 

														
															 
														 

														
															```shell
														 

														
															# Global App Configuration settings can be imported by specifying the service url
														 

														
															terraform import citrix_gac_settings.test_gac_settings https://example.com:443
														 

														
															```]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2025_03/cc-sp-1.png.0c18e625d097901a997458b4eff1afd6.png" length="155589" type="image/png"/><pubDate>Fri, 21 Mar 2025 13:51:00 +0000</pubDate></item><item><title>NetScaler Credential Protection: A Comprehensive Technical Analysis</title><link>https://community.stage.citrix.com/tech-zone/build/tech-papers/netscaler-credential-protection/</link><description><![CDATA[Executive Summary



	This paper provides a comprehensive technical analysis of credential protection within NetScaler environments. It examines the evolution from static encryption keys to the modern Key Encryption Key (KEK) system that automatically generates unique encryption materials for each deployment.
 


	NetScaler distinguishes between two credential types with different security requirements: recoverable service passwords that use AES-256-CBC encryption with KEK and non-recoverable administrator passwords that employ PBKDF2-HMAC-SHA256 hashing with multiple iterations and 32-byte salts.
 


	The technical architecture of KEK implementation is detailed, including file locations, derivation methods via HMAC-SHA256, and suffix mechanisms for key rotation. The paper analyzes real-world attack scenarios, demonstrating that while encrypted credentials remain secure when an attacker obtains only configuration files, root access represents the true security boundary.
 


	For security teams and administrators, this document offers technical validation methods, key rotation procedures, and best practices that align with modern cryptographic standards. 
 


	Organizations using current NetScaler firmware benefit from these protections by default, while those with legacy systems should upgrade to ensure credentials are not vulnerable to known default keys.
 


	
		Note:
	 

	
		 While every effort has been made to ensure accuracy, this content is based on independent analysis and practical experience rather than official documentation. Readers should validate critical security configurations against their requirements and consult with official NetScaler resources where available.
	 



	 
 


	Overview and Cryptographic Foundations



	A platform's security is improved when it is well-documented and understood. This paper provides a comprehensive technical analysis of credential protection within NetScaler environments, detailing reversible encryption mechanisms (for service passwords) and one-way hashing implementations (for administrator accounts). It's intended for security teams, cryptography enthusiasts, and NetScaler administrators. 
 


	"A cryptosystem should be secure even if everything about the system, except the key, is public knowledge." — Auguste Kerckhoffs (1883)
 


	"If you believe that keeping the algorithm's insides secret improves the security of your cryptosystem more than letting the academic community analyze it, you're wrong. And if you think that someone won't disassemble your code and reverse-engineer your algorithm, you're naïve." — Bruce Schneier, Applied Cryptography (1996)
 


	These principles emphasize that effective security relies on transparent, well-documented cryptographic methods and proper key management. By providing detailed information about these implementations, we enable security teams to evaluate and appropriately secure their environments through informed decision-making.
 


	 
 


	Credential Types and Security Requirements



	NetScaler makes a critical distinction between two types of credentials, each with different security requirements:
 


	
		Recoverable: Service accounts requiring NetScaler to authenticate externally (e.g., LDAP, RADIUS). These must be stored in a reversible (encrypted) format.
	
	
		Non-recoverable: Administrator passwords (e.g., nsroot) that are validated against a supplied credential but never need to be reversed. These should be hashed (one-way).
	



	Encryption vs. Hashing: Two Fundamental Approaches



	
		Encryption is reversible, using a secret key. NetScaler must decrypt service passwords at runtime to send them externally, so these credentials are securely encrypted with a Key Encryption Key (KEK).
	
	
		Hashing is one-way. Once hashed, you can't retrieve the original password. NetScaler must only validate admin logins by comparing hashes, making hashing a safer option.
	



	Encryption vs. Hashing Credential Workflows



	 
 


	
 


	Evolution of Credential Protection



	The security of credentials in NetScaler has evolved significantly over time. Understanding this evolution helps administrators identify potential vulnerabilities in their deployments and implement the strongest available protections.
 


	The Legacy Approach: Static Encryption Keys



	Historically, NetScaler shipped with factory default encryption keys. If you did not explicitly enable Key Encryption Key (KEK) functionality, you remained reliant on these defaults - meaning:
 


	
		Any NetScaler with the default key could decrypt your service passwords.
	
	
		Attackers or researchers possessing those keys (available in NetScaler binaries as the default) could unlock the passwords in any captured ns.conf file still using default encryption.
	



	NetScaler documentation, including my “Best Practices for Generic NetScaler Deployments” articles and the “NetScaler Secure Deployment Guide,” has long called for administrators to implement a KEK, but this critical step was often overlooked. This issue was so prevalent historically that from NetScaler 13.0–76.31, the KEK creation process was modified to no longer be an optional step. It was automatically and mandatorily applied at installation or upgrade. 
 


	Today, all current firmware releases include a KEK. Historically, however, if no KEK was present, the following keys were used:
 


	Pre-11.0 (RC4)
 


	
		The encryption method lacked ENCMTHD_X in ns.conf.
	
	
		Default key: 2286da6ca015bcd9b7259753c2a5fbc2 (an RC4 key).
	



	Post-11.0 (AES-256)
 


	
		ENCMTHD_2 or ENCMTHD_3 in ns.conf.
	
	
		Default AES key: 351cbe38f041320f22d990ad8365889c7de2fcccae5a1a8707e21e4adccd4ad9.
	



	These keys are not secret and are stored in the plaintext file /nsconfig/.skf on modern systems for backward compatibility with older config files.
 


	All customers on current firmware have automatically migrated to the KEK system. Only those on very old, out-of-date firmware might still rely on the universal static keys.
 


	The Modern Approach: Key Encryption Keys (KEK)



	NetScaler introduced the Key Encryption Key (KEK) system to end reliance on universal static keys. Under the KEK design, each NetScaler deployment has unique encryption materials and does not use a shared default key.
 


	Implementation Timeline:
 


	
		Pre-NetScaler 12.1: Default keys 
	
	
		NetScaler 12.1 – NetScaler 13.0-76.31: Default keys remained in use unless you explicitly and manually created a KEK as per my Best Practices for Generic Deployments article or the Netscaler Secure Deployment Guide.
	
	
		NetScaler 13.0-76.31 onwards: NetScaler firmware upgrades began enabling the KEK by default, automatically generating unique F1.key and F2.key files for new or upgraded deployments.
	



	If you're on current firmware, your NetScaler already uses unique KEK files by default.
 


	 
 


	Encryption Methods Technical Specifications



	The NetScaler KEK is intended for AES-256. However, this hasn’t always been the encryption method used. For recoverable passwords, NetScaler supports:
 


	
		ENCMTHD_NONE: No encryption - plaintext (insecure).
	
	
		ENCMTHD_1: RC4 (legacy encryption).
	
	
		ENCMTHD_2: AES-256-ECB.
	
	
		ENCMTHD_3: AES-256-CBC (modern standard).
	



	We recommend AES-256-CBC, which is the default. AES-256-CBC uses a random Initialization Vector (IV), so identical passwords produce different ciphertexts, protecting against frequency analysis and rainbow table attacks that might compromise ECB-mode encryption.
 


	Important Distinction: ECB vs CBC Mode



	AES-256-ECB (ENCMTHD_2) uses Electronic Code Book mode, which does not use an Initialization Vector (IV). This approach's security implication is that identical plaintext passwords will always produce identical ciphertext. This creates a potential security risk as pattern recognition becomes possible; if "secretpassword123" always encrypts to "XXYYZZ," an attacker could identify when multiple accounts use the same password by matching the identical encrypted strings.
 


	AES-256-CBC (ENCMTHD_3) (Recommended and the default) uses Cipher Block Chaining mode and employs a random Initialization Vector (IV) for each encryption operation. The IV is prepended to the ciphertext (the encrypted password) in the ns.conf file. This approach has the security benefit that identical passwords will produce different ciphertexts due to different random IVs. This prevents the pattern recognition and deterministic encryption vulnerabilities inherent in ECB-mode encryption.
 


	 
 


	
 


	 
 


	Modern KEK Implementation Details



	Key Files and Generation
 


	
		Key Files: Typically, .F1.key and .F2.key in /nsconfig/ or F1_[suffix].key files in /nsconfig/keys/. Each file is 256 bytes of ASCII hex (128 bytes binary).
	
	
		HMAC-SHA256: NetScaler extracts specific byte ranges (33–65 from F1, 35–67 from F2) and processes them via HMAC-SHA256 - one as the message, one as the key - forming the KEK.
	
	
		No Single KEK File: The KEK is never stored outright. It's reconstructed on-the-fly whenever credentials must be encrypted or decrypted.
	



	
		Note: 
	 

	
		In older versions, NetScaler key files were named 'F1.key' and 'F2.key', later key files were named '.F1.key' and '.F2.key' (with a leading period that causes BSD to hide the file), in both cases stored in /nsconfig/. In newer versions, which use suffixes, the format follows the pattern 'F1_[suffix].key' (e.g., 'F1_2025_01_08_12_34_56.key') stored in /nsconfig/keys/. These naming conventions and locations may be encountered depending on your NetScaler version and update history.
	 



	The benefit of splitting the key into multiple files is limited as these are stored in the same location. However, this approach is technically superior to storing a single key file because:
 


	
		The final KEK never exists on the disk
	
	
		An attacker needs both files to derive the key
	
	
		The computation adds an extra transformation layer
	



	Suffix Mechanism for Multiple Keys



	NetScaler 13.1 onwards supports multiple concurrent KEKs using timestamped suffixes (e.g., -suffix 2025_01_08_12_34_56). This allows staged rotation: older credentials still decrypt with their original suffix, while new credentials use the latest KEK files.
 


	Example Configuration Analysis
 


	A snippet from ns.conf might look like:
 

add authentication ldapAction ldapaction -serverIP 1.2.3.4 -serverPort 636 \

-ldapBase "&lt;dc=mycoolcompany,dc=local&gt;" \

-ldapBindDn "&lt;serviceaccount@mycoolcompany.local&gt;" \

-ldapBindDnPassword ed034210373b0044fae264c20382f00cb59506cb580c5645ef36db0b71d76ef1 \

-encrypted -encryptmethod ENCMTHD_3 -kek -suffix 2025_01_08_12_34_56


	Here, we can observe various KEK-related attributes:
 


	
		-kek → Uses the Key Encryption Key system.
	
	
		-encryptmethod ENCMTHD_3 → AES-256-CBC.
	
	
		-suffix 2025_01_08_12_34_56 → References the KEK files with that suffix.
	



	 
 


	Root Access Is the Real Security Boundary



	Having .F1.key, .F2.key (or their suffixed alternatives), and ns.conf means you can decrypt service credentials - by design. This is akin to possessing a web server's private key, letting you decrypt TLS traffic.
 


	NetScaler allows console (BSD shell) access only as root, meaning anyone with console rights can see all keys. This is also by design—if the device can decrypt credentials, so can anyone with root access. Therefore, root access represents the true security boundary in a NetScaler environment.
 


	The real security measures must focus on:
 


	
		Strictly limiting who has root-level privileges
	
	
		Enforcing multi-factor authentication for root access
	
	
		Physical security for console access
	
	
		Comprehensive logging and auditing of root-level activities
	
	
		Regular audits to ensure old keys or backups aren't exposed
	



	 
 


	Why Decrypting with a KEK Is Not a Security Flaw



	Some might ask, "Isn't it insecure that if you have .F1.key, .F2.key, and ns.conf, you can decrypt everything?" This is expected and necessary, as the system requires decryption capabilities to function properly.
 


	The system must decrypt credentials to send them to third-party services such as LDAP or RADIUS, so a local decryption capability is inherent. The security boundary is that only authorized users should ever have access to the key files.
 


	This architectural decision follows Kerckhoff's principle: security lies in protecting the keys, not in obscuring the mechanism. By understanding this reality, administrators can properly focus their security efforts on protecting root access rather than attempting to secure against theoretical attacks where an attacker has already obtained root.
 


	 
 


	Validating Password Security Without Disruption



	Some security teams want to confirm that a stored service credential (e.g., for LDAP) isn't "Password123." If you have explicit authorization to read it, you can replicate NetScaler's KEK logic using Python:
 

import hmac, hashlib, binascii

from Crypto.Cipher import AES

F1_bytes = binascii.unhexlify(open('/nsconfig/keys/F1_2025_01_08_12_34_56.key').read().strip())[33:65]

F2_bytes = binascii.unhexlify(open('/nsconfig/keys/F2_2025_01_08_12_34_56.key').read().strip())[35:67]

kek = hmac.new(F2_bytes, F1_bytes, hashlib.sha256).digest()

ciphertext = binascii.unhexlify('1bc44f282cb7fb2245394bcaa66ed5ab7c078112d20f74e5867b874527164557')

iv, data = ciphertext[:16], ciphertext[16:]

plaintext = AES.new(kek, AES.MODE_CBC, iv).decrypt(data).rstrip(b'\x00').decode()

print(plaintext)


	You will note that the IV for each encrypted password is prepended to the encrypted password within the ns.conf configuration file. 
 


	
		Note: 
	 

	
		Only decrypt passwords if your organizational policies allow direct inspection of stored credentials and you are authorized to do so. 
	 



	 
 


	Real-World Attack Scenarios



	Threat Modeling Context



	When analyzing potential threats to NetScaler environments, two primary attack vectors emerge:
 


	
		Inappropriate Configuration Disclosure: A significant historical security risk existed when pre-KEK NetScaler configuration files were shared in technical documentation, blog articles, or support forums. Before the KEK implementation era, administrators may have shared configurations without realizing the encrypted credentials could be readily decrypted using known static keys. This exposure risk persists in legacy documentation and configurations, particularly if those same credentials remain used across environments today.
	
	
		Service Credential Management: Even with NetScaler's robust security architecture, proper service credential management remains essential for overall environment security. The credentials stored within NetScaler, mainly LDAP bind credentials, can provide access to other platforms and facilitate lateral movement. Organizations should implement least-privilege principles for these accounts to mitigate potential security impact. When service accounts are assigned excessive permissions or permitted interactive login capabilities, or when credential reuse occurs between service accounts and administrative accounts, a single credential exposure could have cascading security implications beyond the NetScaler itself.
	



	These attack vectors highlight the importance of a defense-in-depth approach rather than relying solely on the encryption mechanisms detailed in this document.
 


	Scenario A: Attacker Obtains ns.conf But Not Key Files



	Technical Analysis:
 


	
		What They Have: The attacker finds a copy of ns.conf - perhaps from a misfiled backup or a configuration snippet posted on a forum.
	
	
		Missing Piece: They do not have the .F1.key and .F2.key files for your environment.
	
	
		Technical Details: Without the specific byte ranges from F1 and F2 KEK files, the attacker cannot derive the HMAC-SHA256 key needed to decrypt credentials. Even with massive computational resources, they cannot brute-force AES-256-CBC without the key.
	
	
		Outcome: They cannot decrypt the credentials, as NetScaler's KEK derivation is unique. The ciphertext remains useless.
	



	
		Warning: 
	 

	
		In rare cases, extremely old pre-KEK config snippets might still rely on the publicly known factory default keys (RC4 or AES). If so, an attacker can decrypt those credentials. Ensure you've upgraded firmware so KEK is automatically enabled.
	 



	Scenario B: Attacker Gains Full Root Console Access



	Technical Analysis:
 


	
		What They Have: Complete control at the BSD shell level.
	
	
		Key Exposure: Now they can view .F1.key, .F2.key, and ns.conf.
	
	
		Technical Process: Using standard cryptographic libraries, the attacker can extract the byte ranges from the key files, compute the HMAC-SHA256 to derive the KEK, and decrypt any AES-256-CBC encrypted credentials.
	
	
		Outcome: They can decrypt recoverable credentials at will. However, hashed admin passwords remain safer, especially if using PBKDF2 (Types 3-5). Even with root, an attacker must still crack any hashed credentials offline, which is computationally expensive due to PBKDF2's iteration count.
	



	Security Insight: This scenario highlights why access must be the primary security concern. It effectively bypasses all cryptographic protections for recoverable passwords, similar to an attacker being able to decrypt SSL/TLS sessions by obtaining the secret SSL certificate key file. In this scenario, you should change your passwords and rotate the KEK, at a minimum. 
 


	 
 


	KEK Rotation and Management



	Rotate KEKs periodically to ensure that future passwords won't remain vulnerable if a key is compromised. NetScaler offers:
 


	Basic Rotation:
 


	update system kek -level basic



	Generates new KEK files; old credentials keep using their prior suffix.
 


	Extended Rotation:
 


	update system kek -level extended



	Re-encrypts all credentials in ns.conf with the new KEK, updating suffix references. Briefly locks config changes.
 


	 
 


	KEK Rotation Workflow



	
 


	 
 


	Rotation Considerations and Best Practices



	Citrix does not provide official recommendations for Key Encryption Key rotation schedules. Organizations should instead consult with their security teams to establish appropriate rotation policies based on their specific risk profile and compliance requirements.
 


	However, administrators may find it helpful to reference industry frameworks such as NIST Special Publication 800-57 Part 1, Revision 5, which provides guidance on cryptographic key management. NIST SP 800-57 specifically addresses Key Encryption Keys, recommending: "If a relatively small number of keys are to be wrapped under a key-wrapping key, the originator-usage period of the key-wrapping key could be up to two years." Furthermore, NIST suggests that “a recipient-usage period extend no more than three years beyond the end of the originator-usage period.”
 


	When developing your organization's KEK rotation strategy, consider:
 


	
		Organizational security policies regarding cryptographic key management
	
	
		Regulatory compliance requirements applicable to your environment
	
	
		Operational factors such as change management windows and testing procedures
	
	
		Procedures for handling suspected security incidents involving credential exposure
	



	The extended rotation method (update system kek -level extended) provides the most comprehensive protection by re-encrypting all credentials with new key material.
 


	Additionally, NIST emphasizes that if a key is suspected of being compromised, 'its cryptoperiod shall no longer be considered valid,' meaning immediate rotation is required regardless of the scheduled timeline. In such scenarios, it's critical to not only rotate the KEK but also change all passwords protected by that KEK, as anyone with access to the KEK fragments will also have access to the encrypted credentials. This includes all service account passwords (LDAP, RADIUS, etc.) stored within the NetScaler configuration.
 


	HA Considerations: KEK rotation is automatically synchronized between nodes in a high-availability environment. The system handles all necessary replication, making the process transparent for administrators. All commands run on both nodes simultaneously, requiring no special handling.
 


	 
 


	Password Hashing



	Evolution of Password Hashing



	For administrator passwords (non-recoverable), NetScaler has also iterated through several hashing mechanisms:
 


	
		Type 1: SHA-1 (legacy)
	
	
		Type 2: SHA-512
	
	
		Type 3: PBKDF2-HMAC-SHA256 (100k iterations, 32-byte salt)
	
	
		Type 4: PBKDF2-HMAC-SHA256 (100k iterations, 32-byte salt, null-terminated password)
	
	
		Type 5: PBKDF2-HMAC-SHA256 (2,500 iterations, 32-byte salt, null-terminated password, current default)
	



	On 11.1 and older releases, NetScaler system user passwords were hashed using SHA1. NetScaler 12.0 used SHA512. NetScaler 13.1 introduced PBKDF2-HMAC-SHA256.
 


	Type 5 is the current default for NetScaler and provides strong protection through PBKDF2's key-stretching approach and large salt values. PBKDF2 remains a widely adopted enterprise standard for password hashing, effectively preventing brute-force attacks on hashed administrator credentials.
 


	 
 


	PBKDF2 Technical Deep Dive



	Password-Based Key Derivation Function 2 (PBKDF2) increases security through key-stretching by re-hashing inputs thousands of times:
 


	
		Iteration Count: Attackers must perform the same multi-iteration process for every guessed password, dramatically slowing brute-force attempts.
	
	
		Cryptographic Cost Analysis: Each PBKDF2 operation forces attackers to compute thousands of SHA-256 hashes, significantly increasing computational cost. A 2,500 iteration PBKDF2-HMAC-SHA256 is approximately 2,500 times more expensive to crack than a single SHA-256 hash on modern hardware.
	
	
		Salting: Each password is combined with a unique 32-byte salt (256 bits), thwarting pre-computed "rainbow tables" and ensuring identical passwords never produce identical hashes.
	
	
		Null-Termination in Type 4 and Type 5: Unlike earlier versions, Types 4 and 5 explicitly append a null byte (\x00) to the password before hashing. This means that the stored hash for a password like "password" will differ from one where the raw password is "password\x00".
	



	This key-stretching technique makes PBKDF2 significantly more resistant to brute-force attacks than traditional hash functions.
 


	
		Note: 
	 

	
		The PBKDF2 iteration count is not a security secret. Anyone with access to a NetScaler could create an account with a known password, extract the stored hash for that password, and use a simple Python script to determine the exact iteration count by incrementally testing values until finding a match, typically in under a second on modern hardware.
	 



	Practical Hash Verification



	You can verify hashed admin passwords offline using the following technical approach:
 


	
		Identify Type: The leading digit in the stored hash (e.g., 5) indicates which approach (type 1, 2, 3, 4, 5).
	
	
		Extract Salt: The salt follows immediately after the first character.
	
	
		Recompute: Run PBKDF2 with the same iteration count, extracted salt, and a null terminator (\x00) at the end of the password for Type 4 and Type 5 hashes, then compare the result to the stored hash.
	



	For example, consider this hash from my lab environment:
 

set system user nsroot 58edac9ebb2880a5a9d820344e95a2db4e55dbc5... -encrypted


	From the leading “5,” we know this is a Type 5 hash (PBKDF2-HMAC-SHA256 with 2,500 iterations). We can then split the characters following the “5” (e.g., “8eda…”) into the 64-hex-character salt and the hash and re-run PBKDF2-HMAC-SHA256 with 2,500 iterations to confirm.
 


	 
 


	Security Considerations and Best Practices



	
		NetScaler will use AES-256-CBC with KEK as a default for service passwords. 
	
	
		Old NetScaler instances with insecure defaults should be upgraded to a current supported release.
	
	
		Any credentials found to be encrypted with legacy encryption (RC4 or AES-256-ECB) within a NetScaler configuration file can be updated simply by changing the password and resaving the configuration file.
	
	
		Avoid using factory defaults and review the Best Practice for NetScaler Deployments and NetScaler Secure Deployment Guide.
	
	
		Rotate KEKs regularly (approximately every two years) in accordance with NIST SP 800-57 guidance, as detailed in the "KEK Rotation and Management" section. Implement rotation immediately if compromise is suspected.
	
	
		Protect KEK files (e.g., F1.key, F2.key, and their suffixed variants) in a similar manner to SSL private keys.
	
	
		Implement strict access controls and monitoring for root access.
	
	
		Consider a privileged access management (PAM) solution for controlling and auditing root access.
	



	 
 


	Compliance and Future Outlook



	Many organizations must adhere to regulatory frameworks with specific credential protection requirements. These frameworks typically require robust password storage mechanisms and strong encryption for sensitive credentials.
 


	While this paper does not claim compliance with any specific standard, the technical protections described, such as PBKDF2 for administrator credentials and AES-256-CBC with unique KEKs for service credentials, align with industry best practices for credential protection. Organizations should consult their compliance teams to determine if these mechanisms satisfy their regulatory requirements.
 


	Looking ahead, the NetScaler leading digit (e.g., 5) scheme of identifying hash types will likely make it relatively easy to evolve iteration counts further or adopt new KDFs (key derivation functions) in response to quantum advances without breaking existing setups.
 


	 
 


	Conclusion



	NetScaler's journey from factory default to unique KEK-based encryption highlights how crucial proper key management is for credential security. By assigning reversible credentials to AES-256-CBC with KEK and non-recoverable credentials to PBKDF2 hashing, NetScaler aligns with modern cryptographic best practices. NetScaler’s default configuration provides a robust defense against credential theft.
 


	 
 


	Appendix: Advanced Reference &amp; Legacy Details



	Below are additional technical elements for advanced readers or those troubleshooting older environments.
 


	A.1 Full KEK File Search Order



	When decrypting credentials, NetScaler looks for key files (suffix-based) in the following directories in order:
 


	
		/nsconfig/keys/updated/
	
	
		/nsconfig/keys/
	
	
		/var/nssynclog/keys/
	
	
		/nsconfig/keys/backup/
	
	
		/nsconfig/keys/cfgreplicate/
	



	If NetScaler cannot find the requested suffix in any of these paths, it will attempt to auto-recreate KEK fragments into /nsconfig/keys/
 


	A.2 Basic vs. Extended Rotation Details



	Basic Mode
 

update system kek -level basic


	Creates new KEK files but does not re-encrypt existing passwords. If you want a specific credential to use the new suffix, you must manually re-enter its password.
 


	Extended Mode
 

update system kek -level extended


	Creates new KEK files and re-encrypts every credential in ns.conf to that new KEK, blocking config changes until it finishes. When complete, everything uses the new suffix automatically.
 


	A.3 Legacy Hash Verification Examples (Type 1 &amp; 2)



	Type 1 (SHA-1)
 


	A stored password might look like:
 

set system user nsroot 18a7de5476aa011c6e70631c8f5bdb4c895a9800ea1e6c3d799190a7ec5b9923a5b -encrypted


	The first digit 1 is the type, meaning:
 


	1 &lt;salt=8bytes&gt; &lt;sha1=20bytes&gt;
 


	Python snippet for verifying:
 

python3 -c "

import hashlib, binascii

h = '18a7de5476aa011c6e70631c8f5bdb4c895a9800ea1e6c3d799190a7ec5b9923a5b'

salt = binascii.unhexlify(h[1:17])

test_pw = b'password\\x00'

calc_hash = hashlib.sha1(salt + test_pw).hexdigest()

print(calc_hash)

"


	Compare calc_hash to the 40-hex hash at the end of the hash.
 


	Type 2 (SHA-512)
 


	Similarly:
 

set system user nsroot 291ab0570cc137272a619b32fd9889273adad92c19c1055103abd9153de1beeac91b0dec8... -encrypted


	The first digit 2 is the type, meaning:
 


	2 &lt;salt=8bytes&gt; &lt;sha512=64bytes&gt;
 


	Python snippet:
 

python3 -c "

import hashlib, binascii

h = '291ab0570cc137272a619b32fd9889273adad92c19c1055103abd9153de1beeac...'

salt = binascii.unhexlify(h[1:17])

test_pw = b'admin\\x00'

calc_hash = hashlib.sha512(salt + test_pw).hexdigest()

print(calc_hash)

"


	If the result matches the last 128 hex chars of the string (after 2&lt;salt&gt;), the password "admin" is correct.
 


	A.4 /nsconfig/.skf for Old RC4 and AES Keys
 


	In modern NetScaler systems, the legacy default keys are stored in plain text at /nsconfig/.skf for backward compatibility. For instance:
 

root@NetScaler# cat /nsconfig/.skf 

2286da6ca015bcd9b7259753c2a5fbc2

351cbe38f041320f22d990ad8365889c7de2fcccae5a1a8707e21e4adccd4ad9


	
		The first line is the RC4 key for pre-11.0.
	
	
		The second line is the AES-256 key for post-11.0.
	



	On older systems, these keys were compiled directly into the platform binaries. Modern NetScaler systems store these in a file for backward compatibility with older configs. All current firmware builds will use the KEK as a default. 
 


	A.5 HA Notes
 


	In High-Availability deployments, all KEK files are automatically synchronized between nodes. This ensures that both nodes can decrypt any encrypted credentials in case of failover. The system handles this synchronization transparently, requiring no special administrative intervention.]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2025_03/image.png.2ec41fb01fbbcf630ea6d7c60deb2580.png" length="150427" type="image/png"/><pubDate>Thu, 13 Mar 2025 13:30:01 +0000</pubDate></item><item><title>Migrating Citrix DaaS from Single Tenant to Multitenant</title><link>https://community.stage.citrix.com/tech-zone/build/deployment-guides/csp-daas-single-multitenant-migration/</link><description>Introduction



	This document provides guidance for Citrix Service Providers (CSPs) to migrate a customer from an existing standalone single-tenant Citrix DaaS service to a CSP multitenant service. A CSP admin can follow the steps detailed in the document to complete the migration process.
 


	When a CSP transitions an existing single-tenant DaaS Customer to the partner&#x2019;s multitenant service for centralized management and greater economy of scale, a parallel deployment for the customer in the multitenant environment is needed before the final switchover.
 


	This migration process aims to retain the customer&#x2019;s existing custom Workspace URL and end-user experience while minimizing disruption. 
 


	Architecture



	In the diagram below, the purple-colored cloud instance indicates Customer A&#x2019;s pre-migration single tenant (or standalone) DaaS environment. After the migration (teal color), the same customer A will be sharing the CSP&#x2019;s multitenant service instance (middle frame) while still retaining its workspace experience (same custom workspace URL and VDAs).
 


	
 


	Summary of Steps



	Before October 1, 2024, migrating a customer from Single Tenant to Multitenant required Fast Decommission of the existing single tenant instance before multitenant DaaS can be added to the same customer (cloud account and Org ID) for the new tenant scope to be created. This caused concerns among our partners regarding downtime.
 


	From October 1, 2024, onboarding a new customer in Partner&#x2019;s Citrix Cloud console is for multitenant only and no longer needs an Org ID. This removes the requirement for a partner to re-use the tenant&#x2019;s existing Org ID/cloud account. The updated process is defined below, which no longer requires decommissioning the existing single-tenant instance so that disruption is minimized.
 


	
		
			
				
					Step
				 
			
			
				
					Task
				 
			
			
				
					Task Owner
				 
			
		
		
			
				1
			
			
				
					Create a new tenant for the customer via the partner cloud console that will share the CSP&#x2019;s Org ID.
				 
			
			
				
					Service Provider
				 
			
		
		
			
				2
			
			
				
					Add Service to the new tenant and select DaaS
				 
			
			
				
					Service Provider
				 
			
		
		
			
				3
			
			
				
					Configure the corresponding resource location in the partner account for the new tenant.
				 
			
			
				
					Service Provider
				 
			
		
		
			
				4
			
			
				
					Federate the tenant to the domain of the new resource location
				 
			
			
				
					Service Provider
				 
			
		
		
			
				5
			
			
				
					Create Machine Catalogues with customer scope
				 
			
			
				
					Service Provider
				 
			
		
		
			
				6
			
			
				
					Create a Delivery Group with assigned tenant AD group(s)
				 
			
			
				
					Service Provider
				 
			
		
		
			
				7
			
			
				
					Add test/pilot users to the relevant AD group
				 
			
			
				
					Service Provider and Customer
				 
			
		
		
			
				8
			
			
				
					Configure temporary tenant workspace URL
				 
			
			
				
					Service Provider and Customer
				 
			
		
		
			
				9
			
			
				
					UAT testing
				 
			
			
				
					Service Provider and Customer
				 
			
		
		
			
				10
			
			
				
					Schedule production service cutover to multitenant
				 
			
			
				
					Service Provider and Customer
				 
			
		
		
			
				11
			
			
				
					Production cutover
				 
			
			
				
					Service Provider
				 
			
		
		
			
				12
			
			
				
					Post-migration testing
				 
			
			
				
					Service Provider and Customer
				 
			
		
		
			
				13
			
			
				
					Decommission old single-tenant instance (optional) and delink the account
				 
			
			
				
					Service Provider
				 
			
		
	



	Migration Process



	CSP Creates the New Tenant



	The CSP partner admin must create a new cloud tenant with the customer&#x2019;s name but under the partner&#x2019;s Org ID. To do so, the partner admin can log in to the CSP cloud account and follow the steps defined in the product documentation to &#x201C;Add&#x201D; the customer. By default, when the new tenant account is created for the customer, the CSP admin will automatically be added as an admin in the tenant cloud account to ensure the partner admin has sufficient permission to manage the tenant services.
 


	
 


	
 


	Add Service to the New Tenant



	After creating the new tenant, the partner admin needs to add this customer to its multitenant DaaS Service. This process will automatically generate the tenant scope in Studio for the subsequent security isolations.
 


	
 


	
 


	
		Note: 
	 

	
		If the partner admin is not able to perform &#x201C;Add Service&#x201D; to the new tenant, please first verify if the logged-in admin has the necessary permissions (e.g., a full admin in the tenant&#x2019;s account).
	 



	Configure Corresponding Resource Location for the New Tenant



	Resource locations of all tenants under CSP&#x2019;s multitenant (MT) DaaS must be configured in the partner account.
 


	Since the same customer has an existing single-tenant DaaS in a separate cloud account, the new corresponding dedicated Resource Location in the CSP partner Cloud account will be connected to the same environment and Active Directory that the customer&#x2019;s existing single tenant is currently using.
 


	Install a minimum of two Cloud Connectors to the new resource location for the tenant customer.
 


	
		Note: 
	 

	
		Do not reuse the existing customer&#x2019;s cloud connectors even if they are in the same Active Directory domain. The new cloud connectors need to be reinstalled to connect to the partner&#x2019;s cloud account.
	 



	Federate the Customer Tenant to the Domain of the New Resource Location



	The important step for configuring a customer under multitenant DaaS is to federate the tenant to the domain configured in the partner account. Without this step, the tenant-level authentication to access the resources and FAS multitenant support would not function.
 


	
 


	 
 


	
 


	Follow the product documentation to add the tenant to the newly configured domain in the partner account. Once completed, log in to the tenant cloud account and verify that you can see a domain appear via the federation, although you never configured one in the new tenant&#x2019;s own account.
 


	
 


	
 


	Create Machine Catalogues



	You can reuse the customer Operating System images from the existing single-tenant environment and create new machine catalogs with the new tenant scope under the partner&#x2019;s multitenant service that match the customer&#x2019;s existing catalogs of its single-tenant service.
 


	
		
			
				
					Customer&#x2019;s Existing Catalog under Standalone Single Tenant Service
				 
			
			
				
					Customer&#x2019;s New Catalog with assigned tenant scope under CSP Multitenant Service
				 
			
		
		
			
				
					Catalog 1
				 
			
			
				
					Catalog 1
				 
			
		
		
			
				
					Catalog 2
				 
			
			
				
					Catalog 2
				 
			
		
		
			
				
					Catalog 3
				 
			
			
				
					Catalog 3
				 
			
		
		
			
				
					&#x2026;
				 
			
			
				
					&#x2026;
				 
			
		
	



	As best practice, please use a naming convention in a multitenant environment to identify and separate objects belonging to different tenants. Defining a new Hosting connection may be necessary before creating the machine catalogs if the new tenant workloads are hosted in different infrastructure resources.
 


	Create Delivery Group(s)



	Like machine catalogs, create the mapping delivery groups with tenant scope under the partner&#x2019;s multitenant service as per the customer&#x2019;s existing single tenant instance. 
 


	As best practice: 
 


	
		Delivery groups under CSP multitenant service should always be dedicated to a tenant customer and assigned with the tenant scope.
	
	
		Instead of assigning individual users to the delivery group for user access, assign AD security group(s) to the delivery group and manage individual user access via the AD group memberships. This provides flexibility, separating the Studio access for managing delivery groups and user AD group membership.
	



	Add Test/Pilot Users



	Create necessary test or pilot users in the customer&#x2019;s Active Directory and add them to the relevant security group assigned to a scoped delivery group(s) to match the user personas in the customer&#x2019;s existing single-tenant environment.
 


	Configure Temporary Tenant Workspace URL



	CSP multitenant DaaS provides each tenant with their own workspace experience with separate workspace URL(s) per tenant. Before the customer&#x2019;s existing single-tenant workspace URL can be transitioned over, the new deployment for the same customer under multi-tenant DaaS needs to be tested.
 


	Define a temporary Workspace URL for user acceptance testing in the new tenant account.
 


	UAT Testing



	Thoroughly test the user experience (session launch, access to applications and desktops, etc.) with different personas, compared with their existing experience under single tenant DaaS, until the result is accepted by the customer.
 


	Schedule Production Service Cutover



	Once user acceptance testing is completed, the service provider and the customer must schedule the maintenance window for service cutover to minimize disruption to users&#x2019; access and experience.
 


	Production Cutover



	During the scheduled maintenance window, user access to the existing single-tenant instance should be blocked. The production Workspace URL will be moved from the single-tenant account to the multi-tenant account.
 


	Post Migration Testing



	Additional testing should be conducted after the production Workspace URL has been transferred to the multitenant account; if applicable, necessary DNS records should be updated. Verify that user sessions via the same production Workspace URL are now launched from the multitenant environment.
 


	Decommission Old Single Tenant Instance and Delink the Account



	The customer&#x2019;s old single-tenant environment is now obsolete. A few options the service provider can choose from:
 


	
		If the CSP licenses in that account are not expired, they can be transferred back to the partner or canceled (via customer service/support case).
	
	
		Remove the partner link and admin rights from the single tenant account.
	
	
		(Optional) self-service decommission of the single tenant instance to release back-end resources.
	



	References



	
		Reference Architecture: Citrix Service Provider DaaS
	
	
		Citrix Cloud for Partners
	
	
		Citrix DaaS for Citrix Service Providers</description><enclosure url="https://media.invisioncic.com/r328636/monthly_2025_03/image.png.681838ece1fd11336107898d6a5fe71e.png" length="199117" type="image/png"/><pubDate>Tue, 11 Mar 2025 16:59:00 +0000</pubDate></item><item><title>Citrix Federated Authentication Service</title><link>https://community.stage.citrix.com/tech-zone/learn/poc-guides/citrix-fas/</link><description><![CDATA[POC Guide - Citrix FAS.pdf
 


	Overview



	Citrix Federated Authentication Service (FAS) is a privileged component designed to integrate with Active Directory Certificate Services. It dynamically issues certificates for users, allowing them to log on to an Active Directory environment as if they had a smart card.
 


	This allows StoreFront to use a broader range of authentication options, such as Security Assertion Markup Language (SAML) assertions. SAML is commonly used as an alternative to traditional Windows user accounts on the Internet.
 


	This proof-of-concept guide outlines the steps to install and configure Citrix Federated Authentication Service. The following concepts are covered in this guide:
 


	
		Installation of Citrix Federated Authentication Service
	
	
		StoreFront and Delivery Controller configuration
	
	
		Group Policy Configuration
	
	
		Citrix Federated Authentication Service configuration
	



	Prerequisites



	The following prerequisites are required for this proof-of-concept deployment:
 


	
		Windows Server 2022 Standard or Datacenter, or later
	
	
		A Microsoft Enterprise Certificate Authority or other validated Certificate Authority is required within your domain environment.
	
	
		Citrix Virtual Apps and Desktops components (Delivery Controllers, Virtual Delivery Agents, and StoreFront) must all be on supported versions.
	
	
		Citrix Virtual Apps and Desktops ISO or standalone Citrix Federated Authentication Service MSI installer.
	



	Install Citrix Federated Authentication Service



	
		Connect to the Windows Server dedicated to Citrix Federated Authentication Service and mount the Citrix Virtual Apps and Desktops ISO.
	
	
		Double-click AutoSelect.
	



	
 


	
		Click Start for Virtual Apps and Desktops.
	



	
 


	
		Click Federated Authentication Service.
	



	
 


	
		Select "I have read, understand, and accept the terms of the license agreement, click Next.
	



	
 


	
		Click Next.
	



	
 


	
		Verify Automatically is selected and click Next.
	



	
 


	
		Review the summary page, click Install.
	



	
 


	
		When the installation is completed successfully, click Finish.
	



	
 


	Configure StoreFront and Delivery Controller



	Citrix StoreFront and the Citrix Delivery Controller require additional configuration if you deploy Citrix Federated Authentication Service with Citrix Virtual Apps and Desktops. These steps are unnecessary if you are deploying Citrix FAS with Citrix Cloud.
 


	
		Connect to your StoreFront server, open PowerShell, and run the following:
	



	$store = Get-STFStoreService -VirtualPath [e.g. /Citrix/store]
 


	$auth = Get-STFAuthenticationService -StoreService $store
 


	Set-STFStoreLaunchOptions -StoreService $store -VdaLogonDataProvider "FASLogonDataProvider"
 


	Set-STFClaimsFactoryNames -AuthenticationService $auth -ClaimsFactoryName "FASClaimsFactory"
 


	 
 


	
		Run this command to ensure that if FAS fails users can sign on to the VDA by entering their credentials:
	



	$storeService = Get-STFStoreService -VirtualPath [VirtualPath]
 


	Set-STFStoreLaunchOptions $storeService -FederatedAuthenticationServiceFailover $True
 


	
		Connect to your Delivery Controller, and run the following PowerShell command to enable a trust between your Delivery Controller and StoreFront servers:
	



	Set-BrokerSite -TrustRequestsSentToTheXmlServicePort $true
 


	Configure Group Policy



	
		Connect to your Citrix Federated Authentication Service server and open the following location in File Explorer: C:\Program Files\Citrix\Federated Authentication Service\PolicyDefinitions
	



	
 


	
		Copy the contents of this folder and paste them into your Domain Controllers C:\Windows\PolicyDefinitions and en-US subfolder.
	



	
 


	
		Connect to your Active Directory server and open Group Policy Management Editor.
	



	
 


	
		Right-click on Default Domain Policy and select Edit.
	



	
 


	
		Navigate to the Federated Authentication Service policy in Computer Configuration/Policies/Administrative Templates/Citrix Components/Authentication and open the Federated Authentication Server policy.
	



	
 


	
		Select Enabled and click Show.
	



	
 


	
		Enter the FQDN of your Citrix Federated Authentication Service server and click OK.
	



	
 


	
		Click OK.
	



	
 


	
		The Federated Authentication Service policy should now be enabled.
	



	
 


	
		Open the In-session Behavior policy.
	



	
 


	
		Select Enabled, per-process in the Prompt Scope drop-down menu, enter 30 in Consent timeout, select Disconnect on lock, then click OK.
	



	
 


	
		The In-session Behavior policy is now Enabled.
	



	
 


	
		Exit the Group Policy Management Editor
	



	Configure FAS Server



	
		Connect to your Citrix Federated Authentication Service server.
	
	
		Launch Citrix Federated Authentication Services from the Windows Start menu.
	



	
 


	
		Click Deploy.
	



	
 


	
		Review the Deploy Certificate Templates message and click OK.
	



	
 


	
		Click Publish.
	



	
 


	
		Validate that your Certificate Authority is listed, click OK.
	



	
 


	
		Click Authorize.
	



	
 


	
		Validate that your Certificate Authority is listed, click OK.
	



	
 


	
		The request is sent to the Certificate Authority.
	



	
 


	
		Connect to your Certificate Authority server and open the Certificate Authority Console.
	



	
 


	
		Select Pending Requests. The request from the Citrix Federated Authentication Service is visible.
	



	
 


	
		Right-click on the request and select All Tasks &gt; Issue.
	



	
 


	
		Return to your Citrix Federated Authentication Service server. If the request has been authorized, the Reauthorize button will appear.
	



	
 


	
		Click Create.
	



	
 


	
		Leave Create the default rule (recommended) selected, and click Next.
	



	
 


	
		Select Citrix_SmartcardLogon (recommended) and Filter templates (recommended), click Next.
	



	
 


	
		Select your Certificate Authority and Only show CAs publishing Citrix_SmartcardLogon, click Next.
	



	
 


	
		(Optional) Select Allow in-session use, and click Next.
	



	
 


	
		Click Manage StoreFront access permissions.
	



	
 


	
		Add your StoreFront server, unselect Deny, select Allow, and click OK.
	



	
 


	
		Click Next.
	



	
 


	
		Click Next.
	



	
 


	
		Review the Summary page and click Create.
	



	
 


	
		The Citrix Federated Authentication Service configuration is now complete.
	



	Validation



	
		Connect to your Citrix Federated Authentication Service server and open a PowerShell window.
	
	
		Add the Federated Authentication Service snap-in.
	



	
		    Add-PSSnapin Citrix.Authentication.FederatedAuthenticationService.V1
	 



	
 


	 
 


	
		Check the Federated Authentication Service server connectivity.
	



	
		    Get-FasServer
	 



	
 


	 
 


	
		Check the certificate template status.
	



	
		    Get-FasCertificateDefinition -Address &lt;yourFASserverFQDN&gt;
	 



	
 


	 
 


	
		Test the user certificate process.
	



	
		New-FASUserCertificate -Address &lt;yourFASserverFQDN&gt; -UserPrincipalName &lt;UPNofEndUser&gt; -CertificateDefinition &lt;RuleName&gt;_definition -rule &lt;RuleName&gt;
	 



	
 


	
		Citrix Federated Authentication Service is working properly.
	



	Summary



	This POC Guide outlines the steps to install and configure the Citrix Federated Authentication Service, verify server connectivity, check the certificate template status, and test the user certificate process.
 


	For additional information, optional settings, and FAQs, reference the Citrix Federated Authentication Service product documentation.]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2025_02/image.png.38a066d23c515f59bb982fa01b94b8ce.png" length="49646" type="image/png"/><pubDate>Fri, 28 Feb 2025 13:31:00 +0000</pubDate></item><item><title>Citrix Cheat Sheet - DISA STIGs in Citrix Virtual Apps and Desktops Environments</title><link>https://community.stage.citrix.com/tech-zone/design/diagrams-posters/cvad-disa-stigs/</link><description>Citrix Cheat Sheets - DISA STIG.pdf
 


	The Citrix Cheat Sheet - DISA STIGs in Citrix Virtual Apps and Desktop environments points out some of the standard STIG settings that might be giving you trouble, as well as a few pointers and tips. If you have already finished implementing STIGs, this is a good list to lead you to a setting that might be breaking your environment. If you haven&#x2019;t implemented STIG settings yet, cheat sheet will give you a good head start on what to look for and may save you some time troubleshooting.</description><enclosure url="https://media.invisioncic.com/r328636/monthly_2025_02/CitrixCheatSheet-DISASTIG.png.5536498097245dfdf9ed87337c4f8db9.png" length="112153" type="image/png"/><pubDate>Wed, 26 Feb 2025 14:37:19 +0000</pubDate></item><item><title>Implementing DISA STIGs for Citrix Environments</title><link>https://community.stage.citrix.com/tech-zone/build/tech-papers/citrix-disa-stigs/</link><description><![CDATA[Overview



	
		This tech paper is designed to provide the most up-to-date configurations and exemptions that we have tested. This is meant to be a helpful guide and NOT a definitive solution for STIG implementation or a guarantee to pass an audit. We plan to update this tech document frequently with the release of new products, DISA STIGs, and lessons learned from the field. 
	 



	If you’re a US Government DoD IT engineer, then you’re undoubtedly familiar with the Secure Technical Implementation Guides (STIGs) produced by the US Defense Information Systems Agency (DISA). These guidelines dictate security measures that must be applied to various IT systems to secure them in accordance with US Government standards. While this is great for enhancing security, it can often lead to frustrating situations as STIGs may break expected vendor software functionality. It has been a few years since we have provided updated guidance on STIG integration with Citrix technologies, and a lot has changed since then, including major Citrix product updates and new Operating Systems. This tech paper aims to help you overcome these obstacles and complete your ATO process much quicker. 
 


	This Tech Paper will cover the steps to implement STIGs for all Citrix Virtual Apps and Desktops components. The STIGS covered will be in the order as they appear in the STIG viewer.  If you are following the application, this is their order. 
 


	Test environment: 
 


	
		This Tech Paper is based on a customer deployment of Citrix Virtual Apps and Desktops 2402 Long Term Service Release (LTSR) on Windows Server 2022 and existing Virtual Delivery Agents (VDAs) (both on-premises and public cloud) using Windows 2019 and 2016 servers with external access as well as secure air-gapped environments. 
	
	
		Functionality required the enumeration, common access card (CAC) authentication and launch of resources.
	
	
		The environment underwent a security audit, which included a security content automation protocol (SCAP) scan and manual checks, and passed. 
	
	
		The components were installed, and STIGs added while the servers had antivirus and DLP disabled as a recommended practice. 
	
	
		Additional troubleshooting issues were discovered during deployment that apply to DoD environments that were not related to STIG settings. These findings are included in the articles.
	



	Caveats: 
 


	
		This Tech Paper covers Citrix Virtual App and Desktop LTSR components and required roles such as IIS, SQL, Active Directory, and DNS. Baseline Windows server STIGs are not included in this article. 
	
	
		Citrix Director does not have STIG requirements outside of IIS. The same IIS settings used for Citrix StoreFront were applied to the Director servers and will be tested on Citrix Web Studio in the future. 
	
	
		The current STIG settings for Citrix License server do not reflect in the current License Server Management Console. The article on License Server STIG settings tries to meet the intent of the STIG requirements as best as possible. 
	
	
		This is meant to be a living tech paper, so check back for updates with new LTSRs and published STIG settings.
	
	
		All environments are different, and you may come across issues unique to your environment or desired user experience. There is no guarantee that all of these will apply to your environment as other security settings, applications, or requirements might conflict with STIG settings we have not come across yet.
	
	
		These guides are meant to aid in implementing STIGs but are not a replacement for following the checklist in STIG viewer. 
	



	IIS 10.0 - StoreFront and Director:



	This section details the implementation of StoreFront and IIS 10.0 STIG settings. Director does not have explicit server STIGs, but the same IIS settings apply from StoreFront. The vulnerability, CAT rating, and any rationale or documentation will be included from our testing. Keep in mind that some of the STIG settings did not apply to this environment, and some require discussion with your internal security team for documentation on disaster recovery and security policy. These guides are meant to aid in implementing STIGs but are not a replacement for following the checklist in STIG viewer. 
 


	StoreFront:



	There are only two STIG settings for the actual StoreFront server:
 


	
		V-234252 (CAT II): This setting requires that StoreFront accept Personal Identity Verification (PIV) credentials. On the StoreFront console, select the “Store Node.” Under “Manage Authentication Methods,” make sure that only “Smart Card” is selected for each store. If the store is configured for Smart Card authentication, “Pass-through from NetScaler Gateway” can also be selected.
	
	
		V-234251 (CAT I): This setting requires DoD-approved encryption for remote access sessions. If remote access is used, an approved VPN or gateway/proxy solution like NetScaler must be used. 
	



	IIS 10.0:



	The settings that need to be implemented for IIS 10.0 can be divided into two groups: one that impacts the server settings and the other that impacts the IIS site settings.
 


	IIS Server Settings
 


	
		V-218785 (CAT II): This requires enhanced logging to be enabled and capture all user web server events. Go to IIS Manager 🡪 Server name 🡪 “Logging” 🡪 Format 🡪 “W3C” 🡪 “Select Fields” and verify that the minimum fields are checked: Date, Time, Client IP Address, User Name, Method, URI Query, Protocol Status, and Referrer.
	
	
		V-218786 (CAT II): Log file and Event Tracing for Windows must be enabled. Go to IIS Manager 🡪 Server Name 🡪 click “Logging” 🡪 and verify the “Both log file and ETW event” radio button is selected.
	
	
		V-218787 (CAT II): A web server behind a load balancer or proxy server has to produce log records with the source client IP and destination. If using a proxy server, configure the settings to pass web traffic to the web server transparently. 
	
	
		V-218788 and V-218789 (CAT II): The web server has to produce log records with success or failure records. Go to Server Manager 🡪 Server Name 🡪 “Logging” 🡪 verify that “Format” under “Log File” is configured to “W3C” 🡪 select “Custom Fields” 🡪 verify that “Request Header” &gt;&gt; Connection, “Request Header” &gt;&gt; Warning, “Request Header” &gt;&gt;  Authorization and Content-Type have been configured. Under “Fields” go to “Standard Fields” 🡪 verify “User Agent”, “User Name”, and “Referrer” are selected.
	
	
		V-218790 (CAT II): Log information on the web server has to be protected from unauthorized modification or deletion. In Server Manager, under the Server Name, click “Logging” 🡪 “Browse” 🡪 navigate to the directory storing the log files 🡪 right-click and open “Properties” 🡪 “Security” 🡪 verify the log file access is restricted to “SYSTEM – Full Control” and “Administrators – Full Control”
	
	
		V-218791 (CAT II): Log data records from the web server must be backed up onto a different system. Consult internally with administrators to determine the storage path.
	
	
		V-218792 (CAT II): The Web Server cannot manage users for hosted applications. In this case, StoreFront handles application user management. Please refer to the Citrix product documentation for more information. 
	
	
		V-218793 (CAT II): The server can only contain functions needed to operate. This setting is vague, but essentially check the “Programs and Features” on the server and ensure that there is no unnecessary additional software on the server.
	
	
		V-218794 (CAT II): The web server cannot be used for web and proxy services. 
	
	
		V-218795 (CAT I): Any web server sample code, example applications and tutorials must be removed from the following directories: \inetpub, \Program Files\Common Files\msadc, \Program Files (x86)\Common Files\System\msadc
	
	
		V-218796 (CAT II): Ensure that no local user accounts have been created by features or software that have been uninstalled. 
	
	
		V-218797 (CAT II): Regular reviews should be used to remove any features, utilities, or plug-ins that are not being used. Consult a local administrator.
	
	
		V-218798 (CAT II): Under the “Server Name”, select “MIME Types” 🡪 “Group by:” 🡪 “Content Type” 🡪 “Application”🡪 ensure that the MIME Types .exe, .dll, .com, .bat, .csh, have all been removed.
	
	
		V-218799 (CAT II): Under “Server Name”, make sure that there is no “WebAV Authoring Rules” Icon. If so, remove.
	
	
		V-218800 (CAT II): Under “Server Name” 🡪 “Server Certificate” 🡪 double-click each certificate and ensure the path is to a DoD root CA.
	
	
		V-218801 (CAT II): This setting requires that any files with .java or .jpp extensions be removed.
	
	
		V-218802 (CAT I): Check all user accounts with access to the server and ensure that only essential administrators have access. 
	
	
		V-218803 (CAT II): The web server must operate the hosted applications from hosted web service functionality. This does not apply if the server is running Exchange.
	
	
		V-218804 (CAT II): In IIS, under “Server Name”, click “ASP.Net” 🡪 “Session State” 🡪 “Cookie Settings” 🡪 verify that “Mode” has “Use Cookies” selected.
	
	
		V-218805 (CAT II): In IIS, under “Server Name”, click “ASP.Net” 🡪 “Session State” 🡪 “Cookie Settings” 🡪 verify that “Mode” has “Use Cookies” selected 🡪 under “Time-out (in minutes)”, verify a maximum of 20 minutes is entered.
	
	
		V-218806 (CAT II): Consult a  System Administrator about the patch and update policy and ensure only authorized users have access and that there is a disaster recovery plan in place. 
	
	
		V-218807 (CAT II): In IIS, under “Server Name”, click “Machine-Key”🡪 verify that “HMACSHA256” or stronger encryption is selected for the validation method and “Auto” is selected for the encryption method.
	
	
		V-218808 (CAT II): In IIS, under “Server Name”, click “Directory Browsing” and under the “Actions” pane ensure that “Directory Browsing” is disabled.
	
	
		V-218809 (CAT II): In the Registry Editor, navigate to HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\ContentIndex\Catalogs\. Validate that only the web document directories are being indexed.
	
	
		V-218810 (CAT II): In IIS, under “Server Name”, click “Error Pages” icon 🡪 “Edit Feature Setting” from the “Actions” pane. Ensure that the feature setting is set to "Detailed errors for local requests and custom error pages for remote requests", or "Custom error pages".
	
	
		V-218812 (CAT II): This setting only applies if Web Management Service is installed. If it is, “Enable Remote connections” must be enabled. 
	
	
		V-218813 (CAT II): Verify with an administrator that there are written procedures for shutting down IIS in the event of an attack. 
	
	
		V-218814(CAT II): In File Explorer, navigate to “inetpub” 🡪 “Properties”🡪 “Security” and verify the permissions for the following users are at least this restrictive: 
	



	
 


	
		V-218815 (CAT II): Under “Logging”, determine the directory where “W3C” logging is being written. Confirm with an Administrator that the location is of sufficient size and that “Do not create new log files” is not selected under “Log File Rollover”.
	
	
		V-218816 (CAT II): Review with an Administrator that only web server staff can access the web manager under “InetMgr.Exe”.
	
	
		V-218817 (CAT II): Under “Control Panel” 🡪 “Programs and Features” 🡪 Only the following programs can be installed without additional documentation: 
	



	
 


	
		V-218818 (CAT II): Internet Printing Protocol (IPP) cannot be enabled.  The %windir%\web\printers directory should not exist. On the “Start” menu, click “Administrative Tools” 🡪 “Server Manger” 🡪 right-click “Print Services” 🡪 select “Remove Roles Services”.
	
	
		V-218819 (CAT II): If hosting any applications, obtain documentation on the configuration. In Registry Editor, verify that, at a minimum, the following tuning settings are in the registry:
	



	
 


	
		V-218820 (CAT II): In IIS, select the “Server Name”, under the “Management” section, open “Configuration Editor” 🡪 “Section:”🡪 “system.webServer/asp”🡪 expand “session” section and verify that “keepSessionIdSecure” is set to “True”.
	
	
		V-218821 (CAT I): In Registry Editor navigate to the following keys and verify that the registry paths exist and have the correct values: 
	



	 
 


	
		V-218822 (CAT II):  Consult an Administrator and make sure that the TLS version is 1.2 or higher. 
	
	
		V-218823 (CAT II): Ensure that all Local Users have passwords assigned and default passwords changed.
	
	
		V-218824 (CAT II): In IIS select the “Server Name”🡪 “ISAPI and CGI Restrictions” icon 🡪 “Edit Feature Settings” 🡪 verify that “Allow unspecified CGI modules” and “Allow unspecified ISAPI modules” check boxes are NOT checked.
	
	
		V-218825 (CAT II): If ASP.NET is not installed, SharePoint, WSUS, Exchange are hosted, or the server is public facing this does not apply. Otherwise, open IIS and under the “Server Name” select the “.NET Authorization Rules” icon and ensure “All Users” is set to “Allow” and “Anonymous Users” is set to “Deny”.
	
	
		V-218826 (CAT II): In IIS under “Server Name”, select the “Configuration Editor” under the “Management” section. From the “Section:” drop-down menu, locate “system.applicationHost/sites” 🡪 expand “siteDefaults” and “limits”. Verify that the “maxconnections” parameter is set to zero. 
	
	
		V-218827 (CAT III): In IIS under “Server Name”, select the “Configuration Editor” under the “Management” section. From the “Section:” drop-down menu, locate “system.applicationHost/sites” 🡪 expand “siteDefaults” and “HSTS”. Set “enabled”, “includeSubDomains”, and “redirectHttpToHttps”to “True”. Set “max-age” to a value greater than “31536000”.
	
	
		V-228572 (CAT II): If the Web Server is running SMTP relay services, there needs to be documentation on configuration and hardening.
	
	
		V-241788 (CAT III): Open Registry Editor and navigate to: “HKLM\System\CurrentControlSet\Services\HTTP\Parameters”. Verify that “DisableServerHeader” is set to “1”.
	
	
		V-241789 (CAT III): To remove ASP.NET versioning from the HTTP Response Header, open IIS and select IIS 10.0 web server under the “Connections Pane”. Click on the “HTTP Response Headers” button and remove the “X-Powered-By” HTTP header if it exists. 
	



	IIS 10.0 Site Settings



	
		V-218735 (CAT II): In IIS, select the “site name” 🡪 “ASP.NET” 🡪 “Session State”🡪 “Session State Mode Settings” and verify that it is set to “In Process” mode.
	
	
		V-218736 (CAT II): In IIS, select the “site name” 🡪 “ASP.NET” 🡪 “Session State”🡪 “Session State Mode Settings”🡪 “Cookie Settings” and verify that the “Use Cookies” mode is selected.
	
	
		V-218737 and V-218738 (CAT II): Both settings require the website to accept SSL connections and authentication. On the Default Website: “Require SSL” cannot be checked and “Client Certificates” must be set to ignore. Please refer to Citrix product documentation for rationale.
	
	
		V-218739 (CAT II): Log file and Event Tracing for Windows must be enabled. Go to IIS Manager 🡪 “Site Name” 🡪 click “Logging” 🡪 verify the “Both log file and ETW event” radio button is selected.
	
	
		V-218740 (CAT II): A web server behind a load balancer or proxy server has to produce log records with the source client IP and destination. If using a proxy server, configure the settings to pass web traffic to the web server transparently. 
	
	
		V-218741 and V-218742 (CAT II): The web server has to produce log records with success or failure records. Go to Server Manager 🡪 Server Name 🡪 “Logging” 🡪 verify that “Format” under “Log File” is configured to “W3C” 🡪 select “Custom Fields” 🡪 verify that “Request Header” &gt;&gt; Connection, “Request Header” &gt;&gt; Warning, “Request Header” &gt;&gt;  Authorization and Content-Type have been configured. Under “Fields” go to “Standard Fields” 🡪 verify “User Agent”, “User Name”, and “Referrer” are selected.
	
	
		V-218743 (CAT II): Under the “Site Name”, select “MIME Types” 🡪 “Group by:” 🡪 “Content Type” 🡪 “Application”🡪 ensure that the MIME Types .exe, .dll, .com, .bat, .csh, have all been removed.
	
	
		V-218744 (CAT II): In IIS, open the “Site Name” and double-click “Handler-Mappings”. Black-listed file extensions based on the organization’s security policy should be disabled.
	
	
		V-218745 (CAT II): In IIS, open the “Site Name” and double-click “Request Filtering” and select the File Name Extensions Tab. Black-listed file extensions based on the organization’s security policy should be disabled.
	
	
		V-218746(CAT II): Under “Site Name”, make sure that there is no “WebAV Authoring Rules” Icon. If so, remove.
	
	
		V-218748 (CAT II): The web server has to be configured to listen on a specified IP address and port. The hostname entry can be configured, but configuring the IP address will break StoreFront/Director functionality. Please see Citrix product documentation for more information.
	
	
		V-218749 (CAT II): This rule requires that web authentication must use client certificates to transmit session identifiers. If checked, the "Clients Certificates Required" option breaks smart card authentication for StoreFront. Please see Citrix product documentation for additional Plan of Action and Milestones (POA&amp;M) references.
	
	
		V-218750 (CAT I): For each site, click the “Authentication” Icon and disable “Anonymous Access”. 
	
	
		V-218751 (CAT II): In IIS, select the “site name” 🡪 “ASP.NET” 🡪 “Session State”🡪 “Session State Mode Settings” and verify that it is set to “In Process” mode.
	
	
		V-218752 (CAT II): Ensure the IIS directory location is moved to a separate drive from the operating system. Please see Citrix product documentation for installing StoreFront on a non-system drive. 
	
	
		V-218753 (CAT II): In IIS, select the “site name” 🡪 “Request Filtering” 🡪 “Edit Feature Settings” in the “Actions” pane 🡪 verify the “maxUrl” value is set to “4096” or less.
	
	
		V-218754 (CAT II): In IIS, select the “site name” 🡪 “Request Filtering” 🡪 “Edit Feature Settings” in the “Actions” pane 🡪 verify the “maxAllowedContentLength” value is set to 30000000 or less. 
	
	
		V-218755 (CAT II): In IIS, select the “site name” 🡪 “Request Filtering” 🡪 “Edit Feature Settings” in the “Actions” pane 🡪 verify the “Maximum Query String” value is set to 2048” or less.
	
	
		V-218756 (CAT II): In IIS, select the “site name” 🡪 “Request Filtering” 🡪 “Edit Feature Settings” in the “Actions” pane 🡪 Uncheck the “Allow high-bit characters” checkbox.
	
	
		V-218757 (CAT II): In IIS, select the “site name” 🡪 “Request Filtering” 🡪 “Edit Feature Settings” in the “Actions” pane 🡪 Uncheck the “Allow double escaping” checkbox.
	
	
		V-218758 (CAT II): In IIS, select the “site name” 🡪 “Request Filtering” 🡪 “Edit Feature Settings” in the “Actions” pane 🡪 Uncheck the “Allow unlisted file name extensions” checkbox.
	
	
		V-218759 (CAT II):  Under the “site name”, double-click the “Directory Browsing” icon. Verify “Directory Browsing” is disabled under the “Actions” pane. 
	
	
		V-218760 (CAT II): Under the “site name”, double-click the “Error Pages” icon. Verify that the feature setting is set to "Detailed errors for local requests and custom error pages for remote requests", or "Custom error pages".
	
	
		V-218761 (CAT II):  Under the “site name”, double-click the “.NET Compilation” icon. Scroll down to “Behavior” and verify that the value for “Debug” is set to “False”.
	
	
		V-218762 (CAT II): For each site, open “Application Pools”. Highlight the Pool to view the “Advanced Settings” and scroll to “Process Model” Verify the value for “Idle Time-out is not set to “0”.
	
	
		V-218763 (CAT II): Under the “Site Name”, select “Configuration Editor” under the “Management” section. From the drop-down menu, select “system.web/sessionState”. Verify the “timeout is set to “00:15:00” or less. 
	
	
		V-218764 (CAT II): Verify with the relevant administrator that there is a documented process for shutting down IIS in the event of an attack.
	
	
		V-218765 (CAT II): Under “Logging”, determine the directory where “W3C” logging is being written. Confirm with an Administrator that the location is of sufficient size and that “Do not create new log files” is not selected under “Log File Rollover”.
	
	
		V-218766 (CAT II): Verify that no unknown ports or protocols are being used.
	
	
		V-218767 (CAT II): Under “Site name”, click on “Bindings” in the “Action” pane 🡪 “HTTPS Type” 🡪 “Edit” 🡪 “View” 🡪 verify that the CAs in the trust hierarchy lead to DoD PKI Root CAs.
	
	
		V-218768 (CAT II): This rule requires that TLS encryption be employed. The only setting that does not break functionality is setting the ssFlags to ssl128. All other required settings will break authentication. 
	
	
		V-218769 (CAT II): Under the “site name” 🡪 “Management”🡪 “Configuration Editor” 🡪 drop-down list 🡪 “system.webServer/asp”🡪 “session” 🡪 verify that “keepSessionIdSecure” is set to “True”. 
	
	
		V-218770 (CAT II): Under the “site name” 🡪 “Management”🡪 “Configuration Editor” 🡪 drop-down list 🡪 “system.web/httpCookies”🡪 verify the “require SSL is set to “True”. Go back to the drop-down menu and select “system.web/sessionState” Verify “compressionEnabled” is set to “False”.
	
	
		V-218771 (CAT II): In “Application Pools”, review the “Applications” column and verify that there are unique pools for each website.
	
	
		V-218772 (CAT II):  This setting requires that the maximum number of requests an application pool can process must be explicitly set for each application pool. If the request value is set to a value other than zero it breaks StoreFront functionality as the user timeouts are set on the StoreFront console.
	
	
		V-218775 (CAT II): In “Application Pools” highlight a pool and click “Recycling” in the “Actions” pane. Verify that at least one condition is checked (“Regular Time Intervals”, “Scheduled Time” etc.)
	
	
		V-218777 (CAT II): In “Application Pools” highlight a pool and click “Advanced Settings” in the “Actions” pane. Find “Rapid Fail Protection” and verify the value for “Enabled” is set to “True”.
	
	
		V-218777 (CAT II): In “Application Pools” highlight a pool and click “Advanced Settings” in the “Actions” pane. Find “Rapid Fail Protection” and verify the value for “Failure Interval” is set to “5”.
	
	
		V-218779 (CAT II): Right-click on the “site name” and select “Explore”. Look for script extensions (.c, .php, .asp, etc) and ensure they are in unique designated folders separate from web content.
	
	
		V-218780 (CAT II): This setting is only applicable if using Common Gateway Interface (CGI).
	
	
		V-218781 (CAT II): This setting is only applicable if using CGI.
	
	
		V-218782 (CAT II): This only applies to private DoD websites. If you are using NetScaler Gateway it must be configured on the gateway. A warning banner must be configured on StoreFront and Director homepages. To implement the banner for StoreFront: 
	
	
		Under inetpub\wwwroot\Citrix\StoreWeb\custom (where Store is your Store name) open the script.js file. You will see a number of commented out lines of code which can be safely removed and replaced with something similar to the following shortened version of the DoD warning banner:
	



	
		var doneClickThrough = false;
	 

	
		CTXS.Extensions.beforeLogon = function (callback) {
	 

	
		doneClickThrough = true;
	 

	
		CTXS.ExtensionAPI.showMessage({
	 

	
		messageTitle: "DoD Logon Banner",
	 

	
		messageText:　　"&amp;lt;div class='logonBanner'&amp;gt;I've read &amp;amp; consent to terms in IS user agreem't.&amp;lt;/div&amp;gt;",
	 

	
		okButtonText: "Accept",
	 

	
		okAction: callback });
	 

	
		};
	 



	
		For Director, under  inetpub\wwwroot\Citrix\Director open the Logon.ASPX folder in notepad. Scroll to the bottom and add the following text:
	



	 
 


	
 


	Additional Notes



	
		These settings must be applied to each StoreFront and Director server individually.
	
	
		Additionally, utilize SSL_Bridge load balancing when using smart card authentication at StoreFront. 
	
	
		Make sure Loopback Communication is Off on all StoreFront Stores.
	
	
		If using Windows 2022 Server OS for multi-session VDAs, ensure that TLS 1.3 is unchecked on the certificate binding in IIS.
	
	
		If you have problems authenticating at StoreFront, ensure that Client Certificates Required under SSL Settings has not accidentally been enabled for your StoreFront Stores. Only the Default Web Site and the StoreAuth\Certificates folder under each Store should have this enabled.
	



	Delivery Controller



	The following settings must be configured in every Delivery Controller in the environment.
 


	
		V-234565 (CAT I): To verify, open the Registry Editor on each Delivery Controller and find the following key name: HKEY_LOCAL_MACHINE\SOFTWARE\Citrix\DesktopServer 
	
	
		Verify that the XmlServicesSslPort registry key exists with the correct value for SSL port. By default, it is set to "443".
	
	
		Verify XmlServicesEnableNonSsl is set to "0".
	
	
		Verify the corresponding registry value to ignore HTTPS traffic, XmlServicesEnableSsl, is not set to "0".
	
	
		If "XmlServicesSslPort" is not set to the desired port, this is a finding.
	
	
		If "XmlServicesEnableNonSsl" is not set to "0", this is a finding.
	
	
		If XmlServicesEnableSsl is not set to "1", this is a finding.
	
	
		To verify the FIPS Cipher Suites used:
	
	
		From the Group Policy Management Console, go to Computer Configuration &gt;&gt; Administrative Templates &gt;&gt; Networks &gt;&gt; SSL Configuration Settings.
	
	
		Double-click "SSL Cipher Suite Order" and verify the "Enabled" option is checked. 
	
	
		Verify the correct Cipher Suites are listed in the proper order per current DoD guidelines.
	
	
		V-234567 (CAT II): The Citrix Customer Experience Improvement Program (CEIP) must be disabled. In Studio, select “Configuration” 🡪 “Support” and verify that CEIP is disabled. 
	
	
		V-234569 (CATII): Configure the following computer configurations policies: Windows Settings🡪 Security Settings🡪 Local Policies/User Rights Assignment. Verify “Allow log on locally” and “Shut down the system” are set to the global security group name for Citrix Virtual App and Desktop administrators.
	



	License Server



	Once you start implementing STIGs on the License Server, you will realize that the instructions do not match the License Server Management console in its current version (version 490000 was tested for this paper). We have included workarounds and additional documentation to help meet the STIG requirements.
 


	
		V-234222 (CAT I): License Servers require two certificates, one for the server and one for the connection between the License Management Console and Citrix Studio. Once the Licensing service has been stopped on the server, we can use the steps in the product documentation to add certificates and secure traffic on the License server.
	
	
		V-234223 (CAT II): Log in to the License Server Management console. In the top right corner, select Administration 🡪 Settings 🡪 Accounts. Make sure that these user accounts should have administrative rights on the server. 
	
	
		V-234224 (CAT II): This setting also requires appropriate certificates. This can be accomplished by using the above solution for V-234222.
	
	
		V-234225 (CAT II): The STIG does not match the License server version - session timeout is not able to be set through the license server console. 
	
	
		V-234226 (CAT II): License Servers require two certificates, one for the server, and one for the connection between the License Management Console and Studio. Once Licensing service has been stopped on the server, we can use the steps found in this article to add certificates and secure traffic on the License server: Get started, install, and configure the License Server
	
	
		V-234227 (CAT II): License Servers require two certificates, one for the server, and one for the connection between the License Management Console and Studio. Once Licensing service has been stopped on the server, we can use the steps found in this article to add certificates and secure traffic on the License server:Get started, install, and configure the License Server
	
	
		V-234228 (CAT II): License Servers require two certificates, one for the server, and one for the connection between the License Management Console and Studio. Once Licensing service has been stopped on the server, we can use the steps found in this article to add certificates and secure traffic on the License server: Get started, install, and configure the License Server
	



	Virtual Delivery Agent



	The following settings apply to the Citrix Virtual Delivery Agent (VDA). This section is broken up by operating system. Further guidance will be added around Linux operating systems.
 


	Windows



	
		V-234253 (CAT I): This setting states that remote users must use a DoD-approved VPN or gateway/proxy to access Windows VDAs. 
	
	
		V-234254 (CAT II): Unnecessary logical and physical ports should be disabled. While some organizations prefer not to use port 80 to connect VDAs to Delivery Controllers, it does not pose a major security risk as the VDA only sends out a heartbeat broadcast and is encrypted using WCF. 
	



	Citrix Workspace app



	The following setting applies to the Citrix Workspace app across all deployment platforms (Windows, macOS, Linux, etc.).
 


	
		V-234262 (CAT II): Citrix Workspace app is required to accept Personal Identity Verification credentials (PIV). In Group Policy, verify that the policy value under Administrative Templates 🡪 Citrix Components 🡪 Citrix Workspace 🡪 User Authentication 🡪 “Smart card authentication” is set to “Enabled”
	



	Summary



	Throughout this Tech Paper, we aim to provide the most up-to-date information of STIG settings and exemptions found in our test and customer environments. This is meant to be a helpful tool and not a definitive guide to STIG implementation in your particular environment. Below you can find links to additional security resources, as well as a quick troubleshooting guide with our most commonly seen issues in STIG implementations.
 


	Resources



	
		Citrix Cheat Sheet - DISA STIGs
	
	
		Tech Paper: Citrix VDA Operating System Hardening Guide
	
	
		Tech Paper: Security best practices for Citrix Virtual Apps and Desktops
	
	
		Tech Paper: Improving ICA File Security
	
	
		Tech Paper: Managing Timeout Settings Across Citrix Components for Optimized User Experience and Security
	
	
		Security considerations and best practices | Citrix Virtual Apps and Desktops 7 2402
	



	Disclaimer



	EXCEPT WHERE EXPRESSLY PROVIDED OTHERWISE BY CLOUD SOFTWARE GROUP, THE CONTENT IN THIS DOCUMENT IS PROVIDED “AS IS” AND CLOUD SOFTWARE GROUP HEREBY DISCLAIMS ALL EXPRESS OR IMPLIED REPRESENTATIONS, WARRANTIES, GUARANTIES, AND CONDITIONS, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES OR CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD TO BE LEGALLY INVALID. CLOUD SOFTWARE GROUP MAKES NO REPRESENTATIONS, WARRANTIES, GUARANTIES, OR CONDITIONS AS TO THE QUALITY, SUITABILITY, TRUTH, ACCURACY, OR COMPLETENESS OF ANY OF THE CONTENT CONTAINED ON THE WEBSITE.
 


	The above disclaimer applies to any damages, liability, or injuries caused by any failure of performance, error, omission, interruption, deletion, defect, delay in operation or transmission, computer virus, communication line failure, theft or destruction of or unauthorized access to, alteration of, or use, whether for breach of contract, tort, negligence or any other cause of action.]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2025_02/image.png.d0d7b354549de75f9c4cb030929b8640.png" length="15618" type="image/png"/><pubDate>Wed, 26 Feb 2025 14:37:09 +0000</pubDate></item><item><title>POC Guide: Citrix Session Remote Start</title><link>https://community.stage.citrix.com/tech-zone/learn/poc-guides/citrix-session-remote-start/</link><description><![CDATA[POC Guide - Citrix Session Remote Start.pdf



	Overview



	Session Remote Start offers APIs that allow trusted third-party services to enumerate, launch, and log off Citrix sessions. It enables unattended logins triggered by events such as building badge scans and reduces delays in time-intensive environments. Optional login scripts can disconnect sessions after logon, keeping them available for users to reconnect as needed.
 


	With seamless integration into existing Citrix components, Session Remote Start streamlines deployment enhances user experience and redefines how businesses manage virtual access, boosting overall productivity.
 


	This proof-of-concept guide outlines the steps to install and configure Citrix Session Remote Start. It does not provide instructions for creating API-based applications to start Citrix sessions remotely. The following is covered in this guide:
 


	
		Installation and configuration of Citrix Session Remote Start
	
	
		Configuration of REST API credentials
	
	
		Citrix StoreFront and Delivery Controller configuration
	
	
		Configuration of Citrix Session Remote Start Group Policy
	
	
		Telemetry configuration
	
	
		Enumeration and Launch tests
	



	
		Note:
	 

	
		Citrix Session Remote Start is currently in Tech Preview. This POC Guide will be updated accordingly during the Tech Preview period and when the feature becomes generally available.
	 



	Prerequisites



	The following prerequisites are required to use Citrix Session Remote Start:
 


	
		Windows Server 2019 or a later version.

		
			
				4 vCPU
			
			
				16 GB RAM
			
			
				Microsoft AD or Entra ID hybrid-joined.
			
		
	
	
		Citrix Session Remote Start downloaded to the above Windows Server
	
	
		Citrix StoreFront 2203 or a later version.
		
			
				StoreFront must be configured to use https
			
			
				A new dedicated store created for Session Remote Start users
			
		
	
	
		Citrix Federated Authentication Service (FAS) 2203 or later version
	
	
		SSL Certificate created for the Citrix Session Remote Start server
	



	Citrix Session Remote Start Installation



	
		Connect to your Citrix Session Remote Start server and extract the Citrix Session Remote Start zip file previously downloaded to the desktop.
	



	
 


	
		Open the folder Citrix Session Remote Start folder on your desktop and double-click Citrix.Srs.Installer_x64.msi.
	



	
 


	
		Click Next.
	



	
 


	
		Accept the license terms, click Next.
	



	
 


	
		Click Next.
	



	
 


	
		Click Install.
	



	
 


	
		The Citrix Session Remote Start installation begins.
	



	
 


	
		When the installation completes, click Finish.
	



	
 


	SSL Certificate



	
		Open IIS Manager, select the Session Remote Start Server name, open the Default Web Site, and click Bindings.
	



	
 


	
		Click Add.
	



	
 


	
		In the Add Site Binding screen, add and select the following, then click OK.
	



	
		Select the type as https.
	
	
		Enter the IP address and hostname of Session Remote.
	
	
		Set https port to 443.
	
	
		Select Require Server Name Indication
	
	
		Select the SSL certificate for the Session Remote Server
	



	
 


	
		Click Close.
	



	
 


	
		Select the Session Remote Start Site and double-click SSL Settings.
	



	
 


	
		Click Require SSL, then click Apply.
	



	
 


	Configure Session Remote Start



	Session Remote Start configuration parameters are stored in the Web.config file found under the Session Remote Start installation directory ('C:\Program Files\Citrix\SessionRemoteStart\' by default).
 


	
		Browse to 'C:\Program Files\Citrix\SessionRemoteStart.
	



	
 


	
		Open the web.config file in Notepad.
	



	
 


	
		Add your Web URL of the store explicitly created for Session Remote Start to the following setting: &lt;add key="StoreFrontServer" value=""/&gt;.
	



	
 


	
		Add the FQDN of Session Remote Start server to the following setting:  &lt;add key="LocalFqdn" value="".
	



	
 


	
		Save and close the web.config file.
	



	Configure REST API Credential



	
		Connect to your Session Remote Start server as an administrator and open a PowerShell window.
	



	
 


	
		Change the directory to the SessionRemoteStart on the desktop.
	



	
 


	
		Run the create-cred.ps1 script, provide the username and password and click Enter.
	



	
 


	
		Enter the credential target name, username, and password, and click Enter.
	



	
 


	
		Open IIS and go to Application Pools.
	



	
 


	
		Right-click SrsAppPool and select Advanced Settings.
	



	
 


	
		Under Edit Application Pool, scroll down to Process Model &gt; Identity and click the ellipsis (the button with the three dots.
	



	
 


	
		Select Custom Account and click OK.
	



	
 


	
		Enter the username and password created in Step 3 and click OK.
	



	
 


	
		Click OK.
	



	
 


	
		The identity field should now display the account you created in Step 3. Click OK.
	



	
 


	
		Navigate to the %windir%/system32/inetsrv/config folder and open the applicationHost.config file.
	



	
 


	
		Locate the &lt;system.applicationHost&gt;&lt;applicationPools&gt;&lt;SrsAppPool&gt;&lt;processModel&gt; element and confirm that the setProfileEnvironment attribute is not present.
	



	
 


	
		Restart IIS.
	



	Configure StoreFront and Delivery Controller



	
		Connect to your StoreFront server and open the Citrix StoreFront Management Console.
	



	
 


	
		Click Stores and select the store setup for Session Remote Start, click Manage Citrix Gateways in the Stores panel on the right.
	



	
 


	
		Click Add.
	



	
 


	
		Set Display name, set Citrix Gateway URL with https://&lt;Session Remote Start FQDN&gt;/SessionRemoteStart/, and change Usage or role to Authentication only. Click Next.
	



	
 


	
		Set Callback URL with https://&lt;Session Remote Start FQDN&gt;/SessionRemoteStart. Click Create.
	



	
 


	
		The Gateway is added. Click Finish.
	



	
 


	
		Click Close.
	



	
 


	
		Click Manage Authentication Methods.
	



	
 


	
		Unselect User name and password, then select Pass-through from Citrix Gateway.
	



	
 


	
		Expand settings. Click Configure Delegated Authentication.
	



	
 


	
		Select Fully delegate credential validation to Citrix Gateway. Click OK.
	



	
 


	
		Click OK.
	



	
 


	
		Click Configure Remote Access Settings.
	



	
 


	
		Select Enable Remote Access, select the Gateway just configured, and click OK.
	



	
 


	
		Open File Explorer, go to the website directory of the main store users use for Citrix access: C:\inetpub\wwwroot\Citrix\%StoreName%\. Create a new folder called Backup.
	



	
 


	
		Open the bin folder and copy the StoreCustomization_Input.dll and StoreCustomization_Enumeration.dll to the Backup folder you just created.
	



	
 


	
		Copy the StoreFront Plugin folder from your Citrix Session Remote Start server to the desktop of your Citrix StoreFront server.
	



	
 


	
		Open the StoreFront Plugin folder on your StoreFront server.
	



	
 


	
		Copy the three dll files within this folder to: C:\inetpub\wwwroot\Citrix\&lt;StoreName&gt;\bin, ensuring the store is the main store used by users. Make sure to Replace the files in the destination.
	



	
 


	 
 


	
		Open System.
	



	
 


	
		Click the Advanced system settings link.
	



	
 


	
		Click Environment Variables.
	



	
 


	
		Add New… in the System variables section. In the New System Variable window, add the Variable Name: srs_server_urls and Variable value: https://&lt;YourSessionRemoteStartServer&gt;/SessionRemoteStart. 
	



	
 


	
		Click OK. Close all remaining windows by clicking OK.
	



	
 


	
		Restart the IIS service on the StoreFront server.
	
	
		Connect to your Delivery Controller. Open PowerShell and run the command:
	



	asnp Citrix* 
 


	Set-BrokerSite -TrustRequestsSentToTheXmlServicePort $true
 


	
 


	Configure Citrix Session Remote Start Group Policy



	
		Copy the DisconnectSession.ps1 file from your C:\Users\administrator.WORKSPACES\Desktop\AD folder to a file share or Active Directory server.
	



	
 


	
		Connect to your Active Directory Server and open the Group Policy Management console.
	



	
 


	
		Right-click your domain, select Create a GPO in this domain, and Link it here…
	



	
 


	
		Give your GPO a name, and click OK.
	



	
 


	
		Right-click the new Group Policy and select Edit.
	



	
 


	
		Expand User Configuration &gt; Policies &gt; Windows Settings &gt; Scripts (Logon/Logoff). Right-click Logon in the right panel and select Properties.
	



	
 


	
		Select the PowerShell scripts tab.
	



	
 


	
		Click Add.
	



	
 


	
		Click Browse.
	



	
 


	
		Browse to where you save the DisconnectSession.ps1 file. Select DisconnectSession.ps1 and click Open.
	



	
 


	
		Click OK.
	



	
 


	
		Select Run Windows PowerShell scripts last and click OK.
	



	
 


	Configure Telemetry



	The following steps are required to make the IIS Application Pool have permission to collect performance monitor data.
 


	
		On the Session Remote Start server, open and select System Tools &gt; Local Users and Groups &gt; Groups. Then right-click Performance Monitor Users on the right panel and select Add to Group….
	



	
 


	
		Click Add.
	



	
 


	 
 


	
		Click Locations.
	



	
 


	
		Select the Session Remote Start server, click OK.
	



	
 


	
		Type  IIS AppPool\SrsAppPool, click Check Names, then click OK.
	



	
 


	
		Click OK.
	



	
 


	
		Open PowerShell, and run the following command: iisreset
	



	
 


	Enumeration Test



	
		Open a browser and connect to https://&lt;SessionRemoteStartFQDN&gt;/SessionRemoteStart/index.html. The following page should appear:
	



	
 


	
		Enter login for a test or known user and click Enumerate Resources.
	



	
 


	
		Available resources for the user should now be listed on the page.
	



	
 


	
		Open Citrix Web Studio, browse to an appropriate Machine Catalog, and confirm no sessions exist.
	



	
 


	
		 Return to the enumeration test page and select the launch button for one of the enumerated applications or desktops. The launch complete message will appear:
	



	
 


	5. Return to Citrix Web Studio and refresh.  The application or desktop you launched will now show in a Prelogon state.
 


	
 


	
		Citrix Session Remote Start has been configured correctly.
	



	
 


	Summary



	This guide walked you through the steps to deploy and configure Citrix Session Remote Start. This consisted of installing and configuring the Citrix Session Remote Start components and Citrix StoreFront, Citrix Delivery Controllers, Citrix Federated Authentication Service, Active Directory, and Group Policy.  Once configured, the deployment was tested using the Citrix Session Remote Start provided test scripts. 
 


	For additional information, optional settings, and FAQs, reference the Citrix Session Remote Start documentation.]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2025_02/image.png.b6b67feb6e9807e397c09831e4543ee4.png" length="595710" type="image/png"/><pubDate>Fri, 14 Feb 2025 13:00:00 +0000</pubDate></item><item><title>Citrix NetScaler Cheat Sheet</title><link>https://community.stage.citrix.com/tech-zone/design/diagrams-posters/netscaler-cheat-sheet/</link><description>Overview



	This cheat sheet for Citrix NetScaler provides a comprehensive list of commands and their functions for system status, service management, network configuration, high availability, authentication, SSL certificates, backup, traffic analysis, connectivity testing, and system resources. Key commands and functions include:
 


	System Status: Commands to check system uptime, CPU, memory, SSL utilization, hardware, firmware, licenses, current time, operating modes, and feature status. 
 


	Service Management: Commands for viewing vServer statistics, backend status, and service group details. 
 


	Network: Commands to display interface status, VLAN configuration, IP configuration, ARP table, and routing table. 
 


	Configuration: Commands to show current configuration, save configuration, and show changed configuration. 
 


	High Availability: Commands to check node sync status, force sync, and trigger failover. 
 


	Authentication: Commands to change passwords, view live authentication data, and show current AAA users/sessions. 
 


	SSL Certificates: Commands to manage SSL certificates, certificate chains, SSL profiles, and cipher groups. 
 


	Backup: Commands to show, create, and restore system backups. 
 


	Traffic Analysis / Connectivity Testing: Commands for DNS lookup tests, HTTP(S) connection tests, viewing current connections, access control lists, live network traffic, and creating/stopping traces. 
 


	System Resource: Commands for memory allocation and policy hits. 
 


	Technical Support: Command to create a technical support bundle. 
 


	Management Traffic: Command to use a different router for management traffic. 
 


	
 


	
 


	Download the document here:  NetScalerCheatSheet_v07.pdf</description><enclosure url="https://media.invisioncic.com/r328636/monthly_2025_01/NetScalerCheatSheet_v07-1.png.c8f90329b511bb0fb49a30486fe2eb06.png" length="556022" type="image/png"/><pubDate>Thu, 30 Jan 2025 16:00:00 +0000</pubDate></item><item><title>Managing Timeout Settings Across Citrix Components for Optimized User Experience and Security</title><link>https://community.stage.citrix.com/tech-zone/build/tech-papers/manage-timeout-settings/</link><description><![CDATA[Overview



	This article provides a comprehensive guide for End User Computing (EUC) administrators on configuring and managing timeout settings across Citrix components, including NetScaler, StoreFront, Citrix Identity Platform, and third-party Identity Providers (IDPs). Since multiple components manage session and timeout policies independently, configuring these settings optimally is crucial for delivering a secure, seamless user experience.
 


	Admins can leverage Citrix's timeout settings to ensure consistency across cloud and on-premises environments. This document explains how to use the available settings to control session behavior, enforce reauthentication policies, and configure timeouts for both internal and external users.
 


	Managing Timeout Settings Across Citrix Components



	Timeout settings are vital in maintaining the balance between security and user experience. Each Citrix component, such as NetScaler, StoreFront, Citrix Identity Platform (Cloud), and third-party IDPs, has its own set of timeout configurations. EUC admins must understand how to set and align these timeouts to ensure a consistent and secure environment.
 


	Timeouts generally fall into the following categories:
 


	
		Inactivity timeouts: Disconnect users after a specified period of no activity.
	
	
		Forced timeouts: End a session regardless of activity after a certain duration.
	



	Timeouts can be configured at various levels, either globally or at the component level, depending on the desired session behavior.
 


	High-level authentication flows and components



	Authentication involves various flows depending on the user environment—cloud-based or on-premises. These flows differ for Citrix Workspace App (CWA) and browser clients and can involve multiple components, such as Active Directory, external IdPs, StoreFront, and NetScaler Gateway. Each flow also has varying token lifetimes and session timeout policies, which are enforced based on the type of authentication and client used.
 


	Interaction flows depend on whether the user is accessing the service via a native app or a web browser. For Citrix Workspace App, tokens such as access and refresh are issued by the Citrix Identity Platform (cloud environment) or StoreFront. For browser-based interactions, session cookies and tokens are set with distinct timeout configurations based on the authentication mechanism. The StoreFront, Workspace, and NetScaler configuration screens provide customization options for admins to tweak many of these settings.
 


	Cloud Environment Authentication



	Active Directory Legacy



	AD Legacy is used when using Active Directory (without token) where the following is true:
 


	
		There is no sign in policy
	
	
		Device Posture Service is not used.
	
	
		Only one Workspace URL is configured.
	



	Activity Directory Legacy provides a simplified login experience that allows legacy clients that do not support OIDC to authenticate.
 


	Native:
 


	
		Citrix Workspace app displays a screen or pop-up Window for the user to enter their username and password.
	
	
		There is no prompt to remain logged in, and re-authentication settings do not apply. Users remain logged in for up to 24 hours.
	
	
		For desktop, you can configure an inactivity timeout in Workspace configuration. The client enforces this. Once users are logged out, they must always re-enter their credentials.
	



	Browser:
 


	
		Citrix Workspace displays the login screen directly without redirecting to Citrix Identity Platform.
	
	
		The session inactivity timeout defaults to 20 minutes, but you can configure this in Citrix Workspace configuration.
		
			
				The session inactivity timeout is enforced by both the server and the client.
			
			
				Once users are logged out, they must always re-enter their credentials.
			
		
	



	Active Directory or Active Directory + Token



	This applies when using Active Directory with any of the following features:
 


	
		Multi-factor authentication
	
	
		Sign in Policies
	
	
		Device Posture Service
	
	
		Multiple Workspace URLs
	



	Native:
 


	
		The authentication screen is displayed as a web form

		
			
				On Windows a pop-up Window is displayed containing the form.
			
		
	
	
		Users enter their credentials in a Citrix Identity Provider webpage. The webpage is normally embedded into the application. On Linux and Mac, the webpage can be configured to open in your system browser but there is generally no need.
	
	
		After entering their credentials, users are prompted whether they wish to stay signed in.
		
			
				If they accept, they are kept logged in for up to the reauthentication period.
			
			
				If they deny, they are taken back to a login screen and must authenticate again. This time, the prompt to stay signed in is not displayed. The reauthentication period does not apply, so the authentication period is limited to 24 hours.
			
		
	
	
		The reauthentication period defaults to 30 days but can be configured between 1 and 365 days in Citrix Workspace Configuration. This sets the refresh token's maximum lifetime.
	
	
		The inactivity period defaults to 4 days but can be configured in Citrix Workspace Configuration to be up to the maximum reauthentication period. This sets the refresh token idle lifetime. Users stay authenticated even if they are inactive for this duration.
	
	
		For desktop, you can configure an inactivity timeout in Workspace configuration. The client enforces this.
	
	
		When the user logs out, or the inactivity timeout applies, Citrix Workspace app deletes all cookies if possible, which logs the user out. However this is not possible in the default configuration on iOS or Android, or if Linux and Mac are configured to use the system browser.
	
	
		Normally users are required to enter their credentials to sign back in. However, they may be able to sign back in without entering credentials if:
		
			
				The admin has enabled single sign-on (Federated Identity Provider Sessions off) 
			
			
				Citrix Workspace app was unable to delete the cookies.
			
			
				Under 12 hours have elapsed.
			
		
	



	Browser:
 


	
		Citrix Workspace uses session cookies to manage the session.
	
	
		The session inactivity timeout defaults to 20 minutes, but you can configure this In Citrix Workspace configuration.
	
	
		The session inactivity timeout is enforced by both the server and the client.
	
	
		Users are logged out after 24 hours, regardless of activity. However, this is not indicated by the client. The next time the user attempts to perform an action requiring a connection to the server, they must re-authenticate.
	



	External IDP



	Native:
 


	
		If Single Sign-on is enabled (the setting “Federated Identity Provider sessions” is disabled) and the IdP has an active session, the user does not need to enter credentials.
	
	
		If single sign-on is disabled (the setting “Federated Identity Provider sessions” is enabled), the user must always enter their credentials.
	
	
		Users enter their credentials in a webpage provided by the IdP. Normally the web page is embedded into Citrix workspace app. Linux and Mac can be optimally configured to open the webpage in the system browser which is necessary when using FIDO2.
	
	
		After entering their credentials, users are prompted whether they wish to stay signed in.
		
			
				If they accept, they are kept logged in for up to the reauthentication period.
			
			
				If they deny, they are taken back to a login screen and must authenticate a second time. The reauthentication period does not apply, so the authentication period is limited to 24 hours.
			
		
	
	
		The reauthentication period defaults to 30 days but can be configured In Workspace Configuration between 1 and 365 days. This sets the refresh token max lifetime.
	
	
		The inactivity period defaults to 4 hours but can be configured in Workspace Configuration to be up to the maximum reauthentication period.
	
	
		For desktop, you can configure an inactivity timeout in Workspace configuration. The client enforces this.
		
			
				When the timeout expires, the IdP is not notified to log out. However Citrix Workspace app deletes all cookies if possible which normally forces the IdP to log out. However this is not possible if Citrix Workspace on Mac or Linux have been configured to use the system browser for authentication (not the default).
			
			
				In the case that cookies have not been deleted, and single sign-on is enabled (the “Federated Identity Provider Sessions” setting is disabled) and the IdP session has not expired, the user may be able to log back in without entering their credentials.
			
		
	
	
		When the user logs off manually, the IdP is logged out (using SLO for SAML or chained logout for OIDC). In addition, if possible all cookies are deleted.
	
	
		When the user logs out due to inactivity, the IdP is not logged out.
	



	Browser:
 


	
		Citrix Workspace uses session cookies to manage the session.
	
	
		By default users are logged off after 20 minutes of inactivity. This is configurable in Citrix Workspace configuration.
		
			
				The session inactivity timeout is enforced by both the server and the client.
			
			
				When the session timeouts out, the IdP is not logged out. Therefore, if the session is still active and Federated Identity Provider Sessions is disabled, the user may be able to sign back in without entering their credentials.
			
		
	
	
		Users are logged off after 24 hours, regardless of activity. However, the client does not indicate this. The next time the user attempts to perform an action requiring a connection to the server, they must re-authenticate.
	
	
		When the user logs off manually, the IdP is logged off (using SLO for SAML or chained logout for OIDC). Citrix Identity does not support federated logout.
	



	On-Premises Environment Authentication



	Admins can enable one or more authentication methods. If multiple methods are enabled, the user can choose which to use.
 


	Username and Password



	With username and password authentication, users enter their active directory username and password.
 


	Citrix Workspace app
 


	
		A native (not browser or webview) log on screen is displayed. It does not display logon screen branding.
	
	
		By default, users remain logged in for 20 hours. This can be configured within StoreFront.
	
	
		When the user is logged out, there is no indication to the user. The next time the user performs an action requiring connection to the server, the user must log in again.
	
	
		By default, when users log in, they can save their password, which is stored on their device. The next time the app needs to log back in, it does so silently without prompting for credentials unless the user has changed their password.
		
			
				It is possible to configure StoreFront to remove the option to save passwords.
			
		
	



	Inactivity timeouts are not available. To add an inactivity timeout, use Gateway authentication instead.
 


	Browser
 


	
		The log in screen can be customized to add a branding image.
	
	
		By default, users are logged out after 20 minutes of inactivity. This is configurable within the StoreFront management console.
		
			
				To keep the session active, users must perform actions that require a connection to the server, such as refreshing or launching an app.
			
			
				Users are notified when the session will expire, giving the user the option to keep the session alive.
			
			
				Both the server and the client enforce the timeout.
			
		
	
	
		Once the user logs out or the inactivity timeout expires, the browser displays a screen notifying the user of the option to log back in.
	



	Domain pass-through



	Domain pass-through, also known as Windows integrated authentication (IWA), uses the Windows identity to single sign-on to the store using NTLM or Kerberos.
 


	Citrix Workspace app
 


	
		Users are automatically logged in without any prompts.
	
	
		No timeouts apply.
	



	Browser
 


	
		Users are automatically logged in without any prompts.
	
	
		When the user logs out, or the inactivity timeout applies, the user is taken to a log-off screen. However, when the user presses “Log On”, they are immediately logged back in without needing to enter any credentials.
	
	
		By default, users are logged out after 20 minutes of inactivity. This is configurable within the StoreFront management console. However, when the user presses “Log On”, they are immediately logged back in without needing to enter any credentials.
	



	Smartcard Authentication



	With Smartcard Authentication, users swipe their smartcard containing a certificate and optionally enter a PIN to authenticate. Smartcard authentication is available on Windows and Linux.
 


	If multiple smart card readers are attached, the user must choose which reader to use.
 


	If users use a smart card to log in to Windows, consider using Domain pass-through authentication instead. This uses the Windows identity to single sign-on to the store.
 


	Citrix Workspace app
 


	
		By default, users remain logged in for 20 hours. This can be configured within StoreFront. When the user is logged out, there is no indication to the user. The next time the user performs an action requiring connection to the server, the user must log in again. In some cases, the application caches the PIN and silently re-authenticates.
	
	
		There is no inactivity timeout.
	



	Browser:
 


	
		By default, users are logged out after 20 minutes of inactivity. This can be configured within StoreFront.
	
	
		When a user logs out or the session times out due to inactivity, the browser displays a message telling users to close their browser to force the browser to clear any cached certificates. Until they do this, users cannot log back in.
		
			
				When using the Classic UI, admins can apply a customization that allows users to log back without restarting their browsers. The user may be logged in without using their smart card depending on browser behavior.
			
		
	



	SAML Authentication



	With SAML authentication, the user is redirected to a third-party identity provider for authentication.
 


	If the user is already signed into the IdP, they may not need to enter their credentials. There is no option to set the ForceAuthn SAML attribute to tell the IdP to always prompt for credentials.
 


	IdPs may implement single-sign on for AD, hybrid, or Entra ID-joined Windows devices
 


	Citrix Workspace app
 


	
		Users enter their credentials in a webpage provided by the IdP. Normally the web page is embedded into Citrix workspace app. Linux and Mac can be optimally configured to open the webpage in the system browser which is necessary when using FIDO2.
	
	
		There is no inactivity timeout.
	
	
		By default, users remain logged in for 20 hours. This can be configured within StoreFront. If the IdP session is still active then the app is automatically logged back in the next time the user acts. Otherwise the user is taken back to the log in screen.
	
	
		Single logout is not implemented.
		
			
				Therefore logging off Citrix Workspace app does not directly log off the IdP. However in some cases Citrix Workspace app is able to clear the IdP cookies which has the effect of logging off the IdP.
			
		
	



	Browser
 


	
		When users go to the store website, they are redirected to their IdP for authentication. StoreFront login screen branding is not displayed, but you can configure branding within the IdP.
	
	
		The inactivity timeout is applied in the same way as it is for user and password authentication.
	
	
		Single logout is not implemented. Therefore logging out of the web client does not log off the IdP.
		
			
				When a user logs out or the session times out, the browser displays a message telling them to close their browser, which may cause the browser to delete the IdP session cookies. Until they do this, users cannot log back in.
			
			
				On the Classic UI, admins can apply a customization that overrides this behavior and allows users to log back in without restarting their browser. If the IdP session is still active, the user is not prompted to provide credentials.
			
		
	



	Gateway Authentication



	With Gateway authentication, you can configure a variety of authentication methods, including LDAP (username/password), OTP, SAML, and smartcard authentication. User certificate authentication is only available when using a web browser, not the Citrix Workspace app.
 


	If you use SAML authentication, you can configure whether the user is always prompted for credentials, even if they have an active session. For more information, see the NetScaler documentation.
 


	Citrix Workspace app
 


	
		Citrix Workspace app displays a web view for authentication.
	
	
		With LDAP authentication, users cannot save their passwords. This feature is only available when authenticating directly to StoreFront.
	
	
		There is a session inactivity timeout defined in NetScaler. This defaults to 30 minutes but can be configured.
		
			
				There is no indication to the user when the timeout expires. The next time the user performs an activity that requires a network connection or the background refresh occurs, the user must re-authenticate.
			
			
				By default, the Citrix Workspace app performs a background refresh every hour. Therefore, if the gateway session inactivity timeout is over an hour, the background refreshes may prevent the session from expiring until the user exits the app.
			
			
				If the NetScaler gateway uses SAML authentication, when the NetScaler inactivity timeout expires, the IdP may not be logged out. If the IdP still has a session and ForceAuthn is not set, the users will be silently logged back in the next time they act.
			
		
	
	
		An absolute timeout may optionally be configured in the gateway.
		
			
				There is no indication to the user when the timeout expires. The next time the user performs an activity that requires a network connection or the background refresh occurs, the user must re-authenticate.
			
			
				If the NetScaler gateway uses SAML authentication, when the NetScaler absolute timeout expires, the IdP may not be logged out. If the IdP still has a session, and ForceAuthn is not set, then the user will be silently logged back in the next time they act.
			
		
	
	
		EPA (endpoint analysis) is not supported in native clients.
	



	Browser
 


	
		The user goes to the gateway URL to authenticate. Once they have authenticated, they are redirected to StoreFront.
	
	
		Both the gateway and StoreFront have their own session inactivity timeouts.
		
			
				StoreFront has an inactivity timeout defaulting to 20 minutes, configurable within the StoreFront management console.
				
					
						When the timeout expires, the client tells the gateway to log out, and the user is redirected to the Gateway login page. However, if the user closes the page before the timeout expires, the NetScaler Gateway will not be logged out. The next time the user opens their browser, if the NetScaler session is still active, the user may be logged back in.
					
					
						To keep the session active, users must perform actions that require a connection to the server, such as refreshing or launching an app.
					
					
						Users are notified when the session will expire, giving the user the option to keep the session alive.
					
					
						Both the server and the client enforce the timeout.
					
				
			
			
				The gateway has an inactivity timeout defaulting to 30 minutes, configurable within the gateway administration console. If this is set to less than the StoreFront timeout, then it will apply:
				
					
						When the gateway inactivity timeout expires, the user is not notified. The next the user attempts to perform an action that requires a network connection, they are redirected to the Gateway logon page.
					
					
						If the NetScaler gateway uses SAML authentication, when the NetScaler inactivity timeout expires, if the IdP may not be not logged out. if the IdP still has a session and ForceAuthn is not set, the users will be silently logged back in the next time they act.
					
				
			
		
	
	
		When the user manually logs out, the gateway is also logged out, and the user returns to the gateway login page.
		
			
				When using SAML, you can configure the gateway with a single logout URL. This ensures that when the user logs out from StoreFront, they are also logged out from the IdP.
			
		
	
	
		Endpoint analysis (EPA) scans can be used to check the device for compliance with security policies.
	



	Current Timeout Settings Across Citrix Components



	Timeout settings can be applied at multiple layers: cloud, on-premises, or through third-party IDPs. Below is a breakdown of each component's current timeout configurations, default values, and configurable ranges.
 


	
		
			
				
					Component
				 
			
			
				
					Timeout Type
				 
			
			
				
					Default
				 
			
			
				
					Range
				 
			
			
				
					Configurable in
				 
			
			
				
					Applies to
				 
			
		
		
			
				
					NetScaler
				 
			
			
				
					Forced Timeout
				 
			
			
				
					N/A
				 
			
			
				
					1 - 65535 min
				 
			
			
				
					Global/Policy
				 
			
			
				
					Web/App
				 
			
		
		
			
				
					NetScaler
				 
			
			
				
					Session Timeout
				 
			
			
				
					30 mins
				 
			
			
				
					1 - 65535 min
				 
			
			
				
					Global/Policy
				 
			
			
				
					Web/App
				 
			
		
		
			
				
					StoreFront
				 
			
			
				
					Web Inactivity Timeout
				 
			
			
				
					20 mins
				 
			
			
				
					1 min to 24 hours
				 
			
			
				
					Admin console
				 
			
			
				
					Web Only
				 
			
		
		
			
				
					StoreFront
				 
			
			
				
					Token Lifetime
				 
			
			
				
					20 hours
				 
			
			
				
					Custom
				 
			
			
				
					Powershell
				 
			
			
				
					Web/App
				 
			
		
		
			
				
					Workspace
				 
			
			
				
					Reauthentication Period
				 
			
			
				
					30 days
				 
			
			
				
					1 - 365 days
				 
			
			
				
					Workspace Configuration
				 
			
			
				
					App
				 
			
		
		
			
				
					Workspace
				 
			
			
				
					Inactivity Period
				 
			
			
				
					4 days
				 
			
			
				
					1 - 365
				 
			
			
				
					Workspace Configuration
				 
			
			
				
					App
				 
			
		
		
			
				
					Workspace
				 
			
			
				
					Inactivity timeout - desktop
				 
			
			
				
					None
				 
			
			
				
					1 min to 24 hours
				 
			
			
				 
			
			
				 
			
		
		
			
				
					Workspace
				 
			
			
				
					Inactivity timeout - mobile
				 
			
			
				
					None
				 
			
			
				
					1 min to 24 hours
				 
			
			
				 
			
			
				 
			
		
		
			
				
					External IDP (SAML)
				 
			
			
				
					Inactivity Timeout
				 
			
			
				
					Varies
				 
			
			
				
					Based on IDP
				 
			
			
				
					IDP Configuration
				 
			
			
				
					Web/App
				 
			
		
	



	Each component has different methods for configuring these timeouts. Most timeouts can be configured using user interfaces, but some can only be configured using PowerShell. The following sections outline best practices for configuring these timeouts based on the specific use cases.
 


	Best Practices for Administrators



	This section explores specific use cases for managing timeout settings across various components, such as inactivity, session, and forced timeouts. We provide detailed instructions on configuring these timeouts and explain how they impact the end-user experience.
 


	Timeout settings across Citrix components, such as NetScaler, StoreFront, Citrix Workspace, and third-party IDPs, must be harmonized to avoid conflicts and ensure a consistent user experience. Proper alignment of token lifetimes, session timeouts, and forced timeouts helps prevent users from being unexpectedly logged out or left in active sessions longer than intended.
 


	StoreFront



	These settings apply to on-prem environments using Storefront and Netscaler Gateways.
 


	Inactivity timeout for web browsers



	By default, users connecting via a web browser are logged out after 20 minutes of inactivity. Shortly before this time, they are given a warning, allowing them to stay logged in, which resets the inactivity timeout.
 


	On the old UI, only activities that result in network calls, such as refreshing the page or launching an app, count as activities. However, mouse movements over the browser window in the new UI are also considered activities.
 


	You can change this timeout to a suitable value. See StoreFront documentation.
 


	
 


	If you use a NetScaler Gateway, there is also an inactivity timeout. When this expires, the user is not notified; the next time they attempt to act, they must log back in. You should set this timeout to 1 minute higher than the StoreFront timeout. When the StoreFront inactivity timeout expires, it notifies the user, and the user is logged out of both StoreFront and the NetScaler gateway. A slightly larger NetScaler timeout ensures that even if the web browser is closed before the StoreFront timeout expires, the NetScaler session is still logged out in a reasonable time.
 


	NetScaler Gateway &gt; Policies &gt; Session. Click on the Session Profiles tab. Typically, there are separate session profiles for the web browser and the Citrix Workspace app. Edit a profile by clicking on it. Click on the Client Experience tab. Select override and enter the required timeout.
 


	
 


	
		Note:
	 

	
		You must always override the session timeout on each policy, even if you wish to set it to the same value as the global default. If you do not do this, then users may experience a failure to log in.
	 



	If you want external users to have a shorter inactivity timeout than internal users, you can achieve this by setting a shorter timeout on the NetScaler Session profile than in the StoreFront management console. Once the NetScaler Session timeout expires, users will not be notified. The next time they attempt to act, they will be logged out.
 


	Inactivity timeout for Citrix Workspace app



	StoreFront does not apply an inactivity timeout for users connecting via the Citrix Workspace app. If you want an inactivity timeout, you should deploy a NetScaler Gateway. Then, you can set the session timeout on the session profile. There is usually a separate session profile for the Citrix Workspace app. Note that the user will not be notified when the timeout expires. The next time they attempt to act, such as launch an app or desktop, they will be required to re-authenticate.
 


	Users can save their passwords using the Active Directory username and password authentication. However, if you want them to enter their credentials each time they log out, you can disable password saving; see StoreFront documentation.
 


	Absolute timeout for web browsers



	Typically, the inactivity timeout is short enough that the absolute timeout will never be reached. However, it is still essential to have an absolute timeout in case a malicious user deliberately performs activity regularly to keep the session alive.
 


	By default, for users connecting directly to StoreFront using a web browser, an 8-hour timeout is applied. You can change this by updating the Authentication token lifetime. To allow for clock skews, there is a five-minute grace period on this timeout, configurable using the registry setting HKLM\Software\Citrix\DeliveryServices\ClockSkew. If you increase this to more than 20 hours, you must also increase the maximum token lifetime of the authentication service. The user is not notified when the timeout expires. The next time they perform an action requiring a network connection, they must re-authenticate. These settings do not apply if the user has connected via a NetScaler Gateway, as when the StoreFront token expires, if the NetScaler still has a valid session, it will automatically log back in.
 


	You can configure the “forced timeout” for users connecting via a NetScaler gateway. If you want the same experience for users connecting directly and via the NetScaler gateway, set the forced timeout to the same value as the StoreFront Authentication token lifetime. Alternatively, you may wish to set it to a lower value for external users connecting via the NetScaler gateway.
 


	You can configure the NetScaler forced timeout globally. Alternatively, you can override it for each session profile.
 


	You can configure the NetScaler forced timeout globally. Alternatively, you can set it on each session profile, which allows you to set different timeouts for the web browser, Citrix Workspace app, or other factors.
 


	To set the forced timeout globally, see here. To set a timeout for a profile, go to NetScaler Gateway &gt; Policies &gt; Session. Click on the Session Profiles tab to edit the appropriate session profile. Click on the Client Experience tab. Select override and enter the required timeout. For more information, see here.
 


	
 


	
		Note:
	 

	
		The “Forced Time-out Warning (mins)” has no effect. When the timeout expires, the user is not notified. The next time they attempt to act, they are prompted to re-authenticate.
	 



	Absolute timeout for Citrix Workspace app



	By default, for users connecting directly to StoreFront using the Citrix Workspace app, it applies a 20-hour absolute timeout. You can change this by updating Maximum token lifetime of Authentication Service. This setting does not apply if the user has connected via a NetScaler Gateway. When the StoreFront token expires, it will automatically log back in if the NetScaler still has a valid session. You are not recommended to increase this timeout beyond a day, as there is no periodic check that the user account is still active. A five-minute grace period during this timeout allows for clock skews.
 


	You can configure the " forced timeout " for users connecting via a NetScaler gateway. This is not configured by default, but you are recommended to do so. Typically, the inactivity timeout is short enough that the forced timeout has no effect. However, it is still recommended that you set a forced timeout to prevent malicious users from keeping their session active for long periods by performing regular activity.
 


	You can configure the NetScaler forced timeout globally. Alternatively, you can set it on each session profile, which allows you to set different timeouts for the web browser, Citrix Workspace app, or other factors.
 


	To set the forced timeout globally, see here. To set a timeout for a profile, go to NetScaler Gateway &gt; Policies &gt; Session. Go to the Session Profiles tab. Click on the appropriate session profile to edit it. Go to the Client Experience tab. Select Override Global and enter the required timeout. For more information, see here.
 


	Whether the timeout is specified in StoreFront or NetScaler, the user is not notified when it expires. The next time they perform an action requiring a network connection, they must re-authenticate.
 


	Users can save their passwords when using Active Directory username and password authentication. However, if you want them to enter their credentials each time they log out, you can disable password saving; see StoreFront documentation.
 


	Citrix Workspace



	This section applies to cloud environments using Citrix Workspace.
 


	Workspace controls when users are logged out. If users sign in using a third-party IdP, which has its timeouts configured, these have no impact on existing Citrix Workspace sessions.
 


	Sign-in behavior



	When users authenticate using a third-party identity provider (IdP), by default Citrix Workspace tells the IdP to prompt for credentials even if the user has a session. 
 


	Alternatively, you can configure Workspace to let the IdP decide whether to prompt for credentials. To do this, set Federated Identity Provider Sessions to off.
 


	
 


	The login screen on Windows, Mac, and Linux is embedded in a web browser by default. However, the Citrix Workspace app can be configured to use the system browser.
 


	Sign-out behavior



	When users manually log out, Citrix Workspace tells the Identity Provider to also log out. Workspace does not support federated logout so Workspace cannot tell other applications signed in using the same session to also log out.
 


	When users’ sessions time out, Citrix Workspace does not tell the Identity Provider to log out. Therefore if the IdP’s session is still inactive then it may be possible for the user to log back in without entering credentials. If you wish users to always enter credentials to log back in, either:
 


	
		Enable the Federated Identity Provider Sessions setting so users are always prompted for credentials or
	
	
		Configure your IdP so that the session timeout is less than the Workspace inactivity timeout. This way, users can single sign on. However, when their Citrix Workspace session expires, the IdP session will also expire, so the user must log back in.
	



	Inactivity Timeout for Web



	By default, users are logged out after 20 minutes of inactivity. Activity includes any mouse moves over the web page.
 


	Shortly before the session expires, the user is notified and has the option to extend the session.
 


	Session expiry is enforced on both the client and server. Therefore, if the user closes their browser without logging off, the session still expires on the server after the timeout period.
 


	To harmonize with IDP, ensure these settings reflect a similar timeout period.
 


	To configure the inactivity timeout, set the Inactivity timeout for the web to an appropriate value in Citrix Workspace Configuration.
 


	
 


	To ensure that users always have to log back in, enable Federated Identity Provider Sessions.
 


	Inactivity Timeout for Workspace App



	When users log in, the app prompts them for permission to stay logged in. If they accept, then by default, users are logged out after 4 days of inactivity (excluding ChromeOS). This is default is used so that users stay logged in over the weekend. To configure this, modify the Inactivity Period in Workspace Configuration. It can be set to between 1 day and the Reauthentication period. The user is not notified when the inactivity timeout expires. The next time they attempt to perform an action, they are required to log back in.
 


	
 


	On Citrix Workspace app for Windows, Mac, and Linux, to apply timeouts of less than a day, you can enable the Inactivity Timeout for Workspace App - Desktop and set it to between 1 minute and 24 hours. If the user does not perform any activity for the configured length of time, the user is logged out after the timeout expires. Activity includes any mouse or keyboard interaction. This timeout is enforced by the client. Shortly before the session expires, the user is notified and can extend the session.
 


	The inactivity timeout does not log the user out of the IdP. If using the web view (the default), it clears cookies, which logs out of any IdPs using cookie-based authentication. Otherwise, if “Federated Identity Provider Sessions” is turned on and the IdP session is still active, the users may be able to log back in without being prompted for credentials.
 


	
 


	On Citrix Workspace app iOS and Android, to apply timeouts of less than a day, you can enable the Inactivity Timeout for Workspace App - Mobile and set it to between 1 minute and 24 hours. If the device has biometrics enabled, then this does not log the user out. Instead, it locks the app. The user must unlock the app using biometrics.
 


	
 


	On ChromeOS, there is an inactivity timeout after 20 minutes. This is not configurable.
 


	Absolute timeout for web browsers



	Typically, the inactivity timeout is short enough that the absolute timeout will never be reached. However, it is still important to have an absolute timeout in case a malicious user deliberately performs activity regularly to keep the session alive.
 


	The session expires after 24 hours, regardless of any activity. This is not configurable.
 


	Absolute timeout for Citrix Workspace app



	When users log in they are asked for permission to stay logged in. If they accept, then by default, users are logged out after 30 days, regardless of activity. To configure this, in Workspace Configuration, modify the Reauthentication Period. It can be set to between 1 day and 365 days.
 


	
 


	During this time, the Citrix Workspace app periodically validates that the user account is still valid.
 


	If the user denies the permission to stay logged in, they are logged out after 24 hours.
 


	Conclusion



	The timeout settings across Citrix components allow EUC admins to configure secure and seamless user experiences. Admins can effectively manage session lengths, inactivity timeouts, and forced re-authentication by leveraging the existing settings in NetScaler, StoreFront, Citrix Identity Platform, and third-party IDPs. Adhering to best practices ensures a consistent and predictable user experience that balances security and user convenience.]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2025_01/image.png.9df967f3f911611ed3f8dca0036a950a.png" length="299130" type="image/png"/><pubDate>Thu, 23 Jan 2025 15:02:00 +0000</pubDate></item><item><title>Citrix Secure Developer Spaces</title><link>https://community.stage.citrix.com/tech-zone/by-product/citrix-secure-developer-spaces/</link><description>Citrix Secure Developer Spaces, formerly known as Strong Network, is a secure, cloud-based development environment (CDE) platform that enhances developer productivity while maintaining enterprise-grade security. It provides fast onboarding through preconfigured workspaces that are accessible from anywhere&#x2014;ideal for hybrid and remote teams. Deployment GuidesDeploying Secure Developer Spaces in Azure Kubernetes - Provides the steps to deploy Citrix Secure Developer Spaces CDE in Azure Kubernetes. Deploying Citrix Secure Developer Spaces on OpenShift - Provides the steps to deploy Citrix Secure Developer Spaces to an existing OpenShift cluster.</description><pubDate>Wed, 15 Jan 2025 16:08:00 +0000</pubDate></item><item><title>Key Exchange in SSL/TLS: Understanding RSA, Diffie-Hellman, and Elliptic Curves</title><link>https://community.stage.citrix.com/tech-zone/build/tech-papers/key-exchange-in-ssl-tls/</link><description>Overview



	When you connect to a secure website, your browser and the server must establish an encrypted connection to exchange data. Many will say this connection is "protected with the server's certificate," but this oversimplifies what's happening. Certificates are crucial in authenticating the server and enabling key exchange, but they&#x2019;re just one piece of the puzzle. 
 


	 Modern websites depend on seamless yet robust encryption to ensure data security. Understanding how key exchanges evolved - from RSA to Diffie-Hellman to ECDHE - unlocks the complete picture of how cryptography balances performance and security. 
 


	 Public/Private Key Pair encryption is called &#x201C;asymmetric encryption&#x201D; (meaning the keys used for encryption and decryption are not the same) and relies on a process called &#x201C;modular exponentiation.&#x201D; This process can be hundreds of times slower than &#x201C;symmetric encryption&#x201D; (which uses the same key for encryption and decryption). If the server certificate (asymmetric encryption) were used for the entire connection, the experience would be poor and significantly slower. Instead, Key Exchange occurs at the beginning of each connection and focuses on moving from asymmetric to symmetric encryption, ensuring the connection remains secure while significantly improving performance.  
 


	 But how do your browser and the server agree on a symmetric key without exposing it to potential eavesdroppers? This article unpacks the evolution of key exchange protocols - RSA, Diffie-Hellman, and ECDHE - to explain their trade-offs between security and performance. 
 


	The companion document Networking SSL/TLS Best Practices (Q1 2025 Edition) provides practical implementation guidance on configuring SSL/TLS using these key exchange methods. 
 


	Historical Context 



	In the late 90s, most of the Internet operated over plaintext ("http" rather than "https"). Websites that required security, such as banking portals, relied on the RSA encryption system with 512-bit certificates, balancing computational load and security. Servers with limited encrypted traffic could handle this load using software like Apache 1.3&#x2019;s mod ssl. However, high-traffic sites often relied on specialized hardware accelerators to offload the resource-intensive RSA calculations. 
 


	At that time, 512-bit certificates were common, and "secure" often meant little more than the browser displaying a padlock icon and the assurance the server &#x201C;had a certificate installed.&#x201D; The certificate's presence provided assurance, but there was often little discussion about the level of security the &#x201C;encryption&#x201D; provided. 
 


	As computation power doubled every 18 months, what was considered secure in the late 90s (512-bit keys) became increasingly vulnerable. In 1999, Adi Shamir, one of RSA&#x2019;s creators, led a research effort that publicly cracked a 512-bit certificate, showing it was vulnerable to modern computing but also the computational power of a single organization. Using distributed computing, a second certificate was cracked in 2000 as part of the RSA-155 project. This highlighted the urgent need to move to stronger key sizes such as RSA-1024 and RSA-2048, which were already recommended by standards bodies like NIST. 
 


	The evolution of key sizes highlights the trade-off between computational feasibility and resilience against attacks. While 512-bit keys sufficed in the early web era, increasing key sizes to 1024 bits doubled the security margin but required over 30 times more processing power.
 


	This growing computational demand drove the development of more efficient cryptographic methods, such as Elliptic Curve Cryptography (ECC). 
 


	By 2003, the National Institute of Standards and Technology (NIST) recommended a minimum RSA key size of 1024 bits, with 2048 bits suggested for long-term security. In 2004, Visa, Mastercard, American Express, Discover, and JCB introduced the Payment Card Industry Data Security Standard (PCI DSS), mandating strong encryption practices for credit card processing, making 1024-bit keys the industry norm.
 


	In 2009, SSL Labs launched, raising greater awareness about SSL/TLS best practices. By 2010, Microsoft, Mozilla, and Google began marking 512-bit certificates as insecure, rendering them obsolete.  
 


	In 2013, NIST began recommending 2048-bit keys for most security applications, and the CA/Browser Forum mandated that Certificate Authorities stop issuing 1024-bit certificates. Website owners with certificates expiring after December 31, 2013, were required to replace them with 2048-bit certificates before the deadline. By this point, 2048-bit certificates were the standard for encryption, reflecting a broader industry commitment to security. Around this point, I began writing the NetScaler &#x201C;How to get an A+ at SSL Labs&#x201D; series, as it had become a frequent question from enterprise customers looking to ensure their platforms were secure.  
 


	After 2013, while 2048-bit certificates have remained widely used, cryptographic standards and practices have continued to evolve in response to technological advancements and emerging threats. However, an alternative became necessary, with 4096-bit RSA keys being significantly more computationally intensive than their 2048-bit counterparts. ECC emerged as that alternative. Today, ECC provides the same level of security as RSA but with significantly smaller key sizes. For example, a 256-bit ECC key is considered equivalent in strength to a 3072-bit RSA key, though this depends on the state of cryptanalysis techniques and computing power available.  
 


	This efficiency has reduced computational load and improved performance, particularly for mobile devices, IoT applications, and environments where resources are constrained. For key exchange protocols, ECC has now become the current preferred choice. 
 


	However, while ECC is increasingly adopted for key exchange, RSA certificates remain widely deployed due to their longstanding prevalence and compatibility across existing infrastructure. This reflects the ongoing transition in cryptographic practices, where ECC is gaining traction, but RSA continues to dominate legacy systems. 
 


	Looking ahead, however, cryptographic standards will continue to change. RSA's security relies on the computational difficulty of factoring large numbers, a problem that quantum computers may efficiently solve using Shor's algorithm. Similarly, ECC's security depends on solving the elliptic curve discrete logarithm problem, which is also potentially vulnerable to quantum attacks. Although these quantum threats remain theoretical, they underscore the need for continued innovation in cryptographic systems. More on this later. 
 


	RSA: A Static Key Exchange 



	Let's begin with the classic RSA key exchange, the cornerstone of secure communications for years. A server has a certificate and a key. The certificate contains the server's public key, shared with anyone who wants to connect, while the private key is securely stored on the server. This is called asymmetric encryption (two different but mathematically linked keys: a public and private key). It's secure but typically hundreds or thousands of times slower than symmetric algorithms like AES. 
 


	How RSA Works 



	An RSA public/private key pair works like a special automatic locking box you can send in the post. This box (the public key) ensures anyone can securely send a message, but only the owner, with their private key, can unlock it to retrieve the contents.  
 


	RSA is based on&#x202F;asymmetric encryption, which uses two mathematically linked keys: 
 


	
		Public Key: Shared openly and used to encrypt data. 
	
	
		Private Key: Kept secure and used to decrypt data. 
	



	When a client connects:  
 


	
		The server provides its RSA public key in a certificate. 
	
	
		The client generates a random secret, known as the&#x202F;pre-master secret, and encrypts it using the server's public key. 
	
	
		The server decrypts the pre-master secret with its private key. 
	
	
		The client and server use the pre-master secret to derive a symmetric session key, which encrypts the rest of the connection using faster algorithms like AES. 
	



	The public and private keys are used only during the initial exchange of the pre-master secret. Once the symmetric session key is derived, it is used for the rest of the communication. This transition ensures the connection remains secure while leveraging the speed of symmetric encryption for the remainder of the session. 
 


	The Mathematical Foundation 



	RSA&#x2019;s security relies on the mathematical difficulty of factoring large numbers. While it&#x2019;s easy to multiply two numbers together, it&#x2019;s incredibly hard to reverse the process and figure out the original numbers&#x2014;especially when the numbers involved are very large. 
 


	At some point, long before a client connects and the key exchange process begins, the&#x202F;server administrator&#x202F;will have created a&#x202F;private key&#x202F;and a&#x202F;public key&#x202F;while creating the server&#x2019;s certificate. The public key will be included in the certificate and sent to clients, while the private key will remain stored securely on the server. 
 


	This process will have occurred when the keys were generated: 
 


	
		The server will have picked two large&#x202F;prime numbers&#x202F;(commonly called&#x202F;p&#x202F;and&#x202F;q), each hundreds of digits long. 
	
	
		These primes were multiplied to create a larger number (called n), the&#x202F;modulus.  
	
	
		The&#x202F;public key&#x202F;is made up of: 
		
			
				n: the product of multiplying p and q 
			
			
				e: a smaller number, often a standard value like 65537, used for encrypting data 
			
		
	
	
		The&#x202F;private key&#x202F;is derived using&#x202F;p,&#x202F;q, and&#x202F;e&#x202F;through a mathematical process called &#x201C;modular inversion&#x201D; that ensures only the server can decrypt data encrypted with the public key. 
	



	When a client connects, the public and private keys work together during the key exchange: 
 


	
		The&#x202F;public key&#x202F;is included in the certificate and sent to the client, who uses it to encrypt the&#x202F;pre-master secret.  
	
	
		The&#x202F;private key&#x202F;is kept secret. The server uses it to decrypt the pre-master secret sent by the client. 
	



	Once the pre-master secret is securely shared, the client and server use it to derive a&#x202F;symmetric session key, efficiently encrypting the rest of the communication. 
 


	Why the Secrecy of&#x202F;p&#x202F;and&#x202F;q&#x202F;Matters 



	The security of RSA depends on keeping the prime numbers&#x202F;p&#x202F;and&#x202F;q&#x202F;secret. If someone could figure out&#x202F;p&#x202F;and&#x202F;q from&#x202F;n, they could calculate the private key and decrypt any communication. However, factoring&#x202F;n&#x202F;back into&#x202F;p&#x202F;and&#x202F;q is extremely difficult and becomes exponentially harder as the key size increases.  
 


	Modular Exponentiation and Why Asymmetric Encryption is only used for the initial key exchange 



	Aside from sounding like the strangest Harry Potter novel, touching on modular exponentiation is an excellent opportunity to explain&#x202F;why asymmetric encryption (like RSA)&#x202F;is only used for the initial key exchange and not for encrypting the entire conversation.  
 


	Modular exponentiation is the operation used during encryption and decryption in RSA: 
 


	
		To encrypt, the client computes&#x202F;c = m^e mod n, where m is the message, e is the public exponent, and n is the modulus.  
	
	
		To decrypt, the server computes&#x202F;m = c^d mod n, where d is the private exponent.  
	



	This process is computationally intensive, especially for larger key sizes (like 2048 or 3072 bits), because it involves repeated multiplication and division by n. The bigger the key size, the more time and processing power it takes. 
 


	Using RSA for every message in a conversation would be highly inefficient. Rather than&#x202F;m&#x202F;consisting of just small handshake data, the RSA calculation would need to be performed for the content of every data packet exchanged during the session. Even on modern CPUs, performing RSA encryption or decryption for each data packet is significantly slower than using a symmetric cipher like AES. For example, while RSA-2048 can encrypt a small piece of data (such as a 256-byte pre-master secret) in microseconds to milliseconds, AES can encrypt 1 MB of bulk data in under a millisecond on typical hardware. This performance gap is why RSA is used only at the start of a session to secure the key exchange for a faster symmetric cipher like AES. 
 


	The Vulnerability 



	The RSA key exchange has a significant weakness: it lacks forward secrecy. Suppose the server's private RSA key is ever compromised, whether tomorrow or years from now. In that case, an attacker who recorded any past traffic can retroactively decrypt the key exchange, retrieve the pre-master secret, and decrypt the traffic. For this reason, RSA key exchange is discouraged in modern TLS setups, which favor ephemeral methods like ECDHE (for example, TLS 1.3 uses ECDHE as its default key exchange method). The RSA key exchange lacks forward secrecy, a critical limitation in modern cryptography. Since the same private key is reused across all sessions, a single compromise of the key renders all previously recorded traffic vulnerable to decryption. This creates a significant risk for sensitive data in environments requiring long-term security. Historical sessions remain at risk if the primary RSA key is stolen later. 
 


	Forward Secrecy (also called Perfect Forward Secrecy&#x2014;PFS) is a security property in which compromising long-term keys (like a server's RSA private key) doesn't compromise past session keys. The RSA Key Exchange lacks Forward Secrecy. 
 


	 
 


	  
 


	 
 


	The Security-Performance Trade-off  



	Smaller key sizes (e.g., 512 bits) are completely insecure by modern standards and can now be factored within hours using readily available home PCs and laptops. Larger keys (e.g., 1024 or 2048 bits) are exponentially harder to break but require more processing power. This creates a trade-off between security and performance: 
 


	
		In the 90s, 512-bit keys were standard, balancing security and computational cost 
	
	
		By the 2000s, 1024-bit keys became the norm 
	
	
		Today, 2048-bit keys are standard (and considered the minimum) for strong encryption, with larger sizes (e.g., 3072 or 4096 bits) used for high-security applications 
	



	Diffie-Hellman (DHE) and Forward Secrecy  



	Diffie-Hellman Ephemeral (DHE) uses a different approach to address the lack of forward secrecy in RSA key exchange. DHE generates new, temporary key pairs for each session. This 'ephemeral' (meaning short-lived or per session) design ensures that even if a private key from one session is compromised, past and future sessions remain secure. 
 


	How DHE Works In this process: 
 


	
		The client and server each generate a private secret number (their "private DH key"). This private key is never shared with anyone. 
	
	
		They calculate corresponding public values from their private keys Using a shared mathematical base (a known prime number and generator). These public values are safe to share and are exchanged between the client and server. 
	
	
		Each side combines its private key with the other party's public value through a mathematical process (called modular exponentiation) to arrive at the same shared secret. 
	



	The public values are safe to share because they cannot be reversed to reveal the private keys. This security relies on a mathematical property called the discrete logarithm problem, which is computationally infeasible to solve for large numbers. 
 


	Through modular exponentiation, the client and server independently calculate the shared secret without transmitting it over the connection. This differs from public-private key-pair encryption (like RSA), where there are two keys for encryption and decryption. In contrast, Diffie-Hellman is a key exchange protocol focused on securely creating a shared secret without ever exposing it. 
 


	Key Sizes and Security  



	To provide the same level of protection, a DH key needs to match the size of the RSA key it replaces. For example, rather than using a 2048-bit RSA key, you might use a 2048-bit DH key. In this case, the "DH key" refers to the private random number generated by the client and server for that individual session. The cryptographic library on each side will then use that key with predefined groups of widely accepted and pre-agreed secure prime numbers to calculate the modular exponentiation. The cryptographic library will select a prime matching the size of the key. For instance, a "2048-bit DH key" corresponds to a group with a 2048-bit prime modulus, ensuring consistent security. 
 


	The Benefits and Drawbacks  



	Key Benefits: 
 


	
		As ephemeral/per-session keys are used, a new private key is generated for each session.
	
	
		Even if an attacker compromises the server's long-term RSA key in the future, they cannot decrypt past or future sessions.
	
	
		The server can still prove its identity using RSA while the shared secret is derived via DH.
	



	The Drawback:  
 


	The modular exponentiation required to compute public values and derive shared secrets is CPU intensive, especially when using large prime numbers. On busy servers, this can become a bottleneck, leading to: 
 


	
		High CPU usage: Servers handling many connections experience significant overhead 
	
	
		Battery drain: Mobile devices checking email or browsing frequently suffer reduced battery life 
	



	Elliptic Curves&#x2014;Smaller, Yet More Secure  



	Elliptic Curve Diffie-Hellman Ephemeral (ECDHE) involves much smaller key sizes and is orders of magnitude faster.  
 


	ECC significantly reduces computational requirements for servers and clients, making it well-suited for mobile devices, IoT sensors, and high-traffic environments. For instance, a 256-bit ECC key provides the same security strength as a 3072-bit RSA key while requiring a fraction of the processing power and bandwidth. This efficiency allows secure connections without compromising speed or scalability. 
 


	But how does it work? 
 


	Understanding Elliptic Curves  



	First, let's define what Elliptic Curves are. Imagine you have a pre-agreed curve defined by a mathematical equation (shown in the diagram) and a point on that curve that everyone knows about. This point is called the base point (denoted as G). 
 


	To start, you pick a secret number - your private key. The larger this number, the stronger your security. Using your private key, you perform a mathematical operation called scalar multiplication, which involves multiplying the base point G by your private key. This isn't simple multiplication, though - it consists of adding the point to itself following specific geometric rules, where each addition requires drawing lines between points and finding intersections with the curve. As our diagram illustrates, all calculations are done using modular arithmetic, making the resulting points jump unpredictably around the curve. This operation is similar to repeatedly adding the base point to itself a number of times equal to the value of the private key, treated as an integer. 
 


	To put this into perspective, using a 256-bit private key means 2^256 (approximately 10^77) possible private keys. This is an astronomically large number - even the number of atoms in the Milky Way galaxy (roughly 10^67 to 10^69) is around 100 million times smaller than this number of possibilities. While these geometric operations of finding intersections would be incredibly slow if done one at a time, computers can optimize this process using an efficient method called "double-and-add." This method still follows the same geometric rules for each operation. Still, it strategically combines point doublings (2G, 4G, etc.) with selective additions to minimize the number of geometric operations needed. With double-and-add, computing scalar multiplication requires only about 256-point operations for a 256-bit key, making it computationally feasible. 
 


	The Security Foundation  



	The result of this calculation is a new point on the curve, which becomes your public key. While it's easy to calculate the public key from the private key (by performing scalar multiplication), it's challenging to reverse and find the process. 
 


	
		The mathematical properties of elliptic curves over finite fields mean that scalar multiplication produces points with no predictable pattern, making it computationally infeasible to work backward. 
	
	
		To reverse the process, an attacker would need to solve the Elliptic Curve Discrete Logarithm Problem, for which no efficient mathematical solution is currently known (although this may change as quantum computing advances). 
	
	
		With a 256-bit key space (approximately 10^77 possibilities), even with the most efficient known mathematical approaches running on the fastest classical computers, solving the ECDLP would take longer than the universe's age. 
	



	 
 


	  
 


	ECDHE - DHE, but on an Elliptic Curve  



	Why ECDHE Became the Standard 



	Elliptic Curve Diffie-Hellman Ephemeral (ECDHE) is simply taking the Diffie-Hellman concept and performing it over an elliptic curve rather than using huge primes. Each party picks a random integer (the private key), multiplies a known "base point" on the curve by this integer to get a public point, and exchanges these public points. By combining the public point they receive with their private key, they compute a shared secret - and never actually send it over the wire. This approach is much faster than traditional DHE because ECC arithmetic on 256-bit numbers is quicker than exponentiation with 2048+ bit primes, and you still get forward secrecy via ephemeral keys. 
 


	Like the RSA + DHE scenario, an RSA certificate can be used with ECDHE purely for authentication (the server proves its identity with the RSA certificate, while the actual shared secret is derived via elliptic-curve DH). This makes ECC an ideal choice for modern cryptographic applications, where efficiency and security are critical (where are they not?), and ECDHE is now standard for key exchange. (For a detailed NetScaler configuration guide implementing these methods, see 'Networking SSL/TLS Best Practices (Q1 2025 Edition)'.) 
 


	
		
			
				
					Protocol 
				 
			
			
				
					Key Size (Equivalent Strength) 
				 
			
			
				
					Performance Impact 
				 
			
			
				
					Forward Secrecy 
				 
			
			
				
					Modern Usage 
				 
			
		
		
			
				
					RSA 
				 
			
			
				
					2048-bit 
				 
			
			
				
					High 
				 
			
			
				
					No 
				 
			
			
				
					Legacy systems 
				 
			
		
		
			
				
					DHE 
				 
			
			
				
					2048-bit 
				 
			
			
				
					Moderate 
				 
			
			
				
					Yes 
				 
			
			
				
					Some applications 
				 
			
		
		
			
				
					ECDHE 
				 
			
			
				
					256-bit ECC 
				 
			
			
				
					Low 
				 
			
			
				
					Yes 
				 
			
			
				
					Default in TLS 1.3 
				 
			
		
	



	 



	The Importance of Standardized Curves  



	One final note: we can&#x2019;t just casually invent our own elliptic curve parameters. Curves used in cryptography &#x2013; like NIST P-256, Curve25519, and P-384 &#x2013; undergo rigorous scrutiny by the cryptographic community. Small errors in curve definition or parameter selection can severely weaken security, making it easier for attackers to exploit vulnerabilities. Worse, obscure or poorly reviewed curves could contain intentional backdoors, leaving systems compromised. This is why widely validated curves, like NIST P-256 and Curve25519, have become the gold standard for ensuring cryptographic integrity. (Refer to the 'Networking SSL/TLS Best Practices (Q1 2025 Edition)' configuration guide for practical implementation of these curve selections.) 
 


	New standardized curves, such as Curve448 and those from the SafeCurves project, continue to be developed and reviewed to address evolving cryptographic needs. That&#x2019;s why standardization bodies and industry consensus are crucial: adopting widely validated, open curves ensures your security is based on well-tested math that the world&#x2019;s cryptographers have combed over. 
 


	For example, poorly chosen parameters could introduce vulnerabilities, making the curve susceptible to faster-than-expected attacks. Worse, intentionally flawed curves could harbor backdoors, compromising all data protected by them. Adopting widely validated and open standards like NIST P-256 and Curve25519 is critical to ensuring robust security. 
 


	As cryptography continues to evolve, mastering key exchange protocols is crucial&#x2014;not just for securing today&#x2019;s data but also for building resilient systems that can withstand the challenges of tomorrow&#x2019;s threat landscape.</description><enclosure url="https://media.invisioncic.com/r328636/monthly_2025_01/image.png.aaa67ee45f580505cbe38307eb6eb32d.png" length="72956" type="image/png"/><pubDate>Wed, 08 Jan 2025 15:07:33 +0000</pubDate></item><item><title>Networking SSL/TLS Best Practices (Q1 2025 Edition)</title><link>https://community.stage.citrix.com/tech-zone/build/tech-papers/networking-tls-best-practices-2025/</link><description><![CDATA[Overview 



	This Tech Paper aims to convey what someone skilled in NetScaler would configure as a generic implementation to receive an A+ grade at Qualys SSL Labs. Qualys SSL Labs performs a robust series of tests and provides a scorecard that you can use to improve your configuration. The scan is free and only takes about a minute to complete. 
 


	While an A+ at SSL Labs is a useful benchmark, it may not be suitable for every environment. Organizations should weigh the benefits of advanced configurations against potential impacts on legacy systems and business continuity. A risk-based approach, guided by your security team, ensures configurations meet both technical and operational needs. 
 


	For those seeking to understand the cryptographic principles behind key exchange methods and why specific configurations are recommended, see "Key Exchange in SSL/TLS: Understanding RSA, Diffie-Hellman, and Elliptic Curves". This deep dive explains how different key exchange methods work and their security implications. 
 


	Qualys actively develops SSL Labs. Tests will likely change as new protocols are created and vulnerabilities are found. It is good practice to test sites regularly to ensure that any new vulnerabilities are not exposed. 
 


	This paper builds on the “Scoring an A+ at SSL Labs with Citrix NetScaler” published and maintained since 2014.
 


	
		Note: 
	 

	
		SSL/TLS security is a cornerstone of modern cybersecurity. While achieving an A+ on SSL Labs is a valuable benchmark, organizations must align configurations with their unique risk profiles, business requirements, and compliance obligations. This guide provides actionable steps to optimize your NetScaler deployment while considering the evolving security landscape and operational trade-offs. Talk to your security team about your deployment. Security experts say SSL Labs A+ is a good general target, but it may not fit your organization's needs. 
	 



	NetScaler Configuration 



	Items that need to be validated 



	
		Certificates - Is the full chain provided and trusted? Is the signature algorithm secure? 
	
	
		Protocols, Keys, and Cipher Support - Which SSL and TLS versions are supported? Which cipher suites are preferred, and in what order? Do the provided cipher suites support forward secrecy? 
	
	
		TLS Handshake Simulation - Determines which protocol and cipher are negotiated by several different clients and browsers 
	
	
		Protocol Details - Is Secure Renegotiation supported? Is strict transport security (HSTS) supported? 
	
	
		Known Vulnerabilities - Is the server vulnerable to attacks such as POODLE, BEAST, or TLS downgrade? 
	



	Once SSL Labs completes testing, it presents a letter grade along with a point scale for each of 4 categories: 
 


	
		Certificate 
	
	
		Protocol Support 
	
	
		Key Exchange 
	
	
		Cipher Strength 
	



	Each category receives a numerical score that SSL Labs then averages into a total. Some special cases and configurations that SSL Labs recommends against, such as having SSLv3 enabled, can limit your final grade. You can find complete documentation on how SSL Labs grades servers here. 
 


	Implementation Concerns 



	When implementing this configuration, it's essential to collaborate with your security, DevOps, and compliance teams. This ensures alignment with broader organizational security policies and minimizes potential operational disruptions, such as legacy client compatibility issues. 
 


	For example, enabling modern cipher suites or disabling older protocols might impact legacy systems. Coordinating with relevant teams helps ensure a balance between security and operational needs. 
 


	SSL Profiles 



	This article uses SSL Profiles. When first enabled, SSL Profiles set all SSL virtual servers to use the default profile. 
 


	SSL profiles take precedence over global and per virtual server SSL parameters. 
 


	From release 14.1 build 21.x, you can run a script from the NetScaler GUI that parses your configuration and creates custom profiles based on your existing settings. Full details can be found here. 
 


	SSL Profiles provide a modular approach to managing cryptographic settings. This modularity simplifies updating and testing configurations as new standards, such as post-quantum cryptographic algorithms, become available. 
 


	The recommended configurations in this guide prioritize strong elliptic curve cryptography (e.g., X25519, P-256) and are designed with cryptographic agility in mind. These measures enhance current security and position your deployment for seamless adoption of hybrid or post-quantum algorithms in the future. 
 


	Client support 



	Some of the configuration steps in this article can cause connectivity issues with old clients and browsers. If support for modern ciphers is missing, the client cannot connect. While Internet Explorer 8 on Windows XP was the last major example of an OS and browser lacking TLS 1.2 and ECC cipher support entirely, Internet Explorer 11 on Windows 7 and 8.1 (which went end-of-life in January 2023) required configuration changes to enable modern ciphers.  
 


	SSL Labs has a “Test your browser” button on its front page to help determine your needs. 
 


	Additional Note on Key Exchange in TLS 1.3 



	When configuring cipher suites, it's essential to understand the default key exchange (Kx) methods used by different TLS versions. In TLS 1.3, the default key exchange method is ECDHE, even if no Kx method is explicitly specified in the cipher suite. This ensures modern key exchange methods that provide forward secrecy by default. For a deeper understanding of how ECDHE compares to RSA and traditional DHE, refer to "Key Exchange in SSL/TLS: Understanding RSA, Diffie-Hellman, and Elliptic Curves." 
 


	Prioritizing ECDHE key exchanges is particularly effective for appliances equipped with the Lewisburg chip, such as 9100/16000 models, as these chips are optimized for elliptic curve operations. While RSA key exchange methods remain valid for specific use cases or legacy support, they are not utilized by default in TLS 1.3 cipher suites. 
 


	Citrix Receiver/Workspace app Cipher Support for Gateway deployments 



	Review the following articles regarding client cipher support when deploying a gateway virtual server for virtual apps and desktops: 
 


	
		CTX250104 for Citrix Workspace app 
	



	
		CTX234227 and CTX232266 for Citrix Receiver 
	



	Basic Steps - GUI 



	Take the following steps to ensure a high score on the SSL Labs test. 
 


	
		Ensure that the ADC is running a recent firmware release—14.1 is recommended to take advantage of rate limiting during renegotiation.  
	
	
		Ensure that the certificate chain is complete and trusted 
		
			
				Root CAs do not always directly sign certificates. Instead, a root CA often uses an intermediary to sign a certificate. 
			
			
				Install the intermediate certificate on the NetScaler. Link it to the server certificate you bound to the virtual server. 
			
			
				Intermediate certificates are provided by the vendor that provides the server certificate, often in a ‘certificate bundle.’ They can usually be found on the vendor’s public site. 
			
			
				You may need to install and link multiple intermediate certificates. For the server certificate to function, the NetScaler must send all certificates required for the client to have a complete chain. A complete chain ends with a certificate signed by one of the client’s trusted root CAs. 
			
			
				As the client already has the trusted root CA, you don’t need to install and link it on the NetScaler. 
			
			
				To install an intermediate certificate, go to Traffic Management &gt; SSL &gt; Certificates &gt; CA Certificates and choose Install 
			
			
				Link an intermediate by selecting the certificate and choosing link from the action menu 
			
			
				If the correct intermediate certificate is installed, it is automatically populated in the linking menu
			
		
	



	 
 


	  
 


	 
 


	  
 


	  
 


	 
 


	
		Create a custom cipher group that provides Forward Secrecy (FS) 

		
			
				Go to Traffic Management &gt; SSL &gt; Cipher Groups and choose Add 
			
			
				Name the cipher group “SSL_Labs_Cipher_Group_Q1_2025” 
			
			
				Click Add , then expand the ALL section - select the following cipher suites: 
				
					
						TLS1.3-AES256-GCM-SHA384 
					
					
						TLS1.3-AES128-GCM-SHA256 
					
					
						TLS1.3-CHACHA20-POLY1305-SHA256 
					
					
						TLS1.2-ECDHE-ECDSA-AES256-GCM-SHA384 
					
					
						TLS1.2-ECDHE-ECDSA-AES128-GCM-SHA256 
					
					
						TLS1.2-ECDHE-RSA-AES256-GCM-SHA384 
					
				
			
			
				Click the &gt; right arrow to move the ciphers from the Available column to the Configured column 
			
			
				Click Create
			
		
	



	 
 


	  
 


	 
 


	Navigate to Traffic Management &gt; SSL &gt; Change advanced SSL settings, scroll down, and select Enable Default Profile. 
 


	
 


	 
 


	SSL Profiles sets all SSL virtual servers to use the default profile when first enabled. As existing per-virtual-server SSL settings are removed, NetScaler will prompt you to confirm. 
 


	  
 


	 
 


	
		Create an SSL Profile 

		
			
				Navigate to System &gt; Profiles &gt; SSL Profile, and select Add
			
		
	



	 
 


	  
 


	
		
			
				Name the profile “SSL_Labs_Profile_Q1_2025” 
			
			
				Scroll to Deny SSL Renegotiation and select NONSECURE to allow only clients that support RFC 5746 to renegotiate 
			
			
				Scroll to Maximum Renegotiation Rate and set this to 100 or a value suitable for your environment 
			
		
	



	
		Note: 
	 

	
		The number of client-initiated SSL renegotiations expected within your environment will depend on the specific use case. While modern web applications rarely rely on SSL renegotiations due to their overhead and potential security risks, specific specialized or legacy systems may still utilize them for tasks such as client certificate re-authentication or renewing session keys in long-lived connections. 
	 



	 
 


	
		
			
				Scroll to OCSP Stapling and tick this option 
			
			
				Scroll to HSTS, tick HSTS, and specify a Max Age of 157680000 seconds 
			
			
				Scroll to Protocol and select only TLSv12 and TLSv13 
			
		
	



	 
 


	  
 


	 
 


	
		
			
				Scroll to the end of the form and select OK 
			
			
				Scroll to SSL Ciphers, select the pencil icon to edit, then click Remove All 
			
			
				Click Add and add the cipher group we created earlier  
			
			
				Click OK once the SSL Ciphers are added 
			
		
	



	 
 


	  
 


	 
 


	
		
			
				Scroll to ECC Curve and click the current number of ECC Curves
			
		
	



	 
 


	  
 


	 
 


	
		
			
				Tick P_224 and click Unbind  
			
			
				Click Close 
			
		
	



	 
 


	  
 


	
		Note: 
	 

	
		While the P-224 curve does not impact the SSL Labs score, security frameworks such as NIST and PCI DSS recommend a minimum security strength of 128 bits. The P-224 curve, with its 112-bit security level, does not meet this standard and is, therefore, unsuitable for most modern cryptographic applications. Additionally, P-224 offers no significant performance benefits over stronger curves like X25519 or P-256, making it an outdated choice for contemporary use.
	 



	
		 
	
	
		Scroll to the end of the form and select Done 
	
	
		Bind the SSL Profile to the SSL virtual server 
	
	
		On the selected virtual server, select the pencil icon to edit the bound SSL Profile. 
	
	
		Select the SSL Profile we created from the drop-down list 
	
	
		Click OK
	



	Basic Steps - CLI 



	Take the following steps to ensure a high score on the SSL Labs test. 
 


	The SSL virtual server's name in the CLI examples below is Ex-vServer . You can replace it with the name of the SSL virtual server in your environment. 
 


	
		Create a custom cipher group that prefers ECDHE and ECDSA cipher suites 
	


add ssl cipher SSL_Labs_Cipher_Group_Q1_2025   

bind ssl cipher SSL_Labs_Cipher_Group_Q1_2025 -cipherName TLS1.3-AES256-GCM-SHA384 

bind ssl cipher SSL_Labs_Cipher_Group_Q1_2025 -cipherName TLS1.3-AES128-GCM-SHA256 

bind ssl cipher SSL_Labs_Cipher_Group_Q1_2025 -cipherName TLS1.3-CHACHA20-POLY1305-SHA256 

bind ssl cipher SSL_Labs_Cipher_Group_Q1_2025 -cipherName TLS1.2-ECDHE-ECDSA-AES256-GCM-SHA384 

bind ssl cipher SSL_Labs_Cipher_Group_Q1_2025 -cipherName TLS1.2-ECDHE-ECDSA-AES128-GCM-SHA256 

bind ssl cipher SSL_Labs_Cipher_Group_Q1_2025 -cipherName TLS1.2-ECDHE-RSA-AES256-GCM-SHA384 


	
		Enable SSL Profiles 
	


set ssl parameter -defaultProfile ENABLED 


	
		Create an SSL Profile 
	


add ssl profile SSL_Labs_Profile_Q1_2025 -tls1 DISABLED -tls11 DISABLED -tls12 ENABLED -tls13 ENABLED -ocspStapling ENABLED -denySSLReneg NONSECURE -HSTS ENABLE -maxage 157680000


	
		Unbind the default cipher group from the SSL Profile and bind the custom group 
	


unbind ssl profile SSL_Labs_Profile_Q1_2025 -cipherName DEFAULT 

bind ssl profile SSL_Labs_Profile_Q1_2025 -cipherName SSL_Labs_Cipher_Group_Q1_2025 


	
		Throttle SSL Renegotiations  
	


set ssl profile SSL_Labs_Profile_Q1_2025 -maxrenegRate 100 


	 
 


	
		Note: 
	 

	
		The number of client-initiated SSL renegotiations expected within your environment will depend on the specific use case. While modern web applications rarely rely on SSL renegotiations due to their overhead and potential security risks, certain specialized or legacy systems may still utilize them for tasks such as client certificate re-authentication or renewing session keys in long-lived connections. 
	 



	 
 


	
		Unbind the default cipher group from the SSL Profile and bind the custom group 
	


unbind ssl profile SSL_Labs_Profile_Q1_2025 -cipherName DEFAULT 

bind ssl profile SSL_Labs_Profile_Q1_2025 -cipherName SSL_Labs_Cipher_Group_Q1_2025 


	
		Bind Trusted Curves 
	


unbind ssl profile SSL_Labs_Profile_Q1_2025 -eccCurveName ALL 

bind ssl profile SSL_Labs_Profile_Q1_2025 -eccCurveName X25519 

bind ssl profile SSL_Labs_Profile_Q1_2025 -eccCurveName P256 

bind ssl profile SSL_Labs_Profile_Q1_2025 -eccCurveName P384  

bind ssl profile SSL_Labs_Profile_Q1_2025 -eccCurveName P521 


	 
 


	
		Note: 
	 

	
		While the P-224 curve does not impact the SSL Labs score, security frameworks such as NIST and PCI DSS recommend a minimum security strength of 128 bits. The P-224 curve, with its 112-bit security level, does not meet this standard and is, therefore, unsuitable for most modern cryptographic applications. Additionally, P-224 offers no significant performance benefits over stronger curves like X25519 or P-256, making it an outdated choice for contemporary use.
	 



	 
 


	
		Bind the SSL Profile to the SSL virtual server 
	


set ssl vserver "Ex-vServer" -sslProfile SSL_Labs_Profile_Q1_2025 


	Ongoing Maintenance and Governance 



	This configuration strengthens your SSL/TLS posture and supports broader organizational goals such as compliance, customer trust, and operational resilience. By embedding these best practices into your security strategy, you ensure your systems are robust against current and emerging threats. 
 


	Maintaining secure SSL/TLS configurations requires ongoing governance. Test configurations regularly with tools like SSL Labs and automate reviews where possible. Establish clear documentation and processes to ensure compliance with evolving standards and organizational policies. 
 


	Regular updates and reviews are essential for adapting to new vulnerabilities, protocols, and organizational needs and ensuring that your environment remains secure and compliant over time. 
 


	Legacy client support 



	The ECDHE ciphers in this guide replace the older, slower DHE ciphers. If you have legacy clients you cannot upgrade, you may have to enable DHE. 
 


	We propose including a secure DHE cipher with modern features (GCM mode, SHA384). However, if your client is modern enough to support DHE with GCM mode, it should also be able to support the ECHDE ciphers. For this reason, if you must use a DHE cipher, you may need to work with your security team to consider the trade-off between supporting legacy clients and using older, less secure ciphers.  
 


	Enable DHE cipher suites in the GUI 
 


	
		Go to Traffic Management &gt; SSL and select Create Diffie-Hellman (DH) key 
	



	  
 


	 
 


	
		Note: 
	 

	
		Despite the name, this is not a Diffie-Hellman (DH) key in the traditional sense. Instead, it represents long-term DH group parameters, which the NetScaler will use alongside a newly generated ephemeral exponent each time it negotiates a key exchange. While the interface labels it as a "key," this is shorthand for the DH parameter file. When creating the “key,” the next screen will confirm that you are generating these group parameters, not a static DH key. 
	 



	 
 


	
		Name the Diffie-Hellman key “DH_Key_Name_Here.key” 
	
	
		Enter the parameter size (Bits). It must be between 512 and 2048 
	
	
		Choose the Diffie-Hellman generator (2 or 5) 
	
	
		Select Create . Depending on the key size selected, this could take quite some time to complete 
	



	  
 


	
		Note:
	 

	
		Do not navigate past this screen until it is complete.  
	 



	  
 


	
		Navigate to System &gt; Profiles &gt; SSL Profile 
	



	
		Select the SSL_Labs_Profile_Q1_2025 profile and choose Edit 
	



	
		Select the pencil icon 
	



	
		Scroll down to the Enable DH Param check box and select it 
	



	
		Choose the DH key you just created
	



	 
 


	  
 


	 
 


	
		Scroll down and select OK
	
	
		Bind a DHE cipher suite to the cipher group that we created earlier 
	
	
		Navigate to Traffic Management &gt; SSL &gt; Cipher Groups 
	
	
		Select the TLS1.2-DHE-RSA-AES256-GCM-SHA384 cipher 
	
	
		Click the arrow to add it to the Cipher Group 
	



	  
 


	 
 


	Enable DHE cipher suites in the CLI 
 


	
		Create and bind a DH key to the SSL Profile (CLI) 
	


create ssl dhparam DH_Key_Name_Here.key 2048 -gen 2 

set ssl profile SSL_Labs_Profile_Q1_2025 -dh ENABLED -dhFile DH_Key_Name_Here.key 


	
		Bind a DHE cipher suite to the cipher group that we created earlier 
	


bind ssl cipher SSL_Labs_Cipher_Group_Q1_2025 -cipherName TLS1.2-DHE-RSA-AES256-GCM-SHA384 


	 



	Firmware Notes 



	
		TLS 1.2 as Default  
		Enabled by default starting with firmware version 10.5 build 57. Earlier builds supported TLS 1.2 only on appliances with dedicated SSL hardware. 
	
	
		TLS 1.3 Support  
		Introduced in 12.1 build 49.23. To enable, configure in SSL parameters or enhanced SSL profiles. Requires binding TLS 1.3 ciphers, including AES-GCM and ChaCha20-Poly1305. Note: Legacy SSL profiles do not support TLS 1.3. 
	
	
		HSTS (HTTP Strict Transport Security)  
		Added in 12.0 build 35. Earlier builds required a rewrite policy to insert the HSTS header. Using both methods simultaneously is not permitted. 
	
	
		ECC Certificate Support  
		Expanded to VPX appliances in 12.0 build 57. Previously, only appliances with dedicated SSL hardware supported ECC certificates. 
	
	
		TLS 1.3 Hardware Acceleration  
		Introduced in 13.0 build 71, significantly improving cryptographic performance for TLS 1.3. 
	
	
		Mitigating CBC Vulnerabilities  
		Cipher updates removed known weak CBC ciphers (e.g., 0xc028 and 0x39) to address modern cryptographic risks. Disabling CBC-based cipher suites is recommended for compliance. 
	
	
		The Zombie POODLE vulnerability was addressed in builds 12.1 build 50.31, 12.0 build 60.9, 11.1 build 60.14, 11.0 build 72.17, and 10.5 build 69.5. This vulnerability only affects MPX\SDX appliances with Nitrox SSL hardware. MPX\SDX appliances with Coleto Creek are not vulnerable. Disabling CBC-based cipher suites also mitigates this vulnerability. See CTX article for more information 
	
	
		The ROBOT vulnerability was addressed in builds 12.0 build 53, 11.1 build 56, 11.0 build 71, and 10.5 build 67 - more details are available here 
	
	
		Support for Heal-the-BREACH (HTB) was added in 14.0 build 38.53 but is not included within this article as compression is disabled by default - more details are available here 
	
	
		Forward Secrecy with Modern Curves  
		X25519 curve added in 14.1 build 12.x for front-end TLS 1.3 support. Backend support for TLS 1.2 and TLS 1.3 introduced in 14.1 build 25.x. P-256, P-384, and P-521 remain supported for compatibility. 
	
	
		OCSP Stapling  
		Introduced in 12.1 build 55.18. Updated in 14.1 build 38.53 to include the OCSP status regardless of whether the status is cached.]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2025_01/image.png.0090ee12ee73b43968593fc9e6029366.png" length="28544" type="image/png"/><pubDate>Wed, 08 Jan 2025 15:07:00 +0000</pubDate></item><item><title>Horizon to Citrix Migration: What to Know</title><link>https://community.stage.citrix.com/tech-zone/learn/tech-briefs/horizon-to-citrix-what-to-know/</link><description>Overview



	Migrating from Omnissa Horizon to the Citrix Platform requires detailed planning and execution. Though both technologies allow users to access virtual desktops and apps, it is necessary to understand their differences and respective architectures.
 


	Both Omnissa and Citrix deliver applications and desktops to users, but they employ different technologies to achieve this. Omnissa supports various remoting protocols, including Blast Extreme, PCoIP, and RDP, offering flexibility for diverse environments and user needs. On the other hand, Citrix relies on its proprietary HDX technology, which builds upon the ICA remoting protocol. HDX offers a more integrated and optimized experience within Citrix environments, providing performance and management advantages over Omnissa. This document is intended to help Horizon Administrators understand the architectural differences between the two platforms rather than provide a feature-by-feature comparison.
 


	Citrix Platform Introduction



	The Citrix platform is a comprehensive, enterprise-wide solution that includes all of Citrix and NetScaler's capabilities. It provides access to virtual applications and desktops, Zero-Trust access, and high-performance, secure application delivery, all with end-to-end observability. With the Citrix platform, you can deliver, monitor, and manage secure access to enterprise content while ensuring high performance and security for any user on any device.
 


	Citrix Platform Architecture



	The Citrix platform provides both on-premises and cloud architectures to deliver multi-hybrid-cloud resources to your end users.
 


	Citrix Virtual Apps and Desktops



	
 


	Citrix Virtual Apps and Desktops are virtualization solutions that give IT control of virtual machines, applications, licensing, and security while providing access to any device anywhere. The solution allows end users to run applications and desktops independently of the device's operating system and interfaces, and it enables administrators to manage the network and control access from selected devices or all devices. The components in Citrix Virtual Apps and Desktops are broken down into layers.
 


	User Layer



	Citrix Workspace app &#x2013; The Citrix Workspace app is installed on client devices (desktops, laptops, tablets, mobile phones, thin clients) and provides users with quick, secure, self-service access to documents, applications, and desktops. 
 


	Access Layer



	NetScaler Gateway&#x2014;When users connect outside the corporate firewall, Citrix Virtual Apps and Desktops can use NetScaler Gateway to secure these connections with TLS. NetScaler MPX/SDX physical or NetScaler VPX virtual appliance is an SSL VPN deployed in the demilitarized zone (DMZ). It provides a single secure point of access through the corporate firewall.
 


	Citrix StoreFront &#x2013; Citrix StoreFront is a web server that authenticates users and manages stores of desktops and applications that users access. It can host your enterprise application store, giving users self-service access to the desktops and applications you make available. It also tracks users&#x2019; application subscriptions, shortcut names, and other data, helping ensure a consistent experience across multiple devices.
 


	Control Layer



	Delivery Controllers&#x2014;The Delivery Controller is the central management component of each Citrix Virtual Apps and Desktops site. It contains several services that allow it to communicate with the hypervisor or hyperscaler to distribute applications and desktops, authenticate and manage user access, broker connections between users and their resources, load balance connections, and optimize user connections. Other services track which users are logged on and where session resources are being used, and data is being monitored for troubleshooting and historical purposes.
 


	License Server - The License Server manages your Citrix product licenses. It communicates with the Controller to manage licensing for each user&#x2019;s session and with Web Studio to allocate license files. A site must have at least one license server to store and manage your license files.
 


	Web Studio - Web Studio is a web-based management console that lets you configure and manage your Citrix Virtual Apps and Desktops deployment.
 


	Director - Director is a web-based tool that enables IT support and help desk teams to monitor an environment, troubleshoot issues before they become system-critical, and perform support tasks for end users. One Director deployment can connect to and monitor multiple Citrix Virtual Apps or Citrix Virtual Desktops sites.
 


	Secure Private Access&#x2014;Citrix Secure Private Access, an on-premises solution, enhances an organization&#x2019;s overall security and compliance posture by easily delivering Zero Trust Network Access to browser-based apps (internal web apps and SaaS apps) using StoreFront as a unified access portal to web and SaaS apps, along with virtual apps and desktops.
 


	Workspace Environment Management - Workspace Environment Management uses intelligent resource management and profile management technologies to deliver the best possible performance, desktop logon, and application response times for Citrix Virtual Apps and Desktops deployments. It is a software-only, driver-free solution.
 


	Databases &#x2013; Microsoft SQL server databases are required for every Citrix Virtual Apps and Desktops site. Three databases are needed for each site. Each database contains specific information for its database roles, such as the Site database, which stores the running configuration and current session state and connection information. The Logging database stores information about the site configuration changes and administrative activities, and the Monitoring database stores the session and connection information to be used by the Citrix Director monitoring tool. Workspace Environment Management also requires a SQL server database to store its settings. Secure Private Access requires a database for information about the applications, policies, and related artwork. It also includes information related to troubleshooting and telemetry.
 


	Resource Layer



	Virtual Delivery Agent (VDA)&#x2014;Each physical or virtual machine that delivers resources (applications and desktops) must have a Citrix VDA installed. VDAs establish and manage the connection between the machine on which they&#x2019;re installed and the user device and apply policies configured for the session.
 


	uberAgent &#x2013; Provides visibility across physical and virtual Windows servers, client operating systems, and MacOS endpoints. 
 


	Compute Layer



	Citrix Virtual Apps and Desktops infrastructure and resources can be fully deployed in an on-premises data center, supported public cloud, or hybrid approach. 
 


	Citrix DaaS



	
 


	User Layer



	Citrix Workspace app &#x2013; The Citrix Workspace app is installed on client devices (desktops, laptops, tablets, mobile phones, thin clients) and provides users with quick, secure, self-service access to documents, applications, and desktops. 
 


	Access Layer



	Citrix Workspace&#x2014;Citrix Workspace is a cloud-hosted service that provides secure access to virtual apps and desktops, web, and SaaS apps from a web browser or Citrix Workspace app. Citrix manages this service.
 


	Gateway service &#x2013; The Citrix Gateway service is a Citrix-managed service that provides secure remote access to Citrix DaaS desktops and applications without deploying NetScaler Gateway in an on-premises DMZ. 
 


	Control Layer



	Citrix DaaS&#x2014;Citrix DaaS is a service that provides app and desktop virtualization. The components (Delivery Controllers, Databases, Licensing, Studio, Director) mentioned within the Citrix Virtual Apps and Desktops architecture are managed and maintained by Citrix as a service.
 


	Secure Private Access&#x2014;The Citrix Secure Private Access service enables administrators to provide a cohesive experience by integrating single sign-on, remote access, and content inspection into a single solution for end-to-end access control. Thus, administrators can govern access to approved SaaS apps with a simplified single sign-on experience.
 


	Workspace Environment Management &#x2013; The Workspace Environment Management service uses intelligent resource management and Profile Management technologies to deliver the best possible performance, desktop logon, and application response times. 
 


	Monitor&#x2014;The Monitor console enables IT support and help desk teams to monitor an environment, troubleshoot issues before they become critical, and perform support tasks for end users. 
 


	Web Studio - Studio is a web-based central portal that lets you configure, manage, and monitor your DaaS deployments for delivering virtual apps and desktops.
 


	Resource Layer



	Cloud Connector&#x2014;Cloud Connectors are the communications channel between the Citrix Cloud and resource location components. In the resource location, the Cloud Connector acts as a proxy for the Delivery Controller in Citrix Cloud. Citrix updates and manages the Cloud Connector software.
 


	Virtual Delivery Agent (VDA)&#x2014;Each physical or virtual machine that delivers resources (applications and desktops) must have a Citrix VDA installed. VDAs establish and manage the connection between the machine on which they&#x2019;re installed and the user device and apply policies configured for the session.
 


	uberAgent &#x2013; Provides visibility across physical and virtual Windows servers, client operating systems, and MacOS endpoints.
 


	Compute Layer
 


	Citrix DaaS resources can be fully deployed in an on-premises data center, supported public cloud, or hybrid approach. 
 


	Additional Components



	Many additional Citrix components are available for both Citrix Virtual Apps and Desktops and Citrix DaaS deployments. These include:
 


	Citrix Profile Management &#x2013; A profile solution for Citrix deployments can be installed on each Virtual Delivery Agent.
 


	Federated Authentication Service - Federated Authentication Service (FAS) is a privileged component that integrates with Active Directory Certificate Services. It dynamically issues certificates for users, allowing them to log on to an Active Directory environment as if they had a smart card. This allows StoreFront to use more authentication options, such as SAML (Security Assertion Markup Language) assertions. SAML is commonly used as an alternative to traditional Windows user accounts on the Internet.
 


	Citrix Provisioning - Citrix Provisioning is software streaming technology that delivers patches, updates, and configuration information to multiple virtual desktop endpoints through a shared desktop image. It centralizes virtual machine management while reducing a virtualized desktop environment's operational and storage costs.
 


	App Layering&#x2014;Citrix App Layering is a technology that simplifies the management of virtual desktop images by separating the operating system and applications into distinct "layers." Administrators can independently update and manage each layer, creating a more streamlined approach to deploying and maintaining applications across different user groups and environments.
 


	Endpoint Management&#x2014;Citrix Endpoint Management is a solution for managing endpoints. It offers Mobile Device Management (MDM) and mobile application management (MAM) capabilities. It allows you to manage device and app policies and deliver apps to users. 
 


	Global App and Configuration Service &#x2013; Cloud service to help manage Citrix Workspace app settings for end users on managed and unmanaged devices. Settings can be configured for both Citrix Workspace and Citrix StoreFront environments.
 


	XenServer - XenServer is a virtualization platform that allows organizations to create and manage virtualized server infrastructures. It is designed to optimize the delivery of Windows and Linux virtual machines, providing a robust and scalable solution for data center virtualization.
 


	Session Recording&#x2014;Session Recording records, catalogs, and archives sessions for retrieval and playback. It provides flexible policies to automatically trigger recordings of application and desktop sessions and supports dynamic session recording.
 


	deviceTRUST introduces real-time contextual access for virtual desktop infrastructure (VDI) and desktop-as-a-service (DaaS) environments, including locally installed applications. It enables granular access control based on device posture and user context, allowing organizations to continuously monitor and respond to changes, strengthen security, and minimize endpoint risk.
 


	Strong Network &#x2013; Strong Network provides secure cloud development environments where mission-critical applications can be built, launched, and accessed more efficiently and cost-effectively. 
 


	Terms to Know



	If you're a Horizon administrator, you're likely familiar with the terminology and concepts of managing your Horizon deployment. To help with your migration efforts, here's a table highlighting the key differences between Omnissa and Citrix.
 


	
		
			
				
					Omnissa Term/Concept
				 
			
			
				
					Citrix Term/Concept
				 
			
		
		
			
				
					Connection Server
				 
			
			
				
					Delivery Controller
				 
			
		
		
			
				
					Linked Clone
				 
			
			
				
					Machine Creation Services
				 
			
		
		
			
				
					Connection Server
				 
			
			
				
					StoreFront
				 
			
		
		
			
				
					Universal Access Gateway
				 
			
			
				
					NetScaler Gateway
				 
			
		
		
			
				
					Horizon Console
				 
			
			
				
					Web Studio
				 
			
		
		
			
				
					Replicas
				 
			
			
				
					Master Images
				 
			
		
		
			
				
					Pools
				 
			
			
				
					Machine Catalogs
				 
			
		
		
			
				
					Application Pools
				 
			
			
				
					Delivery Groups
				 
			
		
		
			
				
					Floating Dedicated, Dedicated
				 
			
			
				
					Random Non-Persistent, Static Non-Persistent, Static Persistent
				 
			
		
		
			
				
					Entitlements
				 
			
			
				
					Assignments
				 
			
		
		
			
				
					App Volumes
				 
			
			
				
					App Layering
				 
			
		
		
			
				
					Horizon Agent
				 
			
			
				
					Virtual Delivery Agent (VDA)
				 
			
		
		
			
				
					Horizon Client
				 
			
			
				
					Citrix Workspace app
				 
			
		
		
			
				
					Horizon Enrollment Server
				 
			
			
				
					Federated Authentication Service
				 
			
		
		
			
				
					Horizon Edge Gateway Appliance (next-gen cloud service)
				 
			
			
				
					Cloud Connector
				 
			
		
		
			
				
					Horizon Cloud Connector (first gen)
				 
			
			
				
					Cloud Connector
				 
			
		
		
			
				
					Dynamic Environment Manager
				 
			
			
				
					Profile Management
				 
			
		
		
			
				
					ESXi
				 
			
			
				
					XenServer
				 
			
		
		
			
				
					Extended Service Branch (ESB)
				 
			
			
				
					Long Term Service Release (LTSR)
				 
			
		
	



	Available Resources



	Beyond the links in this document, several resources are available to help you get started with Citrix. These include:
 


	Citrix Product Documentation
 


	Citrix DaaS POC Guide
 


	Citrix Virtual Apps and Desktops POC Guide
 


	Citrix Terraform Provider
 


	Citrix Features Explained: Citrix DaaS
 


	Additionally, as part of your Citrix license, Citrix training is available for free at Pluralsight.</description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_12/image.png.801ec1a0eb08c28f9c67add23f410c58.png" length="120845" type="image/png"/><pubDate>Fri, 20 Dec 2024 15:03:00 +0000</pubDate></item><item><title>Automated Monitoring of Cloud Connector Health and Metrics</title><link>https://community.stage.citrix.com/tech-zone/build/tech-papers/automate-cc-health/</link><description><![CDATA[OverviewThis Tech Paper focuses on automated monitoring of already deployed Cloud Connectors in a Citrix DaaS Resource Location using REST-API calls, PowerShell scripts, and a self-written .NET application. REST API calls are the most flexible way to trigger health checks on the Cloud Connectors and retrieve records of past health events. The Advanced HealthData API provides a detailed time series of metrics and connectivity data from all services running on the Cloud Connectors. REST-API calls are Platform- and Client-independent as they rely solely on the HTTP protocol and use standardized HTTP methods like GET, POST, PUT, and DELETE. The .NET application should be an example of using standardized Citrix Cloud APIs in your application. The Cloud Connector is a key element for a fully functional deployment of a Resource Location for Citrix DaaS. It serves as a communication channel between Citrix Cloud and your Resource Locations. As long as at least one Cloud Connector per Resource Location is available, there will be no loss in communication with Citrix Cloud. Note: You can find more information about the Cloud Connectors in our Product Documentation.   Before and after updates, the Cloud Connectors perform health checks to ensure that updates cause no unnecessary downtime for providers - you can also manually trigger a health check in Citrix Cloud:   The Dashboard in Citrix Cloud also provides the connectivity and health status of the Cloud Connectors and each service or provider on them:   However, it seems more sensible to automate the monitoring, which would allow for regular functionality checks, communication tests, and metrics storage. We will demonstrate these checks using REST API calls and PowerShell snippets and implement them in a self-written .NET application. The Advanced HealthData API provides a detailed time series of metric and connectivity data from all services running on the Cloud Connectors.   PrerequisitesBefore retrieving data from the Advanced HealthData API, you need information from our Citrix Cloud tenant. This guide uses the Postman app to showcase the REST-API calls. Download the Postman app for API calls: https://www.postman.com/downloads/ You must install Citrix DaaS Remote PowerShell SDK for all PowerShell-related code.   Retrieve the Customer IDYou need to retrieve the Customer ID. It can be found in the Account Settings of your Citrix Cloud dashboard. Write it down or save it for further use:    Create an API ClientYou need to create an API client. API clients are automatically restricted to the rights of the administrator who created it and are always tied to one administrator and one customer. Select the Identity and Access Management option from the menu to create an API client. If this option does not appear, you may not have adequate permissions to create an API client.    Click on API Access -&gt; Secure clients:  Enter a comprehensive name for the API Client and click on Create Client:  Please copy and store the ID and the Secret of the just created API Client – you cannot reaccess these after closing the pop-up!    Obtain a Bearer Token for AuthenticationA Bearer Token is required for actions that take place on behalf of a user. It secures HTTP requests sent to Citrix Cloud. You must use a custom Authorization header containing a valid Citrix Cloud Bearer Token. You can create the required Bearer Token using all three methods – using REST-API calls with Postman, using PowerShell, and using the .NET app.   Flow to obtain a Bearer Token using PostmanIt is essential to set the URI to call and the required parameters correct: The URI must follow this syntax: [https://api-us.cloud.com/cctrustoauth2/{customerid}/tokens/clients] where {customerid} is the Customer ID you obtained from the Account Settings page.  For example, if your Customer ID is 1234567890, the URI would be: [https://api-us.cloud.com/cctrustoauth2/1234567890/tokens/clients]. If you use variables in Postman, ensure the correct variables are used. Paste the correct URI into Postman´s address bar and select POST as the method. Verify the API call settings are correct.  After sending the API call, the response should contain a valid Bearer Token:  Please be aware that the Bearer Token is only valid for 3600 seconds. Code Snippets: API-Call: POST https://api-us.cloud.com/cctrustoauth2/{customerid}/tokens/clients Response: {     "token_type": "bearer",     "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…pZWFOBHuZ63tvGvRA",     "expires_in": "3600" } You have successfully created a Bearer Token, which you need for all further API calls.   Flow to obtain a Bearer Token using PowerShellTo obtain a Bearer Token using PowerShell, use the following Code snippet. You must provide all adjacent variable values according to your tenant: PS C:\_TACG&gt; $tokenUrl = 'https://api-eu.cloud.com/cctrustoauth2/1234567890/tokens/clients' PS C:\_TACG&gt; $response = Invoke-WebRequest $tokenUrl -Method POST -Body @{ &gt;&gt; grant_type = "client_credentials"                                                                                     &gt;&gt; client_id = "5f4bXXXX-XXXX-XXXX-XXXX-XXXXXXXX313e"                                                                   &gt;&gt; client_secret = "5cXXXXXXXXXXXXXXXXXXXXXXXXXXX=="                                                                           &gt;&gt; }                                                                                                                    PS C:\_TACG&gt; $token = $response.Content | ConvertFrom-Json PS C:\_TACG&gt; $token | Format-List  token_type   : bearer access_token : eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJmNDc2Y2ZhZC1mYjUxLTRjNGUtYWEzOS00NTk1MDIwMmNkYzYiLCJ1c2V ... twxt7f1WMgA0HYpBeGoHHIym-_kzcm8maGaslAPFg65W-Otcqeh04LS              1pKxfH1Pj1EoB4tSRkg expires_in   : 3600  PS C:\_TACG&gt; You have successfully created a Bearer Token, which you need for all further API calls.   Flow to obtain a Bearer Token using the .NET ApplicationTo obtain a Bearer Token using the .NET Application, use the following Code snippet. Update all variables in the adjacent JSON file according to your tenant:  The application automatically requests a Bearer Token after start:  Code Snippets: JSON-File: {   "CustomerID": "dcXXXXXXXXXXXX",   "ClientID": "5f4bXXXX-XXXX-XXXX-XXXX-XXXXXXXX313e",   "ClientSecret": "5cXXXXXXXXXXXXXXXXXXXX==",   "GrantType": "client_credentials" } Encapsulating REST-API call (excerpts): Try   Dim success As Boolean = rest.Connect("api-eu.cloud.com", port, bTls, bAutoReconnect)  If (success &lt;&gt; True) Then     Debug.WriteLine(rest.LastErrorText)   Exit Sub End If success = rest.AddQueryParam("grant_type", obj_CCAPI_Token.GrantType) success = rest.AddQueryParam("client_id", obj_CCAPI_Token.ClientID) success = rest.AddQueryParam("client_secret", obj_CCAPI_Token.ClientSecret) Dim APIPostCallPath As String = "/cctrustoauth2/" &amp; obj_CCAPI_Token.CustomerID &amp; "/tokens/clients" Dim s_Response As String = Nothing obj_BearerToken = New CCAPI_BearerToken s_Response = rest.FullRequestFormUrlEncoded("POST", APIPostCallPath) obj_BearerToken = JsonConvert.DeserializeObject(Of CCAPI_BearerToken)(s_Response, JSONSettings) Catch exc As Exception    Console.WriteLine(exc.ToString)  End Try If s_Response &lt;&gt; "" Then    Dim json As JObject = JObject.Parse(s_Response)    obj_BearerToken.TokenType = json.SelectToken("token_type")    obj_BearerToken.Expiry = json.SelectToken("expires_in")    obj_BearerToken.AccessToken = json.SelectToken("access_token")    txtParams(0) = txt_status    txtParams(1) = "Successfully obtained Bearer-Token..." &amp; vbCrLf    Me.Invoke(New WriteTextDelegate(AddressOf WriteText), txtParams)    txtParams(0) = txt_status    txtParams(1) = "-------------------------------" &amp; vbCrLf &amp; vbCrLf    Me.Invoke(New WriteTextDelegate(AddressOf WriteText), txtParams)   End If   Obtain the Resource LocationsAfter obtaining the Customer ID and the Bearer Token, you must decide which Resource Location to query. It would be best to create adjacent configurations for each needed Resource Location.   Flow to obtain the Resource Locations using PostmanIt is essential to set the URI to call and the required parameters to be correct. The URI must follow this syntax: https://api-eu.cloud.com/resourcelocations  If you use variables in Postman, ensure the correct variables are used. Paste the correct URI into Postman´s address bar and select GET as the method. Verify the correct settings of the API call.  After sending the API call, the response should contain a list of all Resource Locations:  You have successfully obtained the IDs of the Resource Locations, which you need for all future API calls. Choose the ID of the Resource Location you want to query. Please write it down or save it for future use. Code Snippets: API-Call: GET https://api-eu.cloud.com/resourcelocations Response: {     "items": [         {             "id": "08b2XXXX-XXXX-XXXX-XXXX-XXXXXXXX27c3",             "name": "TACG-TF-Azure",             "internalOnly": false,             "timeZone": "GMT Standard Time",             "readOnly": false         },         {             "id": "2497XXXX-XXXX-XXXX-XXXX-XXXXXXXX4b93",             "name": "TACG-Azure",             "internalOnly": false,             "timeZone": "GMT Standard Time",             "readOnly": false         },         {             "id": "3c78XXXX-XXXX-XXXX-XXXX-XXXXXXXXa970",                              "name": "EBC-Azure-EU-West",                              "internalOnly": false,             "timeZone": "GMT Standard Time",             "readOnly": false         },         {             "id": "e53fXXXX-XXXX-XXXX-XXXX-XXXXXXXX2c70",             "name": "TACG-vSphere ",             "internalOnly": false,             "timeZone": "GMT Standard Time",             "readOnly": false         }     ] }   Flow to obtain the Resource Locations using PowerShellTo obtain the Resource Locations using PowerShell, use the following Code snippet: PS C:\_TACG&gt; Get-XDAuthentication PS C:\_TACG&gt; Get-ConfigZone | select Name, ExternalUid  Name              ExternalUid ----              ----------- EBC-Azure-EU-West 3c78XXXX-XXXX-XXXX-XXXX-XXXXXXXXa970 Initial Zone      0000XXXX-XXXX-XXXX-XXXX-XXXXXXXX0000 TACG-Azure        2497XXXX-XXXX-XXXX-XXXX-XXXXXXXX4b93 TACG-TF-Azure     08b2XXXX-XXXX-XXXX-XXXX-XXXXXXXX27c3 TACG-vSphere      e53fXXXX-XXXX-XXXX-XXXX-XXXXXXXX2c70  PS C:\_TACG&gt; You have successfully obtained the IDs of the Resource Locations, which you need for all future API calls. Choose the ID of the Resource Location you want to query. Please write it down or save it for future use.   Flow to obtain the Resource Locations using the .NET ApplicationThe .NET Application automatically retrieves all Resource Locations adjacent to the configured API Client. You can choose the Resource Location you want in a Drop-Down-Box:  Code Snippets: Encapsulating REST-API call (excerpts): Dim success As Boolean = rest.Connect("api-eu.cloud.com", port, bTls, bAutoReconnect) success = rest.AddHeader("Citrix-CustomerId", obj_CCAPI_Token.CustomerID) success = rest.AddHeader("Authorization", "CWSAuth bearer=" &amp; obj_BearerToken.AccessToken.ToString) success = rest.AddHeader("Accept", "application/json") Dim APIPostCallPath As String = "/resourcelocations/" Dim s_Response As String = Nothing obj_RL = New CCAPI_ResourceLocation s_Response = rest.FullRequestFormUrlEncoded("GET", APIPostCallPath) obj_RL = JsonConvert.DeserializeObject(Of CCAPI_ResourceLocation)(s_Response, JSONSettings) For i_Counter = 0 To obj_RL.items.Count - 1    cmb_rl.Items.Add(obj_RL.items(i_Counter).Name) Next   Trigger Health Checks on Cloud ConnectorsThe Cloud Connectors automatically perform health checks before and after updates to ensure that they do not cause providers unnecessary downtime. You can manually trigger a Health Check - e.g., before querying the Advanced HealthData API.   Flow to Trigger a Health Check using PostmanIt is essential to set the URI to call and the required parameters correct: The URI must follow this syntax:  https://cws.citrixworkspacesapi.net/{customerid}/healthdatarequests If you use variables in Postman, ensure the correct variables are used. Paste the correct URI into Postman´s address bar and select POST as the method. Verify the correct settings of the API call also on the Body tab:    If the trigger was successful, the response should show:  Code Snippets: API-Call: POST https://cws.citrixworkspacesapi.net/{Customer-ID}/healthdatarequests Request Body: {     "objectIdentifier": "{CloudConnector-ID}",     "objectType": "EdgeServers" } Response if Trigger was successful: {     "success": true }   Flow to Trigger a Health Check using PowerShellTo trigger a Health Check using PowerShell, use the following Code snippet. You must provide all adjacent variable values according to your tenant: PS C:\_TACG&gt; $CCID = 'dcXXXXXXXXXXXX' PS C:\_TACG&gt; $ClCoID = '4fb6XXXX-XXXX-XXXX-XXXX-XXXXXXXXe0ec' PS C:\_TACG&gt; $BT = 'eyJhb..._YzBQ' PS C:\_TACG&gt;  PS C:\_TACG&gt; Get-XDAuthentication  PS C:\_TACG&gt; $triggerUrl = "https://cws.citrixworkspacesapi.net/" + $CCID +"/healthdatarequests" PS C:\_TACG&gt; PS C:\_TACG&gt; $Headers = @{ &gt;&gt; 'Authorization' = "CWSAuth bearer=$BT" &gt;&gt; 'Content-Type' = "application/json" &gt;&gt; 'Citrix-CustomerID' = $CCID &gt;&gt; 'Accept-Encoding' = "gzip, deflate, br" &gt;&gt; } PS C:\_TACG&gt; PS C:\_TACG&gt; $BodyJSON = '{ "objectIdentifier":"' + $ClCoID +'", "objectType":"edgeServers" }' PS C:\_TACG&gt; $response = Invoke-WebRequest -URI $triggerUrl -Method POST -Headers $Headers -Body $BodyJSON | ConvertFrom-Json PS C:\_TACG&gt; $response  success -------    True PS C:\_TACG&gt;   Flow to Trigger a Health Check using the .NET ApplicationYou can trigger a Health Check for all Cloud Connectors in the selected Resource Location by clicking “Request a Health Check of all Cloud Connectors in selected Resource Location”:    Get Cloud Connector DataThe Citrix Cloud API for managing the Cloud Connectors provides detailed information about each. The Advanced HealthData API offers 2 API Endpoints to connect to – you can find the detailed Swagger file in the Appendix: ConnectivityData MetricData At first, you can retrieve all Cloud Connectors and its generic properties using the Citrix Cloud API in the chosen Resource Location.   Retrieve Generic Cloud Connector Data using PostmanThe URI must follow this syntax: https://agenthub.citrixworkspacesapi.net/{Customer-ID}/edgeservers?location={ResourceLocation-ID}&amp;connectorType=All    If you use variables in Postman, ensure the correct variables are used. Paste the correct URI into Postman´s address bar and select GET as the method. Verify the API call settings are correct.  After sending the API call, the response contains a list of generic data of all Cloud Connectors in the chosen Resource Location:  You have successfully obtained generic data of all Cloud Connectors in the chosen Resource Location. Choose the ID of the Cloud Connector you want to query. Please write it down or save it for further use. Code Snippets: API-Call: GET https://agenthub.citrixworkspacesapi.net/{{VAR-DEMOCLOUD-CCID}}/edgeservers?location={{VAR-DEMOCLOUD-RLID}}&amp;connectorType=All Response: [     {         "id": "1222XXXX-XXXX-XXXX-XXXX-XXXXXXXXc8cf",         "fqdn": "tmm-ebc-ca-02.democloud.corp",         "role": "UnifiedDefault",         "windowsSid": null,         "location": "3c78XXXX-XXXX-XXXX-XXXX-XXXXXXXXa970",         "currentVersion": "7.3.1.366",         "currentBootstrapperVersion": null,         "expectedVersion": null,         "expectedBootStrapperVersion": null,         "versionState": "Unknown",         "inMaintenance": false,         "leaseEndDateTime": null,         "upgradeDisabled": false,         "connectorType": "Unified",         "status": "Unknown",         "lastContactDate": "2024-12-17T10:26:19.1129972Z"     },     {         "id": "4fb6XXXX-XXXX-XXXX-XXXX-XXXXXXXXe0ec",         "fqdn": "tmm-ebc-cc-01.democloud.corp",         "role": null,         "windowsSid": "S-1-5-21-XXXX-XXXX-XXXX-XXXX-XXXXXXXX",         "location": "3c78XXXX-XXXX-XXXX-XXXX-XXXXXXXXa970",         "currentVersion": "4.368.0.132",         "currentBootstrapperVersion": "6.108.0.132",         "expectedVersion": "4.368.0.132",         "expectedBootStrapperVersion": "6.108.0.132",         "versionState": "Normal",         "inMaintenance": false,         "leaseEndDateTime": null,         "upgradeDisabled": false,         "connectorType": "Windows",         "status": "Unknown",         "lastContactDate": "2024-12-17T10:26:03.8195363Z"     },     {         "id": "58ceXXXX-XXXX-XXXX-XXXX-XXXXXXXX3356",         "fqdn": "tmm-ebc-ca-01.democloud.corp",         "role": "UnifiedDefault",         "windowsSid": null,         "location": "3c78XXXX-XXXX-XXXX-XXXX-XXXXXXXXa970",         "currentVersion": "7.3.1.366",         "currentBootstrapperVersion": null,         "expectedVersion": null,         "expectedBootStrapperVersion": null,         "versionState": "Unknown",         "inMaintenance": false,         "leaseEndDateTime": null,         "upgradeDisabled": false,         "connectorType": "Unified",         "status": "Unknown",         "lastContactDate": "2024-12-17T10:26:12.198522Z"     },     {         "id": "6385XXXX-XXXX-XXXX-XXXX-XXXXXXXXbe81",         "fqdn": "tmm-ebc-cc-02.democloud.corp",         "role": null,         "windowsSid": "S-1-5-21-XXXX-XXXX-XXXX-XXXX-XXXXXXXX",         "location": "3c78XXXX-XXXX-XXXX-XXXX-XXXXXXXXa970",         "currentVersion": "4.368.0.132",         "currentBootstrapperVersion": "6.108.0.132",         "expectedVersion": "4.368.0.132",         "expectedBootStrapperVersion": "6.108.0.132",         "versionState": "Normal",         "inMaintenance": false,         "leaseEndDateTime": null,         "upgradeDisabled": false,         "connectorType": "Windows",         "status": "Unknown",         "lastContactDate": "2024-12-17T10:23:27.815303Z"     } ]   Retrieve Generic Cloud Connector Data using PowerShellUsing PowerShell, use the following Code snippet to obtain the generic Cloud Connector Data in your chosen Resource Location:   PS C:\_TACG&gt; Get-XDAuthentication PS C:\_TACG&gt; Get-ConfigZone | select Name, ExternalUid Name              ExternalUid ----              ----------- EBC-Azure-EU-West 3c78XXXX-XXXX-XXXX-XXXX-XXXXXXXXa970 Initial Zone      0000XXXX-XXXX-XXXX-XXXX-XXXXXXXX0000 TACG-Azure        2497XXXX-XXXX-XXXX-XXXX-XXXXXXXX4b93 TACG-TF-Azure     08b2XXXX-XXXX-XXXX-XXXX-XXXXXXXX27c3 TACG-vSphere      e53fXXXX-XXXX-XXXX-XXXX-XXXXXXXX2c70  PS C:\_TACG&gt; Get-ConfigEdgeServer -ZoneName EBC-Azure-EU-West   ConnectorType                : Windows CpuCores                     : 1 CpuLogicalProcessors         : 2 CpuSockets                   : 1 DataLastReceivedTime         : 17.12.2024 12:21:05 Description                  : IsHealthy                    : True LastCbpTrafficTime           : 17.12.2024 12:19:13 LastCtxStaTrafficTime        : LastNFuseTrafficTime         : LastStateChangeTimeInUtc     : 17.12.2024 12:32:45 LhcConsecutiveImportFailures : 0 MachineAddress               : tmm-ebc-cc-01.democloud.corp MetadataMap                  : {[ResourceLocation, 3c78XXXX-XXXX-XXXX-XXXX-XXXXXXXXa970]} Name                         : tmm-ebc-cc-01_democloud_corp_4fb62bda64a04848a445695c070ae0ec RamInGB                      : 8 SfAdvHealthCheckConfigured   : Sid                          : S-1-5-21-XXXX-XXXX-XXXX-XXXX-XXXXXXXX TenantId                     : Uid                          : bc09XXXX-XXXX-XXXX-XXXX-XXXXXXXXcda9 Uuid                         : 4fb6XXXX-XXXX-XXXX-XXXX-XXXXXXXXe0ec ZoneName                     : EBC-Azure-EU-West ZoneUid                      : f5d8XXXX-XXXX-XXXX-XXXX-XXXXXXXXc9e9  ConnectorType                : Windows CpuCores                     : 1 CpuLogicalProcessors         : 2 CpuSockets                   : 1 DataLastReceivedTime         : 17.12.2024 12:08:08 Description                  : IsHealthy                    : True LastCbpTrafficTime           : 13.12.2024 15:47:41 LastCtxStaTrafficTime        : LastNFuseTrafficTime         : LastStateChangeTimeInUtc     : 17.12.2024 12:32:44 LhcConsecutiveImportFailures : 0 MachineAddress               : tmm-ebc-cc-02.democloud.corp MetadataMap                  : {[ResourceLocation, 3c78XXXX-XXXX-XXXX-XXXX-XXXXXXXXa970]} Name                         : tmm-ebc-cc-02_democloud_corp_63852652425540d9abed770693aebe81 RamInGB                      : 8 SfAdvHealthCheckConfigured   : Sid                          : S-1-5-21-XXXX-XXXX-XXXX-XXXX-XXXXXXXX TenantId                     : Uid                          : 5b92XXXX-XXXX-XXXX-XXXX-XXXXXXXX358f Uuid                         : 6385XXXX-XXXX-XXXX-XXXX-XXXXXXXXbe81 ZoneName                     : EBC-Azure-EU-West ZoneUid                      : f5d8XXXX-XXXX-XXXX-XXXX-XXXXXXXXc9e9   PS C:\_TACG&gt; You have successfully obtained the generic data of the Cloud Connectors in the chosen Resource Location. Select the UUID of the Cloud Connector you want to query further. Then, write it down or save it for later use.     Retrieve Generic Cloud Connector Data using the .NET ApplicationThe .NET Application automatically retrieves all Cloud Connectors adjacent to the selected Resource Location. You can choose the Resource Location you want to query in a Drop-Down-Box:  Code Snippets: Encapsulating REST-API call (excerpts): Dim success As Boolean = rest.Connect("agenthub.citrixworkspacesapi.net", port, bTls, bAutoReconnect) success = rest.AddHeader("Citrix-CustomerId", obj_CCAPI_Token.CustomerID) success = rest.AddHeader("Authorization", "CWSAuth bearer=" &amp; obj_BearerToken.AccessToken.ToString) success = rest.AddHeader("Accept", "application/json") Dim APIPostCallPath As String = "/" &amp; obj_CCAPI_Token.CustomerID &amp; "/edgeservers?location=" &amp; obj_RL_ID &amp; "&amp;connectorType=All" Dim s_Response As String = Nothing obj_CC = New List(Of CCAPI_CloudConnector) s_Response = rest.FullRequestFormUrlEncoded("GET", APIPostCallPath)  If s_Response &lt;&gt; "" Then     obj_CC = JsonConvert.DeserializeObject(Of List(Of CCAPI_CloudConnector))(s_Response)        dgvcc.DataSource = CreateDataTable(Of CCAPI_CloudConnector)(obj_CC) End If After querying the generic Cloud Connector data, you can retrieve the Connectivity metrics of the Cloud Connectors using the Advanced HealthData API in the chosen Resource Location.   Retrieve Cloud Connector Connectivity Metrics using PostmanThe URI must follow this syntax: https://api-eu.cloud.com/healthdata/connectivitydata?edgeServerId={CloudConnector-ID} If you use variables in Postman, ensure the correct variables are used. Paste the correct URI into Postman´s address bar and select GET as the method. Verify the API call settings are correct.  The Response contains all Connectivity-related data – the Timestamps are in the Epoch-Format – you need an Epoch converter tool for viewing them in the standard Date/Time format:  You have successfully obtained the Connectivity Metric data. Code Snippets: API-Call: GET https://api-eu.cloud.com/healthdata/connectivitydata?edgeServerId={CloudConnector-ID} Response: See Appendix   Retrieve Cloud Connector Connectivity Metrics using PowerShellUsing PowerShell, use the following Code snippet to obtain Connectivity Metrics of your chosen Cloud Connector: PS C:\_TACG&gt; Get-XDAuthentication PS C:\_TACG&gt; $CCID = 'dcint77d970d' PS C:\_TACG&gt; $ClCoID = '4fb6XXXX-XXXX-XXXX-XXXX-XXXXXXXXe0ec' PS C:\_TACG&gt; $BT = 'eyJh...0bPw' PS C:\_TACG&gt; PS C:\_TACG&gt; $metricsUrl = "https://api-eu.cloud.com/healthdata/connectivitydata?edgeServerId=" + $ClCoID PS C:\_TACG&gt; $Headers = @{ &gt;&gt; 'Authorization' = "CWSAuth bearer=$BT" &gt;&gt; 'Content-Type' = "application/json" &gt;&gt; 'Citrix-CustomerID' = $CCID &gt;&gt; 'Accept' = "application/json" &gt;&gt; } PS C:\_TACG&gt; $response = Invoke-WebRequest -URI $metricsUrl -Method GET -Headers $Headers PS C:\_TACG&gt; $response | FT Content  Content ------- {"overallConnectorConnectivity":{"1733915640":"Connected","1733919241":"Connected","1733922841":"Connected","1733926...   PS C:\_TACG&gt; You can further use the JSON data based on the Content property. Please note that the Timestamps are in Epoch format—you need an Epoch converter tool to view them in the standard Date/Time format.   Retrieve Cloud Connector Connectivity Metrics using the .NET ApplicationThe .NET Application automatically retrieves the Connectivity Metrics of a Cloud Connector adjacent to the selected Resource Location and exports the data as a JSON-based file. Choose the Cloud Connector you want to query and press Export Connectivity-Data of selected Cloud Connector:  The application retrieves the metrics and exports the data as a JSON-based file in the configured directory on the local file system:  After querying the Connectivity Metrics, you can retrieve the Cloud Connectors' Health metrics using the Advanced HealthData API in the chosen Resource Location.   Retrieve Cloud Connector Health Metrics using PostmanThe URI must follow this syntax: https://api-eu.cloud.com/healthdata/metricdata?edgeServerId={CloudConnector-ID} If you use variables in Postman, ensure the correct variables are used. Paste the correct URI into Postman´s address bar and select GET as the method. Verify the API call settings are correct.  The Response contains all Connectivity-related data – the Timestamps are in the Epoch-Format – you need an Epoch converter tool for viewing them in the standard Date/Time format:  You have successfully obtained the Health Metric data. Code Snippets: API-Call: GET https://api-eu.cloud.com/healthdata/metricdata?edgeServerId={CloudConnector-ID} Response: See Appendix   Retrieve Cloud Connector Health Metrics using PowerShellUsing PowerShell, use the following Code snippet to obtain Connectivity Metrics of your chosen Cloud Connector: PS C:\_TACG&gt; Get-XDAuthentication PS C:\_TACG&gt; $CCID = 'dcint77d970d' PS C:\_TACG&gt; $ClCoID = '4fb6XXXX-XXXX-XXXX-XXXX-XXXXXXXXe0ec' PS C:\_TACG&gt; $BT = 'eyJh...0bPw' PS C:\_TACG&gt; PS C:\_TACG&gt; $metricsUrl = "https://api-eu.cloud.com/healthdata/metricdata?edgeServerId=" + $ClCoID PS C:\_TACG&gt; $Headers = @{ &gt;&gt; 'Authorization' = "CWSAuth bearer=$BT" &gt;&gt; 'Content-Type' = "application/json" &gt;&gt; 'Citrix-CustomerID' = $CCID &gt;&gt; 'Accept' = "application/json" &gt;&gt; } PS C:\_TACG&gt; $response = Invoke-WebRequest -URI $metricsUrl -Method GET -Headers $Headers PS C:\_TACG&gt; $response | FT Content  Content ------- {"cpuData":[{"identifier":null,"alert":false,"subtype":"\\Processor(0)\\% Processor Time","data":{"1733915635":5.7530103,"1733919235":5.663282,"1733922840":5.5012383,"1733926441":5.3453674,"1733930038":3.8343427,"1733933638":4.3896527,"1                733937238":4.138145,"1733940838":4.0494776,"1733944438":3.9888759...   PS C:\_TACG&gt; You can further use the JSON data based on the Content property. Please note that the Timestamps are in Epoch format—you need an Epoch converter tool to view them in the standard Date/Time format.   Retrieve Cloud Connector Health Metrics using the .NET ApplicationThe .NET Application automatically retrieves the Connectivity Metrics of a Cloud Connector adjacent to the selected Resource Location and exports the data as a JSON-based file. Choose the Cloud Connector you want to query and press Export Health-Data of selected Cloud Connector:  The application retrieves the metrics and exports the data as a JSON-based file in the configured directory on the local file system:  NotificationsThe .NET application has a Notification functionality built-in. You can choose between Notifications by e-mail or notifications per Webhook into Teams. You can configure the Notification functionality by modifying the adjacent JSON-file: {   "NotificationType": "Webhook",   "TeamsWebhookURL": "https://prod-208.westeurope.logic.azure.com:443/workflows/xXxXxXxXxXxXxXx/triggers/manual/paths/invoke?api-version=2016-06-01&amp;sp=%2Ftriggers%2Fmanual%2Frun&amp;sv=1.0&amp;sig=xXxXxXxXxXxXxXxXxXxXx",   "mailSender":"xxxxxxxxxx@xxxxxxxxxx.net",   "mailRecipient":"notifications@the-austrian-citrix-guy.at",   "eMailSubject":"Cloud Connector-HealthCheck" } The application is configured to notify using the Teams Webhook functionality in this guide. In the case of the detection of a connectivity or health problem, a notification in Teams appears:  If Teams is not an option, the notifications can also be sent out by e-mail.   SummaryContinuous monitoring such vital components as the Cloud Connectors is imperative for a Citrix DaaS environment. Use the provided methods to implement your automated monitoring and ensure that notifications warn you in case of a Cloud Connector failure. Important Disclaimer: EXCEPT WHERE EXPRESSLY PROVIDED OTHERWISE BY CLOUD SOFTWARE GROUP, THE CONTENT IN THIS DOCUMENT IS PROVIDED “AS IS” AND CLOUD SOFTWARE GROUP HEREBY DISCLAIMS ALL EXPRESS OR IMPLIED REPRESENTATIONS, WARRANTIES, GUARANTIES, AND CONDITIONS, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES OR CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD TO BE LEGALLY INVALID. CLOUD SOFTWARE GROUP MAKES NO REPRESENTATIONS, WARRANTIES, GUARANTIES, OR CONDITIONS AS TO THE QUALITY, SUITABILITY, TRUTH, ACCURACY, OR COMPLETENESS OF ANY OF THE CONTENT CONTAINED ON THE WEBSITE. The above disclaimer applies to any damages, liability, or injuries caused by any failure of performance, error, omission, interruption, deletion, defect, delay in operation or transmission, computer virus, communication line failure, theft or destruction of or unauthorized access to, alteration of, or use, whether for breach of contract, tort, negligence or any other cause of action.   AppendixSwagger-File for Advanced HealthData API swagger: "2.0" info:   title: Citrix Cloud Connector Health Data API   description: APIs for querying Connector health data   version: "1.0" host: 'api.cloud.com' schemes:   - https basePath: /healthdata paths:   /metricdata:     get:       tags:         - MetricData       summary: Fetch the metric data for a connector       operationId: MetricData_FetchMetricDataAsync       consumes:         - application/json       produces:         - application/json       parameters:         - name: Citrix-CustomerId           in: header           description: ID of the customer.           required: true           type: string         - name: edgeServerId           in: query           description: This is the unique id of the EdgeServer.           required: true           type: string         - name: days           in: query           required: false           type: integer           format: int32         - name: retrieveProvidersData           in: query           required: false           type: boolean         - name: dataAggregationTime           in: query           required: false           type: string           enum:             - OneHour             - SixHours             - OneDay       responses:         '200':           description: OK           schema:             $ref: "#/definitions/MetricDataResponsesModel"   /connectivitydata:     get:       tags:         - ConnectivityData       summary: Fetch the connectivity data for a connector       operationId: MetricData_FetchConnectivityDataAsync       consumes:         - application/json       produces:         - application/json       parameters:         - name: Citrix-CustomerId           in: header           description: ID of the customer.           required: true           type: string         - name: edgeServerId           in: query           description: This is the unique id of the EdgeServer.           required: true           type: string         - name: days           in: query           required: false           type: integer           format: int32         - name: retrieveProvidersData           in: query           required: false           type: boolean         - name: dataAggregationTime           in: query           required: false           type: string           enum:             - OneHour             - SixHours             - OneDay       responses:         '200':           description: OK           schema:             $ref: "#/definitions/ConnectivityDataResponsesModel" definitions:   MetricDataResponsesModel:     type: object     properties:       cpuData:         type: array         items:           $ref: "#/definitions/MetricDataResponseModel"       memoryData:         type: array         items:           $ref: "#/definitions/MetricDataResponseModel"       networkData:         type: array         items:           $ref: "#/definitions/MetricDataResponseModel"       diskData:         type: array         items:           $ref: "#/definitions/MetricDataResponseModel"       providersData:         type: object         additionalProperties:           type: object           additionalProperties:             type: array             items:               $ref: "#/definitions/MetricDataResponseModel"   MetricDataResponseModel:     type: object     properties:       identifier:         type: string       alert:         type: boolean       subtype:         type: string       data:         type: object         additionalProperties:           format: float           type: number       maxValue:         format: float         type: number       unit:         type: string       chartType:         enum:           - Line           - StackedHorizontalBar         type: string   ConnectivityDataResponsesModel:     type: object     properties:       overallConnectorConnectivity:         type: object         additionalProperties:           enum:             - Unknown             - Connected             - Disconnected           type: string       providersData:         type: array         items:           $ref: "#/definitions/ConnectivityDataProviderResponseModel"   ConnectivityDataProviderResponseModel:     description: ''     type: object     properties:       providerName:         description: Provider Name         type: string       displayName:         description: Display Name         type: string       providerVersion:         description: Provider Version         type: string       overallProviderConnectivity:         description: Overall Provider Connectivity         type: object         additionalProperties:           enum:             - Unknown             - Connected             - Disconnected           type: string       connectivityCheckData:         description: Data of all the checks in the provider         type: object         additionalProperties:           $ref: "#/definitions/ConnectivityCheckResponseModel"   ConnectivityCheckResponseModel:     description: Connectivity Check Response Model     type: object     properties:       name:         description: Check Name         type: string       label:         description: Check Label         type: string       description:         description: Check Description         type: string       targetUri:         description: TargetUri         type: string       severity:         description: Severity of check         enum:           - Critical           - Warning         type: string       data:         description: Data of all the checks in the provider         type: object         additionalProperties:           enum:             - Unknown             - Connected             - Disconnected           type: string   EdgeServerModel:     type: object     properties:       id:         type: string       fqdn:         maxLength: 100         minLength: 0         type: string       role:         maxLength: 256         minLength: 0         type: string       windowsSid:         maxLength: 100         minLength: 0         type: string       location:         maxLength: 100         minLength: 0         type: string       status:         maxLength: 256         minLength: 0         type: string       currentVersion:         type: string       currentBootstrapperVersion:         type: string       expectedVersion:         type: string       expectedBootStrapperVersion:         type: string       versionState:         type: string       upgradingVersion:         type: string       upgradingStatus:         type: string       failedUpgradeReason:         type: string       lastUpgradeDate:         type: string         format: date-time       lastUpgradeCompletedDate:         type: string         format: date-time       lastContactDate:         type: string         format: date-time       leaseEndDateTime:         type: string         format: date-time       inMaintenance:         type: boolean       upgradeDisabled:         type: boolean       connectorType:         $ref: '#/definitions/ConnectorType'     additionalProperties: false   ConnectorType:     enum:       - 0       - 1       - 2147483647     type: integer     format: int32   Example of Connection-related Cloud Connector data{     "overallConnectorConnectivity": {         "1733865579": "Connected",         "1733869179": "Connected",         ...         "1734462854": "Connected",         "1734466455": "Connected"     },     "providersData": [         {             "providerName": "CitrixCsgAgentWatchDog_x64",             "displayName": "Citrix Agent WatchDog",             "providerVersion": "",             "overallProviderConnectivity": {                 "1733865579": "Connected",                 "1733869179": "Connected",                 ...                 "1734462854": "Connected",                 "1734466455": "Connected"             },             "connectivityCheckData": {                 "blob storage connectivity check": {                     "name": "Blob Storage Connectivity Check",                     "label": "Blob Storage Connectivity Check",                     "description": "Blob Storage Connectivity Check",                     "targetUri": "https://cwsproduction.blob.core.windows.net/downloads/",                     "severity": "Critical",                     "data": {                         "1733865579": "Connected",                         "1733869179": "Connected",                         ...                         "1734462854": "Connected",                         "1734466455": "Connected"                     }                 },                 "servicebus connectivity check": {                     "name": "Servicebus Connectivity Check",                     "label": "Servicebus Connectivity Check",                     "description": "Servicebus Connectivity Check",                     "targetUri": "https://ctxwsp-agentloggingpremium-eastus.servicebus.windows.net/",                     "severity": "Critical",                     "data": {                         "1733865579": "Connected",                         "1733869179": "Connected",                         ...                         "1734462854": "Connected",                         "1734466455": "Connected"                     }                 },                 "process citrix.cloudservices.agentwatchdog running": {                     "name": "Process Citrix.CloudServices.AgentWatchDog Running",                     "label": "Process Citrix.CloudServices.AgentWatchDog Running",                     "description": "Check if windows process Citrix.CloudServices.AgentWatchDog is running",                     "targetUri": null,                     "severity": "Critical",                     "data": {                         "1733865579": "Connected",                         "1733869179": "Connected",                         ...                         "1734462854": "Connected",                         "1734466455": "Connected"                     }                 }             }         },         {             "providerName": "Broker_Service",             "displayName": "Citrix High Availability Service",             "providerVersion": "",             "overallProviderConnectivity": {                 "1733865579": "Connected",                 "1733869179": "Connected",                 ...                 "1734462854": "Connected",                 "1734466455": "Connected"             },             "connectivityCheckData": {                 "process highavailabilityservice running": {                     "name": "Process HighAvailabilityService Running",                     "label": "Process HighAvailabilityService Running",                     "description": "Check if windows process HighAvailabilityService is running",                     "targetUri": null,                     "severity": "Critical",                     "data": {                         "1733865579": "Connected",                         "1733869179": "Connected",                         ...                           "1734462854": "Connected",                         "1734466455": "Connected"                     }                 }             }         },         {             "providerName": "CdfCaptureService",             "displayName": "Citrix CDF Capture Service",             "providerVersion": "",             "overallProviderConnectivity": {                 "1733865579": "Connected",                 "1733869179": "Connected",                 ...                 "1734462854": "Connected",                 "1734466455": "Connected"             },             "connectivityCheckData": {                 "process cdfcaptureservice running": {                     "name": "Process CdfCaptureService Running",                     "label": "Process CdfCaptureService Running",                     "description": "Check if windows process CdfCaptureService is running",                     "targetUri": null,                     "severity": "Critical",                     "data": {                         "1733865579": "Connected",                         "1733869179": "Connected",                         ...                         "1734462854": "Connected",                         "1734466455": "Connected"                     }                 }             }         },         {             "providerName": "Citrix WEM Connector Provider",             "displayName": "Citrix WEM Service",             "providerVersion": "",             "overallProviderConnectivity": {                 "1733865579": "Connected",                 "1733869179": "Connected",                 ...                 "1734462854": "Connected",                 "1734466455": "Connected"             },             "connectivityCheckData": {                 "process connector.authentication.host running": {                     "name": "Process Connector.Authentication.Host Running",                     "label": "Process Citrix WEM Cloud Authentication Service Running",                     "description": "Check if windows process Connector.Authentication.Host is running",                     "targetUri": null,                     "severity": "Critical",                     "data": {                         "1733865579": "Connected",                         "1733869179": "Connected",                         ...                         "1734462854": "Connected",                         "1734466455": "Connected"                     }                 },                 "process connector.messaging.host running": {                     "name": "Process Connector.Messaging.Host Running",                     "label": "Process Citrix WEM Cloud Messaging Service Running",                     "description": "Check if windows process Connector.Messaging.Host is running",                     "targetUri": null,                     "severity": "Critical",                     "data": {                         "1733865579": "Connected",                         "1733869179": "Connected",                         ...                         "1734462854": "Connected",                         "1734466455": "Connected"                     }                 }             }         },         {             "providerName": "CitrixClxMtpService",             "displayName": "Citrix Connection Lease Exchange Service",             "providerVersion": "",             "overallProviderConnectivity": {                 "1733865579": "Connected",                 "1733869179": "Connected",                 ...                 "1734462854": "Connected",                 "1734466455": "Connected"             },             "connectivityCheckData": {                 "process citrix.clxmtpservice running": {                     "name": "Process Citrix.ClxMtpService Running",                     "label": "Process Citrix.ClxMtpService Running",                     "description": "Check if windows process Citrix.ClxMtpService is running",                     "targetUri": null,                     "severity": "Critical",                     "data": {                         "1733865579": "Connected",                         "1733869179": "Connected",                         ...                         "1734462854": "Connected",                         "1734466455": "Connected"                     }                 }             }         },         {             "providerName": "CitrixCsgAgent_x64",             "displayName": "Citrix AD Provider",             "providerVersion": "2.58.0.132",             "overallProviderConnectivity": {                 "1733865579": "Connected",                 "1733869179": "Connected",                  ...                 "1734462854": "Connected",                 "1734466455": "Connected"             },             "connectivityCheckData": {                 "process citrix.cloudservices.agent running": {                     "name": "Process Citrix.CloudServices.Agent Running",                     "label": "Process Citrix.CloudServices.Agent Running",                     "description": "Check if windows process Citrix.CloudServices.Agent is running",                     "targetUri": null,                     "severity": "Critical",                     "data": {                         "1733865579": "Connected",                         "1733869179": "Connected",                         ...                         "1734462854": "Connected",                         "1734466455": "Connected"                     }                 },                 "messaging": {                     "name": "Messaging",                     "label": "Messaging websocket communication",                     "description": "Messaging websocket communication",                     "targetUri": "https://messaging-eastus-release-b.citrixworkspacesapi.net/dcint77d970d/endpoints/connect",                     "severity": "Critical",                     "data": {                         "1733865579": "Connected",                         "1733869179": "Connected",                         ...                         "1734462854": "Connected",                         "1734466455": "Connected"                     }                 },                 "domain joined": {                     "name": "Domain Joined",                     "label": "Connector machine is domain joined",                     "description": "Connector machine is domain joined",                     "targetUri": "",                     "severity": "Critical",                     "data": {                         "1733865579": "Connected",                         "1733869179": "Connected",                         ...                         "1734462854": "Connected",                         "1734466455": "Connected"                     }                 }             }         },         {             "providerName": "CitrixCsgAgentDiscovery_x64",             "displayName": "Citrix Agent Discovery",             "providerVersion": "1.21.0.132",             "overallProviderConnectivity": {                 "1733865579": "Connected",                 "1733869179": "Connected",                 ...                 "1734462854": "Connected",                 "1734466455": "Connected"             },             "connectivityCheckData": {                 "process citrix.cloudservices.agentdiscovery running": {                     "name": "Process Citrix.CloudServices.AgentDiscovery Running",                     "label": "Process Citrix.CloudServices.AgentDiscovery Running",                     "description": "Check if windows process Citrix.CloudServices.AgentDiscovery is running",                     "targetUri": null,                     "severity": "Critical",                     "data": {                         "1733865579": "Connected",                         "1733869179": "Connected",                         ...                         "1734462854": "Connected",                         "1734466455": "Connected"                     }                 },                 "messaging": {                     "name": "Messaging",                     "label": "Messaging websocket communication",                     "description": "Messaging websocket communication",                     "targetUri": "https://messaging-eastus-release-b.citrixworkspacesapi.net/dcint77d970d/endpoints/connect",                     "severity": "Critical",                     "data": {                         "1733865579": "Connected",                         "1733869179": "Connected",                         ...                         "1734462854": "Connected",                         "1734466455": "Connected"                     }                 }             }         },         {             "providerName": "CitrixCsgAgentLogger_x64",             "displayName": "Citrix Agent Logger",             "providerVersion": "",             "overallProviderConnectivity": {                 "1733865579": "Connected",                 "1733869179": "Connected",                 ...                 "1734462854": "Connected",                 "1734466455": "Connected"             },             "connectivityCheckData": {                 "process citrix.cloudservices.agentlogger running": {                     "name": "Process Citrix.CloudServices.AgentLogger Running",                     "label": "Process Citrix.CloudServices.AgentLogger Running",                     "description": "Check if windows process Citrix.CloudServices.AgentLogger is running",                     "targetUri": null,                     "severity": "Critical",                     "data": {                         "1733865579": "Connected",                         "1733869179": "Connected",                         ...                         "1734462854": "Connected",                         "1734466455": "Connected"                     }                 }             }         },         {             "providerName": "CitrixCsgCredentialProvider_x64",             "displayName": "Citrix Credential Provider",             "providerVersion": "",             "overallProviderConnectivity": {                 "1733865579": "Connected",                 "1733869179": "Connected",                 ...                 "1734462854": "Connected",                 "1734466455": "Connected"             },             "connectivityCheckData": {                 "process citrix.cloudservices.credentialprovider running": {                     "name": "Process Citrix.CloudServices.CredentialProvider Running",                     "label": "Process Citrix.CloudServices.CredentialProvider Running",                     "description": "Check if windows process Citrix.CloudServices.CredentialProvider is running",                     "targetUri": null,                     "severity": "Critical",                     "data": {                         "1733865579": "Connected",                         "1733869179": "Connected",                         ...                         "1734462854": "Connected",                         "1734466455": "Connected"                     }                 }             }         },         {             "providerName": "CitrixCsgWebRelayAgent_x64",             "displayName": "Citrix WebRelay Provider",             "providerVersion": "1.20.0.132",             "overallProviderConnectivity": {                 "1733865579": "Connected",                 "1733869179": "Connected",                 ...                 "1734462854": "Connected",                 "1734466455": "Connected"             },             "connectivityCheckData": {                 "process citrix.cloudservices.webrelay.agent running": {                     "name": "Process Citrix.CloudServices.WebRelay.Agent Running",                     "label": "Process Citrix.CloudServices.WebRelay.Agent Running",                     "description": "Check if windows process Citrix.CloudServices.WebRelay.Agent is running",                     "targetUri": null,                     "severity": "Critical",                     "data": {                         "1733865579": "Connected",                         "1733869179": "Connected",                         …                         "1734462854": "Connected",                         "1734466455": "Connected"                     }                 },                 "messaging": {                     "name": "Messaging",                     "label": "Messaging websocket communication",                     "description": "Messaging websocket communication",                     "targetUri": "https://messaging-eastus-release-b.citrixworkspacesapi.net/dcint77d970d/endpoints/connect",                     "severity": "Critical",                     "data": {                         "1733865579": "Connected",                         "1733869179": "Connected",                         ...                         "1734462854": "Connected",                         "1734466455": "Connected"                     }                 }             }         },         {             "providerName": "CitrixITSMAdapterProvider",             "displayName": "Citrix ITSM Adapter Provider",             "providerVersion": "",             "overallProviderConnectivity": {                 "1733865579": "Connected",                 "1733869179": "Connected",                 ...                 "1734462854": "Connected",                 "1734466455": "Connected"             },             "connectivityCheckData": {                 "process workspaceautomationconnectorplugin running": {                     "name": "Process WorkspaceAutomationConnectorPlugin Running",                     "label": "Process WorkspaceAutomationConnectorPlugin Running",                     "description": "Check if windows process WorkspaceAutomationConnectorPlugin is running",                     "targetUri": null,                     "severity": "Critical",                     "data": {                         "1733865579": "Connected",                         "1733869179": "Connected",                         ...                         "1734462854": "Connected",                         "1734466455": "Connected"                     }                 }             }         },         {             "providerName": "CitrixNetScalerCloudGateway",             "displayName": "Citrix NetScaler Cloud Gateway",             "providerVersion": "",             "overallProviderConnectivity": {                 "1733865579": "Connected",                 "1733869179": "Connected",                 ...                 "1734462854": "Connected",                 "1734466455": "Connected"             },             "connectivityCheckData": {                 "process citrix.netscaler.cloudgateway running": {                     "name": "Process Citrix.NetScaler.CloudGateway Running",                     "label": "Process Citrix.NetScaler.CloudGateway Running",                     "description": "Check if windows process Citrix.NetScaler.CloudGateway is running",                     "targetUri": null,                     "severity": "Critical",                     "data": {                         "1733865579": "Connected",                         "1733869179": "Connected",                         ...                         "1734462854": "Connected",                         "1734466455": "Connected"                     }                 },                 "citrixnetscalercloudgateway": {                     "name": "CitrixNetScalerCloudGateway",                     "label": "https://reg.c.nssvc.net:443/Control/Ping",                     "description": "Connectivity Test succeeded, received HTTP 200 OK",                     "targetUri": "https://azure-reg.c.nssvc.net:443/Control/Ping",                     "severity": "Critical",                     "data": {                         "1733865579": "Connected",                         "1733869179": "Connected",                         ...                          "1734462854": "Connected",                         "1734466455": "Connected"                     }                 }             }         },         {             "providerName": "ConfigSync_Service",             "displayName": "Citrix Config Synchronizer",             "providerVersion": "",             "overallProviderConnectivity": {                 "1733865579": "Connected",                 "1733869179": "Connected",                 ...                 "1734462854": "Connected",                 "1734466455": "Connected"             },             "connectivityCheckData": {                 "process configsyncservice running": {                     "name": "Process ConfigSyncService Running",                     "label": "Process ConfigSyncService Running",                     "description": "Check if windows process ConfigSyncService is running",                     "targetUri": null,                     "severity": "Critical",                     "data": {                         "1733865579": "Connected",                         "1733869179": "Connected",                         ...                         "1734462854": "Connected",                         "1734466455": "Connected"                     }                 }             }         },         {             "providerName": "RemoteHCL_Service",             "displayName": "Citrix Remote HCL Server",             "providerVersion": "",             "overallProviderConnectivity": {                 "1733865579": "Connected",                 "1733869179": "Connected",                 ...                 "1734462854": "Connected",                 "1734466455": "Connected"             },             "connectivityCheckData": {                 "process remotehclserver running": {                     "name": "Process RemoteHCLServer Running",                     "label": "Process RemoteHCLServer Running",                     "description": "Check if windows process RemoteHCLServer is running",                     "targetUri": null,                     "severity": "Critical",                     "data": {                         "1733865579": "Connected",                         "1733869179": "Connected",                         ...                         "1734462854": "Connected",                         "1734466455": "Connected"                     }                 }             }         },         {             "providerName": "XaXdCloudProxy",             "displayName": "Citrix Remote Broker Provider",             "providerVersion": "7.43.26349.51626",             "overallProviderConnectivity": {                 "1733865579": "Connected",                 "1733869179": "Connected",                 ...                 "1734462854": "Connected",                 "1734466455": "Connected"             },             "connectivityCheckData": {                 "process xaxdcloudproxy running": {                     "name": "Process XaXdCloudProxy Running",                     "label": "Process XaXdCloudProxy Running",                     "description": "Check if windows process XaXdCloudProxy is running",                     "targetUri": null,                     "severity": "Critical",                     "data": {                         "1733865579": "Connected",                         "1733869179": "Connected",                         ...                         "1734462854": "Connected",                         "1734466455": "Connected"                     }                 },                 "xendesktop ddc test": {                     "name": "XenDesktop DDC Test",                     "label": "https://*.xendesktop.net",                     "description": "XenDesktop DDC Connectivity Test",                     "targetUri": "https://dcint77d970d.xendesktop.net/api/HealthCheck",                     "severity": "Warning",                     "data": {                         "1733865579": "Connected",                         "1733869179": "Connected",                         ...                         "1734462854": "Connected",                         "1734466455": "Connected"                     }                 }             }         }     ] }   Example of Health-related Cloud Connector data{     "cpuData": [         {             "identifier": null,             "alert": false,             "subtype": "\\Processor(0)\\% Processor Time",             "data": {                 "1733865576": 4.8040514,                 "1733869177": 4.8039823,                 ...                 "1734462849": 3.8951037,                 "1734466449": 3.9599369             },             "maxValue": 100,             "unit": null,             "chartType": "Line"         },         {             "identifier": null,             "alert": false,             "subtype": "\\Processor(1)\\% Processor Time",             "data": {                 "1733865576": 4.9327693,                 "1733869177": 4.958199,                 ...                 "1734462849": 4.103055,                 "1734466449": 4.1907554             },             "maxValue": 100,             "unit": null,             "chartType": "Line"         },         {             "identifier": null,             "alert": false,             "subtype": "\\Processor(_Total)\\% Processor Time",             "data": {                 "1733865576": 4.8673444,                 "1733869177": 4.88006,                 ...                 "1734462849": 3.99609,                 "1734466449": 4.0723596             },             "maxValue": 100,             "unit": null,             "chartType": "Line"         }     ],     "memoryData": [         {             "identifier": null,             "alert": false,             "subtype": "\\Memory\\Available MBytes",             "data": {                 "1733865574": 2801.1729,                 "1733869174": 2797.4248,                 ...                 "1734462849": 3959.9211,                 "1734466450": 3966.4487             },             "maxValue": 8140.84,             "unit": null,             "chartType": "Line"         }     ],     "networkData": [         {             "identifier": null,             "alert": false,             "subtype": "\\Network Interface(Microsoft Hyper-V Network Adapter)\\Bytes Total/sec",             "data": {                 "1733865574": 15120.319,                 "1733869174": 15098.142,                 ...                 "1734462850": 14560.1875,                 "1734466450": 14605.242             },             "maxValue": 5E+10,             "unit": null,             "chartType": "Line"         }     ],     "diskData": [         {             "identifier": null,             "alert": false,             "subtype": "\\LogicalDisk(C:)\\Free Megabytes",             "data": {                 "1733865575": 102008,                 "1733869175": 102018,                 ...                 "1734462849": 100562,                 "1734466449": 100525             },             "maxValue": 129481.95,             "unit": null,             "chartType": "Line"         },         {             "identifier": null,             "alert": false,             "subtype": "\\LogicalDisk(D:)\\Free Megabytes",             "data": {                 "1733865575": 15029,                 "1733869175": 15029,                 ...                 "1734462849": 15027,                 "1734466449": 15027             },             "maxValue": 16381.996,             "unit": null,             "chartType": "Line"         }     ],     "providersData": {} }]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_12/cc-RunHC1.png.d5d97c4e3b74fd02d1a6029ab48f3874.png" length="62378" type="image/png"/><pubDate>Wed, 18 Dec 2024 15:19:00 +0000</pubDate></item><item><title>Detecting and Mitigating Password Spraying Attacks on NetScaler Gateway</title><link>https://community.stage.citrix.com/tech-zone/build/tech-papers/detecting-and-mitigating-password-spraying-attacks-nsg/</link><description><![CDATA[Understanding Password Spraying Attacks



	Password spraying attacks continue to increase, with major security vendors reporting significant rises throughout 2024. Unlike traditional brute force attacks that try many passwords against a single account, password spraying attempts to avoid detection through various techniques:
 


	
		Using leaked credentials from the dark web.
	
	
		Generating synthetic usernames based on company naming patterns.
	
	
		Distributing attempts across thousands of source IP addresses.
	
	
		Targeting administrative accounts.
	
	
		Spreading attempts across edge servers to bypass lockout controls.
	



	Traditional rate limiting or account lockouts often don't catch these attacks because:
 


	
		Each account sees very few attempts.
	
	
		Each client IP makes minimal attempts per hour.
	
	
		Attack duration extends beyond typical monitoring windows.
	



	These attacks typically come in two forms:
 


	
		Sophisticated campaigns using tens of thousands of source IPs over weeks or months.
	
	
		More common attacks using 10-50 source IPs attempting different usernames throughout the day.
	



	These sophisticated attacks often overlap, making attribution and detection challenging. Because multiple attackers may be active simultaneously, noisy "dumb" attacks can mask more strategic surgical attempts. This solution specifically targets attacks where a small number of clients attempt passwords against multiple accounts, often using credentials from public data breaches. Unlike IP reputation, which blocks known malicious sources, this detects and blocks new attack sources. While Multi-Factor Authentication (MFA) remains the most effective defense, some environments have legacy applications or other constraints that prevent full MFA deployment and other interactive protections like CAPTCHA. For these scenarios, detecting and blocking password spraying attempts becomes crucial.
 


	Related Protections



	This solution complements the protections detailed in "Password Spraying Attacks—NetScaler (December 2024)," which covers IP reputation, CAPTCHA, and pre-authentication URL protections. Together, these approaches provide in-depth defense against different aspects of password spraying attacks.
 


	Prerequisites and Assumptions



	
		NetScaler 14.1: Build 34.42.nc or later
	
	
		nFactor authentication configured per Tech Paper: Reference Designs for NetScaler Gateway On-Premises
	
	
		Single-factor LDAP authentication for legacy platform access
	
	
		AAA vServer named "Gateway_Auth_vServer"
	
	
		LDAP policy named "Gateway_LDAP_Policy"
	



	Detection Strategy



	This solution targets the more common attack pattern using a limited IP pool. It tracks when a client's IP address switches between different usernames in failed authentication attempts. If an IP address attempts to authenticate with nine different usernames without successful logins, the IP is blocked for 24 hours from their last access attempt (configured via 86400-second expiry). For example, if an IP is blocked at 1 am but attempts to access again at 2 am, the 24-hour block extends to 2 am the next day. A successful login resets this counter, allowing another nine username attempts. The detection and blocking periods can easily be extended by changing the variable expiry times to 366 days. The threshold for failed username attempts can be adjusted as needed, and corporate IP addresses can be allowlisted.
 


	For example:
 


	
		Attempt 1: "bob" → counter = 0
	
	
		Attempt 2: "fred" → counter = 1 (username changed)
	
	
		Attempt 3: "bob" → counter = 2 (changed back)
	
	
		Attempt 4: "alice" → counter = 3 (changed again)
	



	
		Note: 
	 

	
		Blank usernames are ignored, and the counter is not incremented. This prevents false blocks from NetScaler Gateway login page timeouts, which can submit empty authentication attempts. Since blank usernames aren't valid credentials, this doesn't impact the effectiveness of password spray detection.
	 



	
 


	Technical Implementation Details



	Authentication Flow



	The configuration integrates with nFactor authentication using two NetScaler maps that persist in memory:
 


	
		Likelihood_Bad_IP_Counter: Tracks the number of username changes per IP.
	
	
		Username_And_IP_Uniqueness_Checker: Stores last username tried per IP.
	



	
		Note: 
	 

	
		nFactor flows that contain only map variable assignments do not complete the authentication process, create sessions, or generate AAA cookies. Only flows containing an authentication factor result in a completed login - the assignment policies update our tracking variables.
	 



	 
 


	Both maps expire entries after 24 hours (86400 seconds) and can store up to 10,000 entries each. These values can be increased if you want to detect failures over a longer period. In high availability setups, these maps exist independently in memory on each node, so counters are reset on failover.
 


	The 10,000 entry limit is configurable and creates a rolling buffer, where the oldest entries drop out as new ones come in. Each entry combines a 128-byte key with a 64-bit number, requiring approximately 1.3MB of data storage in the map. Even an attacker attempting to fill the maps would need access to thousands of source IP addresses, making resource exhaustion through this mechanism impractical. 
 


	Form Field Processing



	The configuration looks for the username in a form field named "login." This is the fixed form field name that NetScaler Gateway expects—any authentication attempt, whether from a web browser or automated script, must submit credentials using this field name. Unlike AAA, the TYPECAST_NVLIST_T expression is used to parse the form data because we need to capture attempted usernames regardless of the authentication outcome.USER.NAME is only populated after successful authentication.
 


	Processing Logic



	The implementation maps directly to specific NetScaler policies and assignments:
 


	
		Every authentication request:

		
			
				Check if IP is blocked (Drop_Pol checks Likelihood_Bad_IP_Counter &gt;= 9) 
			
			
				Store current username attempt (Username_And_IP_Uniqueness_Checker_Addition)
			
			
				Compare with previously stored username (Increase_Likelihood_Bad_IP_Pol)
			
			
				Increment counter if username changed (Likelihood_Bad_IP_Counter_Incrementer)
			
		
	
	
		On successful authentication:
		
			
				Reset counter for that IP (Likelihood_Bad_IP_Counter_Reset via Reset_Policy_Label)
			
			
				There is no impact on the stored username.
			
		
	



	This detection and blocking logic has proven effective in production environments, as demonstrated by the following example.
 


	Real-World Attack Example



	The following sanitized log excerpt shows a password-spraying attack that was automatically mitigated. Over 4 hours, an attacker alternated between empty usernames and themed usernames like "genetics," "chemistry," and "vaccine." The attack was automatically blocked after nine username rotations as designed.
 


	
 


	This validates that the detection logic and automatic blocking functionality work as intended in a production environment.
 


	Configuration



	
		Important:
	 

	
		Before deployment, any implementation must be thoroughly tested in a pre-production environment.
	 

	
		The configuration in this article relies on advanced NetScaler features, such as variable assignments and complex expressions that are not yet fully implemented in the NetScaler GUI. For implementation and any subsequent modifications:
	 

	
		
			Use the Command Line Interface (CLI) exclusively for these configurations.
		
		
			Do not attempt to edit these policies through the GUI, which may result in error messages.
		
	


# Store username rotation counter per IP
add ns variable Likelihood_Bad_IP_Counter -type "map(text(128), ulong, 10000)" -expires 86400

# Store last username tried per IP
add ns variable Username_And_IP_Uniqueness_Checker -type "map(text(128),text(256),10000)" -expires 86400

# Increment counter when username changes
add ns assignment Likelihood_Bad_IP_Counter_Incrementer -variable "$Likelihood_Bad_IP_Counter[CLIENT.IP.SRC]" -add 1

# Store current username
add ns assignment Username_And_IP_Uniqueness_Checker_Addition -variable "$Username_And_IP_Uniqueness_Checker[CLIENT.IP.SRC.TYPECAST_TEXT_T]" -set "HTTP.REQ.BODY(1000).TYPECAST_NVLIST_T(\'=\',\'&amp;\').VALUE(\"login\")"

# Check if username has changed and increment counter if it has
add authentication Policy Increase_Likelihood_Bad_IP_Pol -rule "($Username_And_IP_Uniqueness_Checker[client.ip.src.typecast_text_t] != HTTP.REQ.BODY(1000).TYPECAST_NVLIST_T(\'=\',\'&amp;\').VALUE(\"login\")) &amp;&amp; HTTP.REQ.BODY(1000).TYPECAST_NVLIST_T(\'=\',\'&amp;\').VALUE(\"login\") != \"\"" -action Likelihood_Bad_IP_Counter_Incrementer
bind authentication vserver Gateway_Auth_vServer -policy Increase_Likelihood_Bad_IP_Pol -priority 110 -gotoPriorityExpression NEXT

# Update stored username
add authentication Policy Username_And_IP_Uniqueness_Checker_Pol -rule true -action Username_And_IP_Uniqueness_Checker_Addition
bind authentication vserver Gateway_Auth_vServer -policy Username_And_IP_Uniqueness_Checker_Pol -priority 120 -gotoPriorityExpression END

# Add Dataset to contain IPs and CIDR ranges that should always be allowed access
add policy dataset Proxy_Server_And_NAT_IP_That_Bypass_Password_Spray_Detection ipv4

# Block IPs that exceed threshold while allowing dataset and status page
add responder policy Drop_Pol "($Likelihood_Bad_IP_Counter[CLIENT.IP.SRC.typecast_text_t].GE(9) &amp;&amp; client.ip.src.typecast_text_t.equals_any(\"Proxy_Server_And_NAT_IP_That_Bypass_Password_Spray_Detection\").NOT) &amp;&amp; (HTTP.REQ.URL.PATH.EQ(\"/logon/LogonPoint/ip_status\").NOT &amp;&amp; HTTP.REQ.URL.PATH.EQ(\"/logon/LogonPoint/ip_status_reset_counter\").NOT)" DROP
bind vpn vserver Gateway_vServer -policy Drop_Pol -priority 10 -gotoPriorityExpression END -type REQUEST -type AAA_REQUEST

# Reset counter on successful authentication
add ns assignment Likelihood_Bad_IP_Counter_Reset -variable "$Likelihood_Bad_IP_Counter[CLIENT.IP.SRC]" -clear
add authentication Policy Increase_Likelihood_Counter_Reset_Pol -rule true -action Likelihood_Bad_IP_Counter_Reset
add authentication policylabel Likelihood_Bad_IP_Counter_Reset_Policy_Label -loginSchema LSCHEMA_INT
bind authentication policylabel Likelihood_Bad_IP_Counter_Reset_Policy_Label -policyName Increase_Likelihood_Counter_Reset_Pol -priority 100 -gotoPriorityExpression NEXT
add authentication Policy Return_Success -rule true -action NO_AUTHN
bind authentication policylabel Likelihood_Bad_IP_Counter_Reset_Policy_Label -policyName Return_Success -priority 110 -gotoPriorityExpression NEXT

# Bind reset policy after LDAP authentication 
bind authentication vserver Gateway_Auth_vServer -policy Gateway_LDAP_Policy -priority 100 -nextFactor Likelihood_Bad_IP_Counter_Reset_Policy_Label -gotoPriorityExpression NEXT

# Status page
add responder action IP_Status_Page_Act respondwith q|"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n&lt;!DOCTYPE html&gt;&lt;html&gt;&lt;head&gt;&lt;title&gt;NetScaler Client IP Status&lt;/title&gt;&lt;style&gt;body{font-family:Arial,sans-serif;margin:0;padding:20px;background:#f5f5f5}div.container{max-width:800px;margin:0 auto;background:white;padding:20px;border-radius:5px;box-shadow:0 2px 4px rgba(0,0,0,0.1)}h1{color:#0066CC;margin-top:0;font-size:24px}hr{border:0;border-top:1px solid #eee;margin:20px 0}label{display:block;margin-bottom:5px;color:#666}input[type='text']{padding:8px;border:1px solid #ddd;border-radius:4px;width:200px}input[type='submit']{background:#0066CC;color:white;border:0;padding:8px 16px;border-radius:4px;cursor:pointer}input[type='submit']:hover{background:#0052a3}.status-box{background:#f8f9fa;border:1px solid #e9ecef;padding:15px;border-radius:4px;margin:15px 0}.field{margin-bottom:10px}.field-label{font-weight:bold;color:#495057}.field-value{color:#212529}&lt;/style&gt;&lt;/head&gt;&lt;body&gt;&lt;div class='container'&gt;&lt;h1&gt;Password Spraying Client IP Status Check&lt;/h1&gt;&lt;div class='status-box'&gt;&lt;div class='field'&gt;&lt;span class='field-label'&gt;IP Address: &lt;/span&gt;&lt;span class='field-value'&gt;" + HTTP.REQ.URL.QUERY.VALUE("ip") ALT client.IP.SRC.typecast_text_t + "&lt;/span&gt;&lt;/div&gt;&lt;div class='field'&gt;&lt;span class='field-label'&gt;Last Username Attempt: &lt;/span&gt;&lt;span class='field-value'&gt;" + $Username_And_IP_Uniqueness_Checker[HTTP.REQ.URL.QUERY.VALUE("ip") ALT client.IP.SRC.typecast_text_t]+ "&lt;/span&gt;&lt;/div&gt;&lt;div class='field'&gt;&lt;span class='field-label'&gt;Username Change Counter: &lt;/span&gt;&lt;span class='field-value'&gt;" + $Likelihood_Bad_IP_Counter[HTTP.REQ.URL.QUERY.VALUE("ip") ALT client.IP.SRC.typecast_text_t] + "&lt;/span&gt;&lt;/div&gt;&lt;div class='field' style='text-align:right'&gt;&lt;form action='/logon/LogonPoint/ip_status_reset_counter' method='get'&gt;&lt;input type='hidden' name='ip' value='" + HTTP.REQ.URL.QUERY.VALUE("ip") ALT client.IP.SRC.typecast_text_t + "'&gt;&lt;input type='submit' value='Reset Counter'&gt;&lt;/form&gt;&lt;/div&gt;&lt;/div&gt;&lt;hr&gt;&lt;form action='/logon/LogonPoint/ip_status' method='get' onsubmit='if(!this.ip.value){this.ip.value=\"" + client.IP.SRC.typecast_text_t + "\";return true}'&gt;&lt;label for='ip'&gt;IP Address:&lt;/label&gt;&lt;input type='text' id='ip' name='ip' placeholder='IP or blank for current' pattern='^(?[0-9]{1,3}\\.){3}[0-9]{1,3}$' title='Please enter a valid IP address'&gt;&lt;input type='submit' value='Check Status'&gt;&lt;/form&gt;&lt;/div&gt;&lt;/body&gt;&lt;/html&gt;"|

add ns assignment Likelihood_Bad_IP_Counter_Reset_Request -variable "$Likelihood_Bad_IP_Counter[HTTP.REQ.URL.QUERY.VALUE(\"ip\")]" -clear
add responder action IP_Status_Reset_Counter_Act respondwith q{"HTTP/1.1 302 Redirect\r\nLocation: /logon/LogonPoint/ip_status\?ip=" + HTTP.REQ.URL.QUERY.VALUE("ip") + "\r\n\r\n"}

add policy dataset IP_Status_Page_Allowed_Clients ipv4
add responder policy IP_Status_Page_Resp_Pol "HTTP.REQ.URL.PATH.EQ(\"/logon/LogonPoint/ip_status\") &amp;&amp; client.ip.src.typecast_text_t.equals_any(\"IP_Status_Page_Allowed_Clients\")" IP_Status_Page_Act
add responder policy IP_Status_Page_Resp_Pol2 "HTTP.REQ.URL.PATH.EQ(\"/logon/LogonPoint/ip_status_reset_counter\") &amp;&amp; client.ip.src.typecast_text_t.equals_any(\"IP_Status_Page_Allowed_Clients\")" Likelihood_Bad_IP_Counter_Reset_Request
add responder policy IP_Status_Page_Resp_Pol3 "HTTP.REQ.URL.PATH.EQ(\"/logon/LogonPoint/ip_status_reset_counter\") &amp;&amp; client.ip.src.typecast_text_t.equals_any(\"IP_Status_Page_Allowed_Clients\")" IP_Status_Reset_Counter_Act

bind vpn vserver Gateway_vServer -policy IP_Status_Page_Resp_Pol -priority 100 -gotoPriorityExpression END -type REQUEST
bind vpn vserver Gateway_vServer -policy IP_Status_Page_Resp_Pol2 -priority 110 -gotoPriorityExpression NEXT -type REQUEST
bind vpn vserver Gateway_vServer -policy IP_Status_Page_Resp_Pol3 -priority 120 -gotoPriorityExpression END -type REQUEST

#Allow IP Addresses or CIDR ranges access to the Password Spraying Client IP Status Check page
bind policy dataset IP_Status_Page_Allowed_Clients 10.0.16.0/24



	Operational Considerations



	High Availability



	The configuration works in highly available pairs, but counters reset on failover. Blocking will continue with fresh counters on the new active node, but each bad client IP must be redetected.
 


	Proxy Servers, NAT, and False Positives



	Large Enterprises will likely have Proxy servers and NAT scenarios (like corporate gateways, VPN concentrators, or carrier NAT) that may legitimately send multiple usernames. Our configuration accounts for these scenarios in two ways:
 


	
		Successful authentications reset the counter automatically.
	
	
		The "Drop_Pol" policy references the dataset named "Proxy_Server_And_NAT_IP_That_Bypass_Password_Spray_Detection" which contains IP addresses and CIDR blocks that are always allowed regardless of username rotations.
	



	For enterprise deployments, we recommend using both mechanisms. Add your corporate Proxy servers and NAT IP addresses to the dataset:
 

bind policy dataset Proxy_Server_And_NAT_IP_That_Bypass_Password_Spray_Detection 172.16.0.0/12
bind policy dataset Proxy_Server_And_NAT_IP_That_Bypass_Password_Spray_Detection 10.0.0.0/8



	
		Note: 
	 

	
		The status page will still show the counter incrementing for these IP addresses, but they will not be blocked regardless of the counter value.
	 



	 
 


	Remember to include your corporate VPN ranges, cloud NAT gateways, and proxy server IPs in this dataset. Consider both internal and external proxy infrastructure and any third-party security services that might act as intermediaries for authentication traffic.
 


	Status Page Deployment Options



	While our configuration shows the status page on the Gateway vServer with IP-based access control, you can alternatively deploy it on an internal load-balancing vServer protected by AAA authentication. This allows you to:
 


	
		Require administrator authentication via existing LDAP or RADIUS server before accessing the status page
	
	
		Host the status page on an internal IP address
	
	
		Use existing AAA policies to control access
	
	
		Separate monitoring from the Gateway interface
	



	Move the status page configuration to your internal vServer and protect it with appropriate AAA policies.
 


	Testing and Verification



	Pre-Implementation Steps



	
		Take a complete backup of your configuration
	
	
		Test in pre-production environment first
	



	
		Note: 
	 

	
		This is a CTA-contributed article that has had limited testing. Performing your testing in pre-production must not be considered an optional step.
	 



	Status Page Access



	A web-based monitoring interface is provided at https://[your_gateway_url]/logon/LogonPoint/ip_status. Access is restricted by default - only IPs in the "IP_Status_Page_Allowed_Clients" dataset can view status or perform resets:
 

bind policy dataset IP_Status_Page_Allowed_Clients 172.16.0.0/12


	
 


	Status Page Features
 


	The status page provides:
 


	
		Current or specified IP address status
	
	
		Last username attempted from that IP
	
	
		Number of username changes detected
	
	
		Reset counter button to clear the counter for an IP
	



	Using the Status Page
 


	You can:
 


	
		Check the status of your current IP automatically
	
	
		Enter a specific IP address to check its status
	
	
		Reset the counter for any IP address
	



	
		Note: 
	 

	
		If you check a blocked IP's status, the 24-hour lockout period will restart. However, you can reset the counter and remove the lockout anytime.
	 



	Test the configuration by attempting logins to the Gateway vServer with different usernames. The counter increments for each failed username change and resets on successful authentication. IPs are blocked when nine username changes are reached.
 


	Testing
 


	Test the configuration by attempting logins to the Gateway vServer with different usernames. The counter increments for each failed username change and resets on successful authentication. IPs are blocked when nine username changes are reached.
 


	Web Application Firewall Integration



	It is best practice to protect the NetScaler Gateway vServers with a Web Application Firewall (WAF) on the same NetScaler instance. Since this capability is relatively new, many customers haven't implemented it yet. For this reason, our core configuration doesn't depend on WAF. However, if you have deployed WAF, you can enhance the solution by implementing the same blocking logic at the WAF layer. This stops password spraying attempts earlier in the connection flow:
 

set aaa parameter -wafProtection AUTH VPN 
set appfw profile ns-aaa-default-appfw-profile -denylist ON 
bind appfw profile ns-aaa-default-appfw-profile -denylist "($Likelihood_Bad_IP_Counter[CLIENT.IP.SRC.typecast_text_t].GE(9) &amp;&amp; client.ip.src.typecast_text_t.equals_any(\"Proxy_Server_And_NAT_IP_That_Bypass_Password_Spray_Detection\").NOT) &amp;&amp; HTTP.REQ.URL.PATH.CONTAINS(\"/logon/LogonPoint/ip_status\").NOT" -valueType Expression -ruleAction RESET



	This configuration enables the Web Application Firewall to examine traffic before it reaches the Gateway module, terminating detected password spraying attempts with a connection reset. We use a single CONTAINS check for the status page URLs instead of two separate PATH.EQ operations, as the Web Application Firewall, has a limit on the number of conditional operations allowed in a single denylist expression.
 


	Summary



	While Multi-Factor Authentication remains the strongest defense against password spraying, this configuration provides an effective additional layer of protection for environments where MFA cannot be fully implemented. It targets the most common attack pattern—multiple username attempts from a limited IP pool—while minimizing the impact on legitimate users.
 


	The solution's username rotation detection offers a more sophisticated approach than simple failed login counting, making it effective against automated tools while remaining computationally efficient.]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_12/image.png.6fa5273e5e890bc042397bb97ef63659.png" length="64793" type="image/png"/><pubDate>Mon, 16 Dec 2024 18:36:00 +0000</pubDate></item><item><title>POC Guide: Citrix Virtual Apps and Desktops</title><link>https://community.stage.citrix.com/tech-zone/learn/poc-guides/cvad/</link><description><![CDATA[Overview



	Citrix Virtual Apps and Desktops is a virtualization solution that provides access to applications and desktops security from anywhere on any device independent of the device’s operating system and interface. It also allows administrators to centrally manage application and desktop access via a web console for resources deployed on-premises or in a public cloud.
 


	This proof of concept (POC) guide provides the steps to create a Citrix Virtual Apps and Desktops deployment to deliver single and multi-session Windows OS desktops and published applications.  The environment will only be accessible within the internal network. If you require remote access during the POC, please follow the Configure NetScaler Gateway product documentation. The guide will provide the steps to:
 


	
		Install and configure Citrix Virtual Apps and Desktops LTSR v2402.
	
	
		Install and configure Citrix StoreFront.
	
	
		Install and configure the Citrix Virtual Delivery Agent (VDA) on master images.
	
	
		Create a Hosting Connection.
	
	
		Create a machine catalog and delivery group to deliver multi-session desktops and applications.
	
	
		Install Citrix Licenses for the POC environment.
	
	
		Create an initial baseline Citrix policy for the POC environment.
	
	
		Launch a session from Citrix Workspace app.
	



	
		Note:
	 

	
		This guide sets up the Citrix infrastructure components on a single Windows server. This is only recommended for POC environments and should not be used by production users.
	 



	Prerequisites



	The following prerequisites are required to build the Citrix Virtual Apps and Desktops POC environment. Please ensure all requisites are met before moving to the first installation phase.
 


	
		A non-production supported virtualization (ESXi, XenServer, Hyper-V, Nutanix) or public cloud environment.

		
			
				For our POC environment, we are using XenServer v8.
			
		
	
	
		Citrix Virtual Apps and Desktops v2402 ISO downloaded from Citrix Downloads.
	
	
		A single domain-joined Windows Server v2019 or higher virtual machine (for Citrix infrastructure components).
		
			
				2 vCPU, 8 GB RAM, 100 GB Storage.
			
		
	
	
		A single Windows Server v2019 or higher virtual machine (for VDA).
	
	
		A valid Citrix license or trial license file.
	



	Install Citrix Virtual Apps and Desktops



	
		Mount the Citrix Virtual Apps and Desktops ISO to your Citrix Virtual Apps and Desktops Windows Server created for the Citrix infrastructure components.
	



	
 


	
		Connect to the server via RDP with an administrator login.
	



	
 


	
		Run AutoSelect.exe to begin the Citrix Virtual Apps and Desktops installation.
	



	
 


	
		We will be providing access to desktops and applications. Click Start for Virtual Apps and Desktops.
	



	
 


	
		Select Delivery Controller.
	



	
 


	
		Select “I have read, understand, and accept the terms of the license agreement,” and click Next.
	



	
 


	
		We are installing the core Citrix infrastructure components on a single server for our POC. Select Delivery Controller, Web Studio, Director, and License Server, unselect Secure Private Access, and click Next.
	



	
 


	
		We have only a single Delivery Controller for our POC. Click Next.
	



	
 


	
		During the POC, we used Microsoft SQL Express on this server for our Citrix Databases, so we selected the option to install SQL Express here and left the Remote Assistance option checked. Click Next.
	



	
 


	
		Select the default value “Automatically” to allow the install to configure the Windows Firewall rules on the server. Click Next.
	



	
 


	
		Review the Summary page and click Install.
	



	
 


	
		The installation process begins.
	



	
 


	
		During the installation process, your server will reboot. The installation will continue once the server reboots and you are logged back in.
	



	
 


	
		Click Next on the Diagnostics page.
	



	
 


	
		Select “None of the above” on the License Server Data page and click Next.
	



	
 


	
		Unselect “Launch Citrix Site Manager” and click Finish.
	



	
 


	Configure Citrix Virtual Apps and Desktops Site



	
		Launch Citrix Site Manager.
	



	
 


	
		Click Deliver applications and desktops to your users.
	



	
 


	
		Name your site and click Next.
	



	
 


	
		Leave the defaults selected on the Databases window and click Next.
	



	
 


	
		The database is validated.
	



	
 


	
		Select “Use the free 30-day trial” and click Next.
	



	
 


	
		Review the summary window and click Finish.
	



	
 


	
		The Citrix Virtual Apps and Desktops site creation begins.
	



	
 


	
		Note:
	 

	
		This process will take time as the required databases and site configurations are created.
	 



	 
 


	
		Once completed, the Site Overview page will appear.
	



	
 


	
		Close Citrix Site Manager.
	



	Install Citrix StoreFront



	
		Run AutoSelect.exe to begin the Citrix Virtual Apps and Desktops installation.
	



	
 


	
		Click Citrix StoreFront.
	



	
 


	
		Accept the license agreement and click Next.
	



	
 


	
		Click Next on the Review prerequisites window.
	



	
 


	
		Click Install.
	



	
 


	
		The Citrix StoreFront installation begins.
	



	
 


	
		Click Finish.
	



	
 


	
		Click Yes to reboot the server.
	



	
 


	Configure Citrix StoreFront



	
		Open Citrix StoreFront.
	



	
 


	
		Click Create a Store.
	



	
 


	
		Click Next.
	



	
 


	
		Enter a Store Name and then click Next.
	



	
 


	
		The Delivery Controllers window opens. Click Add.
	



	
 


	
		Leave the default selections for Display Name and Type. Click Add.
	



	
 


	
		Enter your local server hostname and click OK.
	



	
 


	
		Leave Transport type to HTTPS and Port to 443, then click OK.
	



	
 


	
		Click Next.
	



	
 


	
		We are not allowing remote access to our POC environment, so we click Next.
	



	
 


	
		Note:
	 

	
		If your POC environment requires remote access, follow the instructions to configure NetScaler Gateway.
	 



	 
 


	
		Choose your preferred authentication method for your POC. For our environment, we only require a username and password. Click Next.
	



	
 


	
		Click Create.
	



	
 


	
		The Store creation process begins.
	



	
 


	
		The Citrix StoreFront store has been created successfully. Click Finish.
	



	
 


	
		Click View or Change Stores to review your newly created store.
	



	
 


	
		Citrix StoreFront configuration has been completed.
	



	
 


	 
 


	
		Note:
	 

	
		It is recommended that the Citrix StoreFront server be secured with HTTPS. In our POC environment, we use HTTP, which requires an extra step on the end user device to access. This step will be detailed later in this guide. To secure our StoreFront server with HTTPS, follow the product documentation.
	 



	Install the Citrix Virtual Delivery Agent



	We will use Citrix Machine Creation Services (MCS) within our POC environment to provision virtual machines. We will create a master image and install the Citrix Virtual Delivery Agent (VDA) onto this master image. The master image contains the OS and applications to be used by end users.
 


	
		Mount the Citrix Virtual Apps and Desktops LTSR v2402 ISO to your Windows Server VDA virtual machine.
	



	
 


	
		Connect to your Windows Server VDA via RDP
	
	
		Run AutoSelect.exe to begin the installation of Citrix Virtual Apps and Desktops.
	



	
 


	
		Click Start for Virtual Apps and Desktops.
	



	
 


	
		Click Virtual Delivery Agent for Windows Multi-session OS.
	



	
 


	
		Select “Create a master MCS image” and click Next.
	



	
 


	
		Click Next.
	



	
 


	
		Select “Citrix Profile Management” and “Citrix Profile Management WMI plug-in” and click Next.
	



	
 


	
		Select "Do it manually," enter the FQDN of the Delivery Controller server configured earlier, and click Test connection.
	



	
 


	
		A green check confirms the Delivery Controller is available. Click Add.
	



	
 


	
		Click Next.
	



	
 


	
		Select “Use Windows Remote Assistance” or “Use Screen Sharing.”  If your POC is configured within a public cloud (Azure, AWS, GCP), select “Is this VDA installed on a VM in the Cloud?”. Click Next.
	



	
 


	
		Leave the default values selected on the Firewall window and click Next.
	



	
 


	
		Review the summary page, then click Finish.
	



	
 


	
		Click Next on the Diagnostics screen.
	



	
 


	
		Ensure “Restart machine” is selected and click Finish.
	



	
 


	
		Repeat this process for any other Virtual Delivery Agent master images you wish to create for your POC environment.
	



	Create Hosting Connection



	A Hosting Connection is required to allow communication between the Citrix Delivery Controller and the Virtual Delivery Agent hypervisor or hyperscaler host(s). We are using XenServer for our deployment, so we will create a hosting connection to our XenServer hypervisor. For details on creating a host connection to other hypervisors or hyperscalers, visit our product documentation.
 


	
		Connect to your Delivery Controller and open Web Studio.
	



	
 


	
		Enter your administrator credentials and click Sign in.
	



	
 


	
		Click Hosting.
	



	
 


	
		Click Add Connection and Resources.
	



	
 


	
		Select your Connection type and enter the appropriate information for your hosting connection. Ensure “Citrix provisioning tools (Machine Creation Services or Citrix Provisioning” is selected, and then click Next.
	



	
 


	
		Select the appropriate storage option for your hosting connection and click Next.
	



	
 


	
		Select the Storage Selection options for your hosting connection and click Next.
	



	
 


	
		Enter a name for the Network, select the correct network, and then click Next.
	



	
 


	
		Review the summary page and click Finish.
	



	
 


	
		The Hosting Connection is created.
	



	
 


	Configure Machine Catalog



	A Machine Catalog is a collection of virtual or physical machines managed as a single entity. Machines in a catalog have the same operating system.
 


	
		Click Machine Catalogs.
	



	
 


	
		Click Create Machine Catalog.
	



	
 


	
		Click Next.
	



	
 


	
		Choose the appropriate Machine Type for your POC. We are creating a Windows Server 2022 catalog, so we select “Multi-session OS” and click Next.
	



	
 


	
		Select “Machines that are power managed,” “Citrix provisioning technology,” and “Citrix Machine Creation Services (MCS),” and then click Next.
	



	
 


	
		Select “Master image”.
	



	
 


	
		Expand Select an image, select your master image on the open blade, then click Done.
	



	
 


	
		The master image is added. Choose the catalog's minimal functional level, in our case, 2206, and click Next.
	



	
 


	
		Choose the number of virtual machines to create and the amount of RAM for the created virtual machines, then click Next.
	



	
 


	
		Based on your identity type, select the appropriate options for the Machine Identities. Enter an account naming scheme, then click Next.
	



	
 


	
		Click Enter credentials.
	



	
 


	
		Enter your administrative credentials and click Done.
	



	
 


	
		Click Next.
	



	
 


	
		Review the summary, enter a Machine catalog name, and then click Finish.
	



	
 


	
		The Machine Catalog creation process begins. Depending on the number of virtual machines you have chosen to create, this can take some time.
	



	
 


	
		The progress window can be hidden, and the complete progress can be followed from the machine catalog screen.
	



	
 


	
		Once completed, the Machine Catalog will be available with the provisioned virtual machines.
	



	
 


	Configure Delivery Group



	A Delivery Group is a collection of machines selected from one or more machine catalogs. It specifies which users can use the applications and desktops provided.
 


	
		Click Delivery Groups.
	



	
 


	
		Click Create Delivery Group.
	



	
 


	
		Select the Machine Catalog created in the previous section and the number of machines for the Delivery Group, then click Next.
	
	
		Leave the “Override site-wide setting” unchecked and click Next.
	



	
 


	
		Choose the user access for the environment. You can restrict the delivery group's use to specific users or groups and allow sessions to roam with users if they change devices. Click Next.
	



	
 


	
		Click Add &gt; From Start menu.
	



	
 


	
		Select the applications to publish, then click OK.
	



	
 


	
		Click Next.
	



	
 


	
		Click Add.
	



	
 


	
		Enter a “Display name,” select the user permissions and options for the Desktops, and click OK.
	



	
 


	
		Click Next.
	



	
 


	
		Click Next.
	



	
 


	
		Enter the name for the Delivery Group and click Finish.
	



	
 


	
		The Delivery Group is created, and the machines are registered and ready for user access.
	



	
 


	Install Citrix Licenses



	
		Ensure you have a valid Citrix Virtual Apps and Desktops license file downloaded to your Citrix infrastructure server.
	
	
		Click Licensing within Web Studio.
	



	
 


	
		Click More and select Browse for License.
	



	
 


	
		Select your license file from the Open dialog window and click Open.
	



	
 


	
		Click Yes to add the license file.
	



	
 


	
		The license file will be read.
	



	
 


	
		Once complete, your Citrix Licenses will be listed.
	



	
 


	Create Citrix Policy



	Citrix Policies are a collection of settings defining how sessions, bandwidth, and security are managed for users, devices, or connection types. You can apply policy settings to physical and virtual machines or users. You can apply settings to individual users at the local level or in security groups in an Active Directory. The configurations define specific criteria and rules. The settings are applied to all connections if you don’t specifically assign the policies.
 


	
		Click Policies.
	



	
 


	
		Click Create Policy.
	



	
 


	
		Expand ICA &gt; File Redirection and select the following: Auto connect client drives, Client drive redirection, Client fixed drives, Client optical drives, and Client removable drives.
	



	
 


	
		Click Disable and Prohibit on each of the selected policies.
	



	
 


	
		Click Next.
	



	
 


	
		Select All users and computers and click Next.
	



	
 


	
		Click Enable policy, provide a Policy name, and click Finish.
	



	
 


	
		Click Change Policy Priorities.
	



	
 


	
		Click the up arrow next to Baseline Policy to move that policy to the top of the list.
	



	
 


	
		Click Save.
	



	
 


	 
 


	
		Note:
	 

	
		Additional policy settings can be enabled within the baseline policy or a new Citrix Policy based on your requirements for your POC environment.
	 



	Install Citrix Workspace app



	The Citrix Workspace app provides instant, secure, and seamless access to Citrix applications and desktops. It is optimized for all client OSs, such as Windows, macOS, Linux, iOS, and Android, and can also be accessed via a browser.
 


	
		Download the appropriate Citrix Workspace app version required for your endpoint device from Citrix Downloads.
	



	
 


	
		Run the CitrixWorkspaceApp installer.
	



	
 


	
		Click Continue.
	



	
 


	
		Accept the Citrix License Agreement and click Continue.
	



	
 


	
		Select the appropriate options for your environment and click Install.
	



	
 


	
		Citrix Workspace app installs.
	



	
 


	
		As mentioned during the StoreFront configuration steps, this guide uses HTTP for the Citrix StoreFront server. Before configuring the Citrix Workspace app, the following registry entries must be configured.
	



	
 


	
 


	
		Once completed, enter the Citrix StoreFront URL and click Continue.
	



	
 


	
		Provide a username and password and click Log On.
	



	
 


	
		Citrix Workspace app displays the applications and desktops you can launch.
	



	
 


	
		Click on an application or desktop from Citrix Workspace app.
	



	
 


	 
 


	Summary



	Following this guide, you successfully deployed a Citrix Virtual Apps and Desktops v2402 LTSR environment to deliver applications and desktops to your end users in a POC environment. Citrix provides many additional features to enhance your environment. Additional information on incorporating these features into your POC environment includes:
 


	Citrix Workspace Environment Management (WEM)
 


	Citrix Session Recording
 


	Citrix Profile Management
 


	Citrix VDA for macOS
 


	Citrix App Layering
 


	Citrix integration with Windows 365
 


	uberAgent]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_12/image.png.2464c633adf7ac7a761bf61f98ac2d71.png" length="453187" type="image/png"/><pubDate>Thu, 12 Dec 2024 16:08:00 +0000</pubDate></item><item><title>Deployment Guide: Machine Profiles- Simplifying Provisioning in Hybrid Multi-Cloud Environments</title><link>https://community.stage.citrix.com/tech-zone/build/deployment-guides/mcs-machine-profile/</link><description>Overview



	Machine profile is a feature available to Citrix administrators who use Machine Creation Services (MCS) to provision machines in Citrix Virtual Apps and Desktops and Citrix DaaS environments.
 


	This article will cover the key reasons for using a machine profile and the steps necessary to configure machine profiles in your environment. This includes how to deploy via:
 


	
		Web Studio through

		
			
				Recommendations
			
			
				New Machine Catalog creation
			
			
				Editing an existing Machine Catalog
			
		
	
	
		PowerShell Scripts (navigate to your desired hypervisor)
	



	What is a machine profile?



	A machine profile serves as a template for virtual machines (VM), providing hardware details, network settings, and features across hypervisors. Machine Creation Services (MCS) captures those details and applies them to provisioned machines in the catalog in a unified manner. In other words, the properties of the machine profile source can be "carried through" to the created machines. The source of the machine profile can be a VM or a VM template (like an Azure template). 
 


	For more information on properties captured, please see the Citrix SDK documentation.
 


	Why use a machine profile?



	A machine profile can help you implement new features rolled out by hypervisors from day one. Take, for instance, when Azure added support for using proximity placement groups. When Microsoft released the feature, if a machine profile was configured to use a proximity placement group, machines provisioned by MCS based on that profile would use the same proximity placement group automatically, with minimal admin input.
 


	Additionally, many hypervisor capabilities are only supported via machine profiles. We highly encourage machine profiles if specific hypervisor capabilities are desired in your Citrix environment. Here are some examples of hypervisor-specific features that are captured from a machine profile:
 


	
		Azure: accelerated networking, availability zones, boot diagnostic, host disk caching
	
	
		AWS: tenancy type, hibernation capability
	
	
		GCP: zones, service accounts, accelerators
	
	
		VMware: folder ID, vTPM data, storage policy, guest OS
	
	
		XenServer: generation, vGPU
	



	A machine profile offers several key improvements to deployments.
 


	
		Automation. Machine profiles simplify the process of deploying virtual machines by enabling administrators to implement predefined hardware configurations across all machines.
	
	
		Standardization. Ensure that all machines adhere to standardized configurations on all hypervisor and hyperscaler platforms, which is crucial for reducing errors.
	
	
		Hardware-versioning. Admins can keep an inventory of template versions, each with different customizations, on their hypervisors that can be used to update catalogs. When editing a catalog, a new machine profile can be picked anytime to update or roll back changes. 
	
	
		Security. Certain platform security features (e.g., trusted launch, disk encryption) require a machine profile. A machine profile can also ensure security policies are applied across all VMs.
	
	
		Cost-savings. Using a machine profile also enables VM hibernation, allowing you to pause idle VMs and save on compute costs. Check out our product docs and blog on creating hibernation-capable VMs.
	



	How do I use a machine profile?



	Machine profiles can be enabled when you create or edit a catalog and through the Recommendations widget available within Web Studio. In this guide, we will be provisioning on Microsoft Azure with machine profiles. Machine profiles are available across different platforms; please see the product documentation for more details.
 


	
 


	Using Web Studio



	There are several different ways to implement machine profiles through Web Studio.
 


	Through Recommendation
 


	
		Note: 
	 

	
		If you do not see the recommendation, refer to the &#x201C;Through Catalog Creation&#x201D; and &#x201C;Through Editing an Existing Catalog&#x201D; sections below to deploy a machine profile.
	 



	 
 


	
		On the Citrix DaaS full configuration console, select View all under Recommendations.
	



	
 


	
		Recommendations can also be accessed via the Machine Catalog page. 
	



	
 


	
		On the next screen, Click Start on the recommendation to Simplify Catalog Configuration with Machine Profile. 
	



	
 


	
		Click Edit on the catalog where you would like to begin the configuration of a machine profile.
	



	
 


	
		Use a machine profile for the catalog and select a machine profile source.
	



	
 


	
		Select the machine profile source (e.g., VM, template) you want to apply and hit Done. 
	



	
 


	Through Catalog Creation



	
		After clicking Create in the Machine Catalog console, you can choose a machine profile under the Image tab.
	



	
 


	
		Choose the machine profile source (ex. VM, template) to apply and hit Done.
	



	
 


	
		The selected machine profile will appear, and you may proceed with the catalog creation by clicking Next.
	



	
 


	Through Editing an Existing Catalog



	
		Select the catalog and click Edit Machine Catalog.
	



	
 


	
		Select Machine Profile.
	



	
 


	
		Note: 
	 

	
		When editing a catalog with a machine profile selected, you can update the machine profile by selecting the pencil icon.
	 



	
 


	
		You may see a warning when the selected machine profile differs from the original catalog properties. Select Confirm to proceed with the changes, or select Cancel if you do not wish to continue. 
	



	
 


	
		Select Apply and Save.
	



	
 


	Using Powershell



	To configure a machine profile through PowerShell, use the -MachineProfile parameter available within the New-ProvScheme operation. The MachineProfile parameter is a string containing a Citrix inventory item path to the VM or VM template that will be used as the machine profile.
 


	Example Script: 
 


	
		$provScheme = New-ProvScheme -ProvisioningSchemeName AWS1 -HostingUnitName aws-test -IdentityPoolName idPool1 -MasterImageVM "XDHyp:\HostingUnits\aws-test\TemplateAmi (ami-06927522c36bf4109).template" -CleanOnBoot -MachineProfile "XDHyp:\HostingUnits\aws-test\us-east-1a.availabilityzone\server-vda (i-0c136ee2a01a3dd60).vm"
	 



	For detailed instructions on how to apply a machine profile to new and existing catalogs, follow sample scripts from GitHub (navigate to your desired hypervisor directory&#x2019;s &#x201C;MachineProfile folder&#x201D;). 
 


	Summary 



	With the Machine Creation Services (MCS) machine profile feature, you can roll out hardware updates to VMs using a single centralized workflow. Available for Citrix Virtual Apps and Desktops and Citrix DaaS environments across all supported hypervisors and hyperscalers, the machine profile helps you maintain your environment at scale.</description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_12/image.png.c8f6332323d85257210f243ccd969cea.png" length="22420" type="image/png"/><pubDate>Thu, 12 Dec 2024 15:32:00 +0000</pubDate></item><item><title>PoC Guide: Integrating Windows 365 Cloud PCs with Citrix Session Recording Service</title><link>https://community.stage.citrix.com/tech-zone/learn/poc-guides/windows-365-citrix-session-recording-service/</link><description><![CDATA[This Proof-of-Concept Guide provides the steps to integrate Windows 365 Cloud PCs with Citrix Session Recording Service. The following is covered in this guide:
	 

	
		
			Deploy Citrix Session Recording resources to Azure subscription 
		
		
			Deploy Citrix Session Recording Agent to Windows 365 Cloud PCs
		
		
			Configure Citrix Session Recording policies
		
		
			Replay a recording with Citrix Session Recording Player
		
	

	
		 
	 

	
		Requirements and Prerequisites 
	

	
		
			Citrix Session Recording 2402 LTSR  or later
		
		
			Microsoft Message Queuing on Windows 365 Cloud PCs
		
		
			Citrix Cloud Account: You'll need an active Citrix Cloud account with the necessary licenses.
		
		
			Windows 365 Enterprise Subscription: Ensure you have a Windows 365 Enterprise subscription to access Cloud PCs.
		
		
			Microsoft Intune entitlement: You need an active Microsoft Intune environment in your Entra ID directory.
		
		
			Azure Administrator accounts: You will require an Entra ID global administrator and Intune Global administrator privileges or someone with access to these accounts.
		
	

	
		 
	 

	
		Deploy Session Recording resources 
	

	
		This section guides you through the procedure of creating and deploying Session Recording resources to an Azure subscription from within the Session Recording service through a host connection, including:
	 

	
		
			Session Recording servers
		
		
			Database
		
		
			Storage
		
		
			Load Balancer
		
	

	
		Create and deploy a site through a host connection
	

	
		
			Select Configuration &gt; Server Management from the left navigation of the Session Recording service.
		
	

	
		
	 

	
		
			On the Add connection page, give the new host connection a name and a description (optional). Enter your Azure subscription ID and the following required information about your application registration:

			
				
					Application (client) ID
				
				
					Service principal object ID (ID of the service principal object associated with the application)
				
				
					Directory (tenant) ID
				
				
					Client secret
				
				
					Secret expiration date
				
				
					
				
			
		
	

	
		
			Click Save to test whether the host connection you specify is available.
		
	

	
		
			Note:
		 

		
			If the host connection you specify is available, you’re taken back to the Host Connection page and prompted that the host connection is added successfully.
		 
	

	
		
	 

	
		
			On the Server Management page, click Create site. The Create site page appears.
		
	

	
		
	 

	
		
			Select Create and deploy a site through a host connection. The main steps are listed in the left navigation.
		
	

	
		
	 

	
		
			Site Information - Enter a Site name and description, select a Host Connection that connects to your Azure subscription, and specify a Region.
		
	

	
		
	 

	
		 
	 

	
		
			After completing the site information, click Next to continue.
		
	

	
		
			About your deployment—Provide information about your recording needs to get recommendations for VM and storage configurations. You can skip this step by clicking “I’m good, skip this step” or “Next.”
		
	

	
		
	 

	
		 
	 

	
		
			Network - Select the Virtual network and Subnet your Session Recording resource will connect to.
		
	

	
		
	 

	
		 
	 

	
		
			After completing the Network information, click Next to continue.
		
	

	
		
			Virtual Machines - Create virtual machines (VMs) as your Session Recording servers. Enter the following required information about your Session Recording servers:

			
				
					Session Recording server version to install – 2402 LTSR
				
				
					Image – Windows Server 2022 Datacenter: Azure Edition x64 Gen2
				
				
					Size – Standard_D4s_v3-4vcpus, 16 GiB memory
				
				
					Number of VMs - 2
				
			
		
	

	
		Specify an administrator account for the virtual machines.
	 

	
		
	 

	
		 
	 

	
		
			After completing the Virtual machine information, click Next to continue.
		
	

	
		
			Domain and certificate—Join the Session Recording servers in the same domain as your VDAs and specify a certificate for them. Only .pfx certificate files are accepted.
		
	

	
		
	 

	
		
			After completing the Domain and certificate information, click Next to continue.
		
	

	
		
			Storage - Configure a storage account and file shares to store your recording files. 

			
				
					Performance: Standard: Recommended for most scenarios (general-purpose v2 account)
				
				
					Redundancy: Locally redundant storage (LRS)
				
				
					File shares: 1 file share
				
			
		
	

	
		
	 

	
		
			After completing the Storage information, click Next to continue.
		
	

	
		
			Databases - Create 2 SQL databases for recording and logging data.

			
				
					Service tier: General Purpose
				
				
					Compute tier: Provisioned
				
				
					Hardware configuration: Standard-series (Gen5)
				
				
					vCores: 2
				
				
					Data max size (GiB): 32
				
			
		
	

	
		Specify a database administrator account.
	 

	
		
	 

	
		
			After completing the Database information, click Next to continue.
		
	

	
		
			Load balancer—Create a load balancer to distribute workload among the Session Recording servers. In the Restrict access of the load balancer to only the following addresses field, enter the IP addresses or rangers of your VDAs and separate them by a comma.
		
	

	
		
	 

	
		
			After completing the Load balancer information, click Next to continue.
		
	

	
		
			Tags – Optionally, tags can be applied to Azure Resources.
		
	

	
		
	 

	
		
			After completing the Tags information, click Next to continue.
		
	

	
		
			Secure Client - Create a secure client to onboard the Session Recording server to the Session Recording service. Select Create client.
		
	

	
		
	 

	
		
			After completing the Secure Client information, click Next to continue.
		
	

	
		
			Summary – Review the summary of the resources that will be created in the Azure subscription. Click Start deployment to continue.
		
	

	
		
	 

	
		
			The Citrix Session Recording deployment will begin. You can select the Close dialog and check the status later if necessary.
		
	

	
		
	 

	
		 
	 

	
		
	 

	
		 
	 

	
		
	 

	
		
			When a site deployment is complete, you can expand the site and view and manage the resources.
		
	

	
		 
	 

	
		Deploy Citrix Session Recording Agent with Intune
	

	
		Upload Citrix Session Recording Agent to Intune
	

	
		
			Sign in to the Microsoft Intune Admin Center: https://intune.microsoft.com/
		
	

	
		
	 

	
		
			Select Apps &gt; All apps &gt; Select Add
		
	

	
		
	 

	
		
			On the Select app type pane, under the Other app types, Select Line-of-business app.
		
	

	
		
	 

	
		
			Click Select.
		
		
			On the Line-of-business app dialog, Click Select.
		
	

	
		
	 

	
		
			On the Add App pane, click Select app package file.
		
	

	
		
	 

	
		
			On the App package file pane, Select the browse button. Select the Citrix Session Recording Agent file (SessionRecordingAgentx64.msi), and Select OK.
		
	

	
		
	 

	
		 
	 

	
		Configure Citrix Session Recording Agent App Information
	

	
		
			Provide the following information on the App Information tab, and Select Next.

			
				
					Name: Citrix Session Recording Agent
				
				
					Description: Citrix Session Recording Agent
				
				
					Publisher: Citrix
				
				
					App install context: Device
				
				
					Command-line arguments: SessionRecordingAgentx64.msi /q SESSIONRECORDINGSERVERNAME=yourservername SESSIONRECORDINGBROKERPROTOCOL=HTTPS SESSIONRECORDINGBROKERPORT=443 SESSIONRECORDINGAUTHENTICATION="Citrix Cloud" SESSIONRECORDINGRPC="Websocket"
				
			
		
	

	
		
			
				
					Category: Computer Management
				
				
					Show this as a featured app in the Company Portal: No
				
			
		
	

	
		
	 

	
		 
	 

	
		
			Note:
		 

		
			When using the command-line argument to install the Citrix Session Recording agent, the following switches are used:
		 
	

	
		
			/q specifies quiet mode.
		
		
			yourservername is the NetBIOS name or FQDN of the machine hosting the Session Recording server. If not specified, this value defaults to localhost.
		
		
			yourbrokerprotocol is the HTTP or HTTPS that the Session Recording agent uses to communicate with the Session Recording Broker. If not specified, this value defaults to HTTPS.
		
		
			yourbrokerport is the port number the Session Recording agent uses to communicate with the Session Recording Broker. If not specified, this value defaults to zero, which directs the Session Recording Agent to use the default port number for your selected protocol: 80 for HTTP or 443 for HTTPS.
		
		
			SESSIONRECORDINGAUTHENTICATION is the authentication type between the Session Recording agent and the Session Recording server. This parameter is required only when you want to enable Azure AD support. Add this parameter and set it to Citrix Cloud to enable Azure AD support.
		
		
			SESSIONRECORDINGRPC is the communication method between the Session Recording agent and the Session Recording server. This parameter is required only when you want to enable Azure AD support. Add this parameter and set it to Websocket to enable Azure AD support.
		
		
			SESSIONRECORDINGIDP specifies the identity type. This parameter is required only when you want to enable Azure AD support. Add this parameter and set it to IDP to enable Azure AD support.
		
	

	
		 
	 

	
		
			On the Assignments tab, Select Add Group under the Required section.
		
	

	
		
	 

	
		
			Select an Entra ID group for the Citrix Session Recording Agent assignment. Click Select.
		
	

	
		
	 

	
		
			Review the assignments and select Next.
		
	

	
		
	 

	
		
			On the Review + Create tab, Select Create
		
	

	
		
	 

	
		
	 

	
		 
	 

	
		The Citrix Session Recording agent will be deployed to the Windows 365 Cloud PCs, and the device status will be updated to reflect this in Microsoft Intune Admin Center.
	 

	
		
	 

	
		 
	 

	
		
	 

	
		 
	

	
		Deploy Session Recording Policies
	

	
		Citrix Session Recording lets you view and configure session recording, event detention, and event response policies for a specific site. Each policy you create or activate applies to all Session Recording servers of a site.
	 

	
		
			On the left navigation menu, Select Policies.
		
	

	
		
	 

	
		
			On the Recording policy pane, Select Add policy.
		
	

	
		
	 

	
		
			On the Add recording policy dialog, specify a Name and Description, then Select Add Rule.
		
	

	
		
	 

	
		
			On the Add rule dialog, specify a rule Name and Description. Select Delivery groups and VDA machines, then click Configure.
		
	

	
		
	 

	
		
			On the Select delivery groups or VDA machines dialog, Select Add new.
		
	

	
		
	 

	
		
			On the Add search query dialog, enter CTX- in the keyword of the machine or delivery group name field. Select Add search query.
		
	

	
		
	 

	
		
			On the Select delivery groups or VDA machines, Select the delivery groups or VDA you want and click the Save button to finish the configuration.
		
	

	
		
	 

	
		
			In the Add rule dialog, under Recording action, Select Enable session recording with notification.  Click Save.
		
	

	
		
	 

	
		
			On the Add recording policy dialog, add additional rules for the policy, otherwise select Save.
		
	

	
		
	 

	
		
			After the new policy is created, find it on the Recording policy tab.
		
	

	
		
	 

	
		
			Select the toggle to activate the Default Windows 365 Policy.
		
	

	
		
	 

	
		For more information on Citrix Session Record policies, see:
	 

	
		
			Configure Session Recording Policies
		
		
			Configure Event Detection Policies
		
		
			Configure Event Response Policies
		
	

	
		 
	 

	
		Connect to Windows 365 Cloud PC
	

	
		Launch a Windows 365 Cloud PC connection to a delivery group or machine name you added to the Session Recording Policy. A prompt notifies the user that the session is recorded. Quickly open an application, then log off the session.
	 

	
		 
	 

	
		
	 

	
		Playback Session Recording
	 

	
		
			Log into Citrix Cloud and go to Session Recording Service Console. Select Recordings, then All Recordings.
		
	

	
		
	 

	
		
			Click the Play button on your new recording to play back your session.
		
	

	
		          
	 

	
		 
	 

	
		 
	

	
		Summary
	

	
		This POC guide walked you through setting up Citrix Session Recording in Microsoft Azure and deploying the Citrix Session Recording agent with Microsoft Intune to Windows 365 Cloud PCs. To learn more about Citrix Session Recording Service, visit the following:
	 

	
		
			Product Documentation: Citrix Session Recording Service
		
		
			Product Documentation: Third-party SIEM integration]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_11/image.png.3abd38ea482f5d20480cb040e0bd9959.png" length="47059" type="image/png"/><pubDate>Fri, 29 Nov 2024 18:45:00 +0000</pubDate></item><item><title>Deployment Guide: Using Terraform for Daily Administrative Operations</title><link>https://community.stage.citrix.com/tech-zone/automation/terraform-daily-administration/</link><description><![CDATA[Deployment Guide: Using Terraform for Daily Administrative Operations 
	 



	This guide focuses on daily administrative operations using Terraform after the initial deployment:
 


	
		Keeping the .tfstate file safe
	
	
		Importing an existing Infrastructure into Terraform
	
	
		Checking an Infrastructure for changes after the initial deployment
	
	
		Checking the Endpoint App settings (Workspace App, Enterprise Browser) for changes after the initial deployment
	
	
		Changing settings/configurations of already deployed entities (Machine Catalogs, Delivery Groups, Policies, Workspace App configurations)
	
	
		Adding/removing entities to/from the infrastructure
	



	Terraform's flexibility and wide range of integrations make it a valuable tool for DevOps and infrastructure management. 
	 
 


	Overview



	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.
 


	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.
 


	IaC allows you to build, change, manage, and check your infrastructure safely and consistently by defining resource configurations.
 


	Terraform is declarative: 
	You tell the Terraform provider the desired state of your planned deployment. 
	You do not need to write every step in your code to achieve this goal. 
	The provider takes care of all needed steps.
 


	Terraform is idempotent: 
	No matter how often you execute the same Terraform code - your Infrastructure will remain the same unless you change the code. 
	You will see in the examples below, that we change only the code parts we need to alter the Infrastructure and rerun the complete Terraform code. 
	 
	On Citrix Tech Zone´s Automation page, you can find various guides on initially deploying Citrix DaaS or Citrix CVAD using Terraform. 
	 
 


	
		Note:
	 

	
		Please find a description of installing Terraform and initial configurations in our Tech Zone Deployment Guide: Installing Terraform and Configuring the Citrix Terraform Provider.
	 

	
		If you want to see all the mentioned examples in this guide in action, please watch our Tech Insight video on our YouTube channel. 
		 
	 



	 
 


	The State



	Terraform stores information about your infrastructure and configuration in its State. 
	It primarily maps the deployed resources to the infrastructure and keeps needed metadata.
 


	The State is stored by default in a local file named "terraform.tfstate". 
	Terraform uses the State to determine which changes must be made to your infrastructure. Terraform automatically refreshes the state with the existing infrastructure before any alterations are initiated.
 


	
		Important:
	 

	
		Be very careful with a locally saved .tfstate file—it is recommended that you store it in a safe, encrypted manner with limited access and use a versioning control system. 
		 
	 



	 
 


	
		Caution:
	 

	
		Terraform´s .tfstate can contain sensitive data like database passwords, user passwords, or private keys.
	 

	
		If you use locally stored State files (=Local State), they are stored on your machine as plain-text JSON files. 
		If you use remote stored State files (=Remote State), the State is only transferred from the remote backend to memory when Terraform uses it. Be aware that the transport can be unencrypted. 
		 
	 



	The .tfstate file can grow quite large—for example, the .tfstate file in our demo environment is more than 5MB.
 


	Example of a representation of a Machine Catalog in a locally stored .tfstate file:
 


	
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}	
	
		
			
				
					
						
							...,
						 

						
							 {
						 

						
							      "mode": "managed",
						 

						
							      "type": "citrix_machine_catalog",
						 

						
							      "name": "machine_catalog_0",
						 

						
							      "provider": "provider[\"registry.terraform.io/citrix/citrix\"]",
						 

						
							      "instances": [
						 

						
							        {
						 

						
							          "schema_version": 0,
						 

						
							          "attributes": {
						 

						
							            "allocation_type": "Random",
						 

						
							            "built_in_scopes": [
						 

						
							              "00000000-0000-0000-0000-000000000000"
						 

						
							            ],
						 

						
							            "description": "Machine Catalog on Azure based on Windows 11 Single-Session  for TMM-Gerhard",
						 

						
							            "id": "95d6xxxx-xxxx-xxxx-xxxx-xxxxxxxxdc35",
						 

						
							            "inherited_scopes": [],
						 

						
							            "is_power_managed": null,
						 

						
							            "is_remote_pc": null,
						 

						
							            "machine_accounts": null,
						 

						
							            "machine_catalog_folder_path": null,
						 

						
							            "metadata": null,
						 

						
							            "minimum_functional_level": "L7_34",
						 

						
							            "name": "MC-TMM-GK-AZURE-W11-SS",
						 

						
							            "provisioning_scheme": {
						 

						
							              "availability_zones": [
						 

						
							                "1"
						 

						
							              ],
						 

						
							              "aws_machine_config": null,
						 

						
							              "azure_machine_config": {
						 

						
							                "azure_master_image": {
						 

						
							                  "container": null,
						 

						
							                  "gallery_image": null,
						 

						
							                  "master_image": "TMM-GK-W11-SS-M_OsDisk_1_7d8xxxxxxxx",
						 

						
							                  "resource_group": "tmm_gerhard_westeurope",
						 

						
							                  "shared_subscription": null,
						 

						
							                  "storage_account": null
						 

						
							                },
						 

						
							                "azure_pvs_config": null,
						 

						
							                "disk_encryption_set": null,
						 

						
							                "enroll_in_intune": null,
						 

						
							                "image_update_reboot_options": null,
						 

						
							                "license_type": "Windows_Client",
						 

						
							                "machine_profile": {
						 

						
							                  "machine_profile_resource_group": "tmm_gerhard_westeurope",
						 

						
							                  "machine_profile_template_spec_name": null,
						 

						
							                  "machine_profile_template_spec_version": null,
						 

						
							                  "machine_profile_vm_name": "TMM-GK-W11-SS-M"
						 

						
							                },
						 

						
							                "master_image_note": "",
						 

						
							                "service_offering": "Standard_D2s_v5",
						 

						
							                "storage_type": "Premium_LRS",
						 

						
							                "use_azure_compute_gallery": null,
						 

						
							                "use_managed_disks": true,
						 

						
							                "vda_resource_group": "tmm_gerhard_westeurope",
						 

						
							                "writeback_cache": null
						 

						
							              },
						 

						
							              "custom_properties": null,
						 

						
							              "gcp_machine_config": null,
						 

						
							              "hypervisor": "bec2xxxx-xxxx-xxxx-xxxx-xxxxxxxxf8f0",
						 

						
							              "hypervisor_resource_pool": "2c70xxxx-xxxx-xxxx-xxxx-xxxxxxxx87cb",
						 

						
							              "identity_type": "ActiveDirectory",
						 

						
							              "machine_account_creation_rules": {
						 

						
							                "naming_scheme": "TMM-GK-W11-SS-#",
						 

						
							                "naming_scheme_type": "Numeric"
						 

						
							              },
						 

						
							              "machine_domain_identity": {
						 

						
							                "domain": "democloud.corp",
						 

						
							                "domain_ou": "OU=_AZURE,OU=_WORKER,OU=_CITRIX,OU=TACG,OU=EBC-TMM,DC=democloud,DC=corp",
						 

						
							                "service_account": "xxxxxxxxxxxxxxxxxxxxxxxx",
						 

						
							                "service_account_password": "xxxxxxxxxxxxxxxxxxxxxxxx"
						 

						
							              },
						 

						
							              "metadata": null,
						 

						
							              "network_mapping": null,
						 

						
							              "number_of_total_machines": 1,
						 

						
							              "nutanix_machine_config": null,
						 

						
							              "scvmm_machine_config": null,
						 

						
							              "vsphere_machine_config": null,
						 

						
							              "xenserver_machine_config": null
						 

						
							            },
						 

						
							            "provisioning_type": "MCS",
						 

						
							            "remote_pc_ous": null,
						 

						
							            "scopes": [],
						 

						
							            "session_support": "SingleSession",
						 

						
							            "tags": null,
						 

						
							            "tenants": null,
						 

						
							            "vda_upgrade_type": null,
						 

						
							            "zone": "4050xxxx-xxxx-xxxx-xxxx-xxxxxxxxc507"
						 

						
							          },
						 

						
							          "sensitive_attributes": [
						 

						
							            [
						 

						
							              {
						 

						
							                "type": "get_attr",
						 

						
							                "value": "provisioning_scheme"
						 

						
							              },
						 

						
							              {
						 

						
							                "type": "get_attr",
						 

						
							                "value": "machine_domain_identity"
						 

						
							              },
						 

						
							              {
						 

						
							                "type": "get_attr",
						 

						
							                "value": "service_account_password"
						 

						
							              }
						 

						
							            ]
						 

						
							          ]
						 

						
							        }
						 

						
							      ]
						 

						
							    },
						 

						
							...Sensitive data would be readable in clear text.
						 
					
				
			
		
	



	Sensitive data would be readable in clear text.
 


	Keeping the State safe



	The recommended way to keep the State secure is not to use Local State but Remote State. 
	The State is written to a remote data store. Terraform supports HCP Terraform, HashiCorp Consul, Amazon S3, Azure Blob Storage, Google Cloud Storage, Alibaba Cloud OSS, and more.
 


	As mentioned, Terraform will not persist the State anywhere on the local disk. 
	It will write the State locally only in the case of a non-recoverable error, where writing the State to the backend fails. This is to prevent data loss.
 


	We use an Azure Storage Account to store the State in this example. 
	You can use Terraform to create the needed Storage Account:
 


	
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}	
	
		
			
				
					
						
							data "azurerm_resource_group" "GetAzureRG" {
						 

						
							  name = "TMM-Gerhard-WestEurope"
						 

						
							}
						 

						
							 
						 

						
							resource "azurerm_storage_account" "AzureStorageAccountForTerraform" {
						 

						
							  name                              = "${var.TF-StorageAccount-Name}"
						 

						
							  resource_group_name               = data.azurerm_resource_group.GetAzureRG.name
						 

						
							  location                          = data.azurerm_resource_group.GetAzureRG.location
						 

						
							  account_tier                      = "Standard"
						 

						
							  account_replication_type          = "LRS"
						 

						
							  allow_nested_items_to_be_public   = false
						 

						
							 
						 

						
							  tags                              = {
						 

						
							                                            environment  = "TMM-Gerhard"
						 

						
							  }
						 

						
							}
						 

						
							 
						 

						
							resource "azurerm_storage_container" "AzureStorageAccountContainerForTerraform" {
						 

						
							    depends_on = [ azurerm_storage_account.AzureStorageAccountForTerraform ]
						 

						
							  name                              = "${var.TF-StorageAccountContainer-Name}"
						 

						
							  storage_account_name              = azurerm_storage_account.AzureStorageAccountForTerraform.name
						 

						
							  container_access_type             = "private"
						 

						
							}
						 

						
							 
						 
					
				
			
		

		
			Or you can use PowerShell to create the needed Storage Account:
		 

		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
			
				
					
						
							
								# Get Azure Resource Group 
								$ResourceGroup = Get-AzResourceGroup -Name $RESOURCE_GROUP_NAME 
								 
								# Create Azure Storage Account 
								$StorageAccount = New-AzStorageAccount -ResourceGroupName $ResourceGroup.Name -Name $TF_StorageAccount_Name -SkuName Standard_LRS -Location $ResourceGroup.Location -AllowBlobPublicAccess $false
							 

							
								# Create Azure Blob Container 
								New-AzStorageContainer -Name $TF_StorageAccountContainer_Name -Context $StorageAccount.context
							 

							
								# Retrieve Storage Container Access Key 
								$SC_Access_Key=(Get-AzStorageAccountKey -ResourceGroupName $ResourceGroup.Name -Name $StorageAccount.Name)[0].value
							 
						
					
				
			
		

		
			Now you can configure Terraform to use Remote State using a backend configuration:
		 

		
			
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}			
			
				
					
						
							
								
									terraform {
								 

								
									  required_providers {
								 

								
									    azurerm = {
								 

								
									      source  = "hashicorp/azurerm"
								 

								
									      version = "=&gt;4.9"
								 

								
									    }
								 

								
									  }
								 

								
									 
								 

								
									  backend "azurerm" {
								 

								
									    resource_group_name  = "tmm_gerhard_westeurope"
								 

								
									    storage_account_name = "tmmgkstorageaccount"
								 

								
									    container_name       = "terraform"
								 

								
									    key                  = "terraform.tfstate"
								 

								
									    use_oidc             = true
								 

								
									    client_id            = "a99fxXxX-xXxX-xXxX-xXxX-xXxXxXxXxd2de"
								 

								
									    client_secret        = "xXxX-xXxX-xXxX-xXxX-xXxXxXxXx"
								 

								
									    subscription_id      = "893axXxX-xXxX-xXxX-xXxX-xXxXxXxXx2de793"
								 

								
									    tenant_id            = "db32xXxX-xXxX-xXxX-xXxX-xXxXxXxXx2de49f"
								 

								
									  }
								 

								
									 
								 

								
									}
								 

								
									 
								 

								
									provider "azurerm" {
								 

								
									  features {}
								 

								
									}
								 
							
						
					
				
			
		

		
			 
		 

		
			
				Caution:
			 

			
				You cannot use variables in the backend configuration part. You need to use define the settings in clear text, including all sensitive settings. 
				 
			 
		

		
			 
		 

		
			
				Important:
			 

			
				Azure Storage blobs are automatically locked before any operation writes State. 
				This prevents concurrent State operations. 
				 
				All Data stored in an Azure blob is encrypted. 
				Terraform retrieves the State from the backend and stores it in local memory. 
				The Transport is usually encrypted, as accessing the Azure Storage Container enforces a TLS-capable connection. 
				 
			 
		

		
			Terraform initializes successfully using Remote State:
		 

		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
			
				
					
						
							
								PS C:\_TERRAFORM\_BACKEND&gt; terraform init 
								Initializing the backend... 
								 
								Successfully configured the backend "azurerm"! Terraform will automatically 
								use this backend unless the backend configuration changes. 
								Initializing provider plugins... 
								- Finding hashicorp/azurerm versions matching "&gt;= 4.6.0"... 
								- Finding citrix/citrix versions matching "&gt;= 1.0.5"... 
								- Installing hashicorp/azurerm v4.10.0... 
								- Installed hashicorp/azurerm v4.10.0 (signed by HashiCorp) 
								- Installing citrix/citrix v1.0.7... 
								- Installed citrix/citrix v1.0.7 (self-signed, key ID BD4BD0E690CB7D88) 
								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:\_TERRAFORM\_BACKEND&gt;
							 
						
					
				
			
		

		
			You can see the .tfstate file in the Azure Storage Container: 
			
		 

		
			You have successfully configured Terraform to use Remote State. 
			 
		 

		
			Importing an existing Infrastructure into Terraform
		

		
			You can import an existing Infrastructure using a PowerShell script, which enables you to use all the benefits of Terraform.
		 

		
			The script works with Citrix DaaS- and Citrix CVAD-based environments. 
			The script collects the list of resources, imports them into Terraform, and generates the Terraform skeletons for the resources. 
			
		 

		
			
				Note:
			 

			
				The Onboarding script is still in Tech Preview. 
				You can download the actual version of the Onboarding script from our GitHub repository. 
				 
			 
		

		
			The needed Terraform configuration is straightforward:
		 

		
			
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}			
			
				
					
						
							
								
									terraform {
								 

								
									  required_version = "&gt;= 1.9.7"
								 

								
									 
								 

								
									  required_providers {
								 

								
									    citrix = {
								 

								
									      source  = "citrix/citrix"
								 

								
									      version = "=1.0.7"
								 

								
									    }
								 

								
									  }
								 

								
									 
								 

								
									  backend "local" {}
								 

								
									}
								 
							
						
					
				
			
		

		
			We altered the script to insert all sensitive information directly into the script and do not need to store sensitive information in variables:
		 

		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
			
				
					
						
							
								...
							 

							
								[CmdletBinding()]
							 

							
								Param (
							 

							
								    [Parameter(Mandatory = $false)]
							 

							
								    [string] $CustomerId = "dcxXxXxXxXx",
							 

							
								 
							 

							
								    [Parameter(Mandatory = $false)]
							 

							
								    [string] $ClientId = "4045xXxX-xXxX-xXxX-xXxX-xXxXxXxXxd2dea,
							 

							
								    
							 

							
								    [Parameter(Mandatory = $false)]
							 

							
								    [string] $ClientSecret ="xXxXxXxXxXxXxXxXx",
							 

							
								 
							 

							
								    [Parameter(Mandatory = $false)]
							 

							
								    [string] $DomainFqdn,
							 

							
								 
							 

							
								    [Parameter(Mandatory = $false)]
							 

							
								    [string] $Hostname = "api.cloud.com",
							 

							
								 
							 

							
								    [Parameter(Mandatory = $false)]
							 

							
								    [ValidateSet("Production", "Staging")]
							 

							
								    [string] $Environment = "Production",
							 

							
								 
							 

							
								    [Parameter(Mandatory = $false)]
							 

							
								    [switch] $SetDependencyRelationship,
							 

							
								 
							 

							
								    [Parameter(Mandatory = $false)]
							 

							
								    [switch] $DisableSSLValidation
							 

							
								)
							 

							
								...
							 
						
					
				
			
		

		
			Before running the script, the Terraform provider must be initialized. 
			Afterwards, you can start the script:
		 

		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
			
				
					
						
							
								PS C:\_TERRAFORM\_ONBOARDING&gt; .\terraform-onboarding-v3.ps1 
								 
								 
								    Directory: C:\_TERRAFORM\_ONBOARDING 
								 
								 
								Mode                 LastWriteTime         Length Name 
								----                 -------------         ------ ---- 
								-a----        11/12/2024  11:41 AM              0 citrix.tf 
								-a----        11/12/2024  11:41 AM              0 import.tf 
								-a----        11/12/2024  11:41 AM              0 resource.tf 
								Initializing the backend... 
								Initializing provider plugins... 
								- Reusing previous version of citrix/citrix from the dependency lock file 
								- Using previously-installed citrix/citrix v1.0.6 
								 
								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. 
								citrix_azure_hypervisor_resource_pool.azure_hypervisor_resource_pool_1_0: Importing from ID "903f991f-5eb1-45b7-b85a-8e6b35529a64,d10c0943-ec66-4435-982f-1ec88e6d83d9"... 
								citrix_azure_hypervisor_resource_pool.azure_hypervisor_resource_pool_1_0: Import prepared! 
								  Prepared citrix_azure_hypervisor_resource_pool for import 
								citrix_azure_hypervisor_resource_pool.azure_hypervisor_resource_pool_1_0: Refreshing state... [id=d10c0943-ec66-4435-982f-1ec88e6d83d9] 
								 
								Import successful! 
								 
								The resources that were imported are shown above. These resources are now in 
								your Terraform state and will henceforth be managed by Terraform. 
								 
								citrix_application_icon.application_icon_11: Importing from ID "3"... 
								citrix_application_icon.application_icon_11: Import prepared! 
								  Prepared citrix_application_icon for import 
								citrix_application_icon.application_icon_11: Refreshing state... [id=3] 
								 
								Import successful!
							 

							
								...
							 
							The resources that were imported are shown above. These resources are now in 
							your Terraform state and will henceforth be managed by Terraform. 
							 
							citrix.tf 
							citrix_admin_role.tf 
							citrix_admin_scope.tf 
							citrix_application.tf 
							citrix_application_icon.tf 
							citrix_azure_hypervisor.tf 
							citrix_azure_hypervisor_resource_pool.tf 
							citrix_delivery_group.tf 
							citrix_machine_catalog.tf 
							citrix_policy_set.tf 
							citrix_vsphere_hypervisor.tf 
							citrix_vsphere_hypervisor_resource_pool.tf 
							citrix_zone.tf 
							 
							 
							PS C:\_TERRAFORM\_ONBOARDING&gt;
						
					
				
			
		

		
			After about 5 minutes, the script has successfully imported the environment. 
			You now have all entities transformed into Terraform files which can be viewed and edited e.g., in Visual Studio Code: 
			
		 

		
			You have successfully transformed your existing Infrastructure in Infrastructure-as-Code (IaC). 
			 
		 

		
			Checking an Infrastructure for changes after the initial deployment
		

		
			You can leverage Terraform to check your infrastructure for alterations/changes that might not have been wanted or that accidentally happened.
		 

		
			Terraform will notify you about these changes and can revert them after asking for consent. 
			You need a connection to your Infrastructure and a valid State. 
			 
		 

		
			Example 1: Deactivation of AutoScale
		

		
			In our sample Infrastructure, AutoScale is usually enabled for all Delivery Groups, but someone changed the AutoScale Settings of a Delivery Group. This is unwanted, as additional costs could arise if AutoScale is disabled. 
			Citrix WebStudio shows the deactivation of AutoScale: 
			
		 

		
			As it would be impracticable to check all essential settings using WebStudio, we leverage Terraform to scan our environment for changes: 
			You only need to run terraform plan in your Terraform directory, where your Terraform files are saved to scan for changes:
		 

		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
			
				
					
						
							
								PS C:\_TERRAFORM\_CCOnAzureV2\CCOnAzureV2-DeployCCEntities&gt; terraform plan 
								data.citrix_zone.GetTFAzureZoneID: Reading... 
								data.citrix_zone.GetTFAzureZoneID: Read complete after 0s [id=12f9de5d-fa7f-4739-aaa2-8338daabe878] 
								citrix_azure_hypervisor.CreateAzureHostingConnection: Refreshing state... [id=903f991f-5eb1-45b7-b85a-8e6b35529a64] 
								time_sleep.wait_30_seconds: Refreshing state... [id=2024-10-23T17:42:53Z] 
								citrix_azure_hypervisor_resource_pool.CreateAzureHostingConnectionPool: Refreshing state... [id=d10c0943-ec66-4435-982f-1ec88e6d83d9] 
								time_sleep.wait_30_seconds1: Refreshing state... [id=2024-10-23T17:44:59Z] 
								citrix_machine_catalog.CreateAzureImgDefMCSCatalog: Refreshing state... [id=c7364b7a-c045-41cc-af81-7a3da2c5d84f] 
								data.azurerm_shared_image_gallery.GetAzureSIG: Reading... 
								data.azurerm_shared_image_gallery.GetAzureSIG: Read complete after 0s [id=/subscriptions/893a4812-27ad-4135-a95a-6e1d6e0bd793/resourceGroups/tmm_gerhard_westeurope/providers/Microsoft.Compute/galleries/TMM_GK_TF_SIG] 
								data.azurerm_shared_image.GetAzureSIGDefinition: Reading... 
								data.azurerm_shared_image.GetAzureSIGDefinition: Read complete after 0s [id=/subscriptions/893a4812-27ad-4135-a95a-6e1d6e0bd793/resourceGroups/tmm_gerhard_westeurope/providers/Microsoft.Compute/galleries/TMM_GK_TF_SIG/images/TMM-GK-CC-W11-SS] 
								data.azurerm_shared_image_version.GetAzureSIGDefinitionVersion: Reading... 
								data.azurerm_shared_image_version.GetAzureSIGDefinitionVersion: Read complete after 0s [id=/subscriptions/893a4812-27ad-4135-a95a-6e1d6e0bd793/resourceGroups/tmm_gerhard_westeurope/providers/Microsoft.Compute/galleries/TMM_GK_TF_SIG/images/TMM-GK-CC-W11-SS/versions/1.0.0] 
								citrix_machine_catalog.CreateAzureMCSCatalog: Refreshing state... [id=50f78ac2-906f-490f-b25f-2bb921df7041] 
								time_sleep.wait_30_seconds_2: Refreshing state... [id=2024-10-23T18:24:28Z] 
								citrix_delivery_group.CreateDG: Refreshing state... [id=3dee598d-3a66-4a43-b3ce-1d989e75a3c4] 
								 
								Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: 
								  ~ update in-place 
								 
								Terraform will perform the following actions: 
								 
								  # citrix_delivery_group.CreateDG will be updated in-place 
								  ~ resource "citrix_delivery_group" "CreateDG" { 
								      ~ autoscale_settings          = { 
								          ~ autoscale_enabled                                     = false -&gt; true 
								            # (22 unchanged attributes hidden) 
								        } 
								        id                          = "3dee598d-3a66-4a43-b3ce-1d989e75a3c4" 
								      ~ inherited_scopes            = [] -&gt; (known after apply) 
								        name                        = "DG-TMM-GK-TF-Azure" 
								      + tenants                     = (known after apply) 
								      ~ total_machines              = 1 -&gt; (known after apply) 
								        # (10 unchanged attributes hidden) 
								    } 
								 
								Plan: 0 to add, 1 to change, 0 to destroy. 
								 
								───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 
								 
								PS C:\_TERRAFORM\_CCOnAzureV2\CCOnAzureV2-DeployCCEntities&gt;
							 
						
					
				
			
		

		
			Terraform found the change and notifies accordingly:
		 

		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
			
				
					
						
							  # citrix_delivery_group.CreateDG will be updated in-place 
							  ~ resource "citrix_delivery_group" "CreateDG" { 
							      ~ autoscale_settings          = { 
							          ~ autoscale_enabled                                     = false -&gt; true 
							 
						
					
				
			
		

		
			If you now run terraform apply, these changes will be remitted, and your infrastructure will match the initial State again:
		 

		
			
				Caution:
			 

			
				Terraform will ask for your confirmation before remitting. 
				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 
				 
				Be aware that the remittance can severely affect the currently running infrastructure, as the changes will take effect immediately. 
				 
			 
		

		
			 
		 

		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
			
				
					
						
							
								PS C:\_TERRAFORM\_CCOnAzureV2\CCOnAzureV2-DeployCCEntities&gt; terraform apply 
								data.citrix_zone.GetTFAzureZoneID: Reading... 
								data.citrix_zone.GetTFAzureZoneID: Read complete after 0s [id=12f9de5d-fa7f-4739-aaa2-8338daabe878] 
								... 
								citrix_delivery_group.CreateDG: Refreshing state... [id=3dee598d-3a66-4a43-b3ce-1d989e75a3c4] 
								 
								Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: 
								  ~ update in-place 
								 
								Terraform will perform the following actions: 
								 
								  # citrix_delivery_group.CreateDG will be updated in-place 
								  ~ resource "citrix_delivery_group" "CreateDG" { 
								      ~ autoscale_settings          = { 
								          ~ autoscale_enabled                                     = false -&gt; true 
								            # (22 unchanged attributes hidden) 
								        } 
								        id                          = "3dee598d-3a66-4a43-b3ce-1d989e75a3c4" 
								      ~ inherited_scopes            = [] -&gt; (known after apply) 
								        name                        = "DG-TMM-GK-TF-Azure" 
								      + tenants                     = (known after apply) 
								      ~ total_machines              = 1 -&gt; (known after apply) 
								        # (10 unchanged attributes hidden) 
								    } 
								 
								Plan: 0 to add, 1 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_delivery_group.CreateDG: Modifying... [id=3dee598d-3a66-4a43-b3ce-1d989e75a3c4] 
								citrix_delivery_group.CreateDG: Modifications complete after 9s [id=3dee598d-3a66-4a43-b3ce-1d989e75a3c4] 
								 
								Apply complete! Resources: 0 added, 1 changed, 0 destroyed. 
								PS C:\_TERRAFORM\_CCOnAzureV2\CCOnAzureV2-DeployCCEntities&gt;
							 
						
					
				
			
		

		
			Terraform has successfully remitted the change to the original State. 
			Running terraform plan shows that the Infrastructure matches the original State again:
		 

		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
			
				
					
						
							
								PS C:\_TERRAFORM\_CCOnAzureV2\CCOnAzureV2-DeployCCEntities&gt; terraform plan 
								data.citrix_zone.GetTFAzureZoneID: Reading... 
								data.citrix_zone.GetTFAzureZoneID: Read complete after 0s [id=12f9de5d-fa7f-4739-aaa2-8338daabe878]
							 

							
								...
							 

							
								citrix_delivery_group.CreateDG: Refreshing state... [id=3dee598d-3a66-4a43-b3ce-1d989e75a3c4] 
								 
								No changes. Your infrastructure matches the configuration. 
								 
								Terraform has compared your real infrastructure against your configuration and found no differences, so no changes are needed. 
								PS C:\_TERRAFORM\_CCOnAzureV2\CCOnAzureV2-DeployCCEntities&gt;
							 
						
					
				
			
		

		
			Citrix WebStudio also reflects the re-enablement of AutoScale: 
			 
			 
		 

		
			Example 2: Policy Changes
		

		
			Another important aspect of keeping an Infrastructure in a desired state are the Policies. 
			Changes in Policies can have various negative consequences. In this example, IT does not want Native Printer Drivers installed. You can leverage Terraform to detect these changes as well: 
		 

		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
			
				
					
						
							
								PS C:\_TERRAFORM\_CCOnAzureV2\CCOnAzureV2-CreatePoliciesAndScopes&gt; terraform plan 
								data.citrix_admin_scope.GetDefaultMonitorScope: Reading... 
								data.citrix_delivery_group.GetDeliveryGroup: Reading... 
								... 
								citrix_policy_set.CreatePolicyExampleSet: Refreshing state... [id=3658867a-c55a-4a76-996a-03be1c0560a2] 
								 
								Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: 
								  ~ update in-place 
								 
								Terraform will perform the following actions: 
								 
								  # citrix_policy_set.CreatePolicyExampleSet will be updated in-place 
								  ~ resource "citrix_policy_set" "CreatePolicyExampleSet" { 
								      ~ assigned    = true -&gt; (known after apply) 
								        id          = "3658867a-c55a-4a76-996a-03be1c0560a2" 
								        name        = "PolSet-TF-GK-Default" 
								      ~ policies    = [ 
								          ~ { 
								              + access_control_filters      = (known after apply) 
								              + client_ip_filters           = (known after apply) 
								              + client_name_filters         = (known after apply) 
								              + delivery_group_type_filters = (known after apply) 
								                id                          = "dd887432-9b66-489d-a7a2-ea96d979c4f6" 
								                name                        = "Printing" 
								              + ou_filters                  = (known after apply) 
								              ~ policy_settings             = [ 
								                  - { 
								                      - name        = "UniversalPrintDriverUsage" -&gt; null 
								                      - use_default = false -&gt; null 
								                      - value       = "SpecificOnly" -&gt; null 
								                    }, 
								                  + { 
								                      + name        = "UniversalPrintDriverUsage" 
								                      + use_default = false 
								                      + value       = "FallbackToSpecific" 
								                    }, 
								                    # (1 unchanged element hidden) 
								                ] 
								              + tag_filters                 = (known after apply) 
								                # (4 unchanged attributes hidden) 
								            }, 
								          ~ { 
								              + access_control_filters      = (known after apply) 
								              + client_name_filters         = (known after apply) 
								              + delivery_group_type_filters = (known after apply) 
								                id                          = "5243115e-fb99-42e2-9139-31c83dd03bca" 
								                name                        = "HDX Graphics" 
								              + ou_filters                  = (known after apply) 
								              + tag_filters                 = (known after apply) 
								                # (6 unchanged attributes hidden) 
								            }, 
								          ~ { 
								              + access_control_filters      = (known after apply) 
								              + client_name_filters         = (known after apply) 
								              + delivery_group_type_filters = (known after apply) 
								                id                          = "723dc9c9-e3d4-414c-a65b-0137ae0face9" 
								                name                        = "Client Drives" 
								              + ou_filters                  = (known after apply) 
								              + tag_filters                 = (known after apply) 
								                # (6 unchanged attributes hidden) 
								            }, 
								        ] 
								        # (3 unchanged attributes hidden) 
								    } 
								 
								Plan: 0 to add, 1 to change, 0 to destroy. 
								 
								 
								────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 
								 
								 
								PS C:\_TERRAFORM\_CCOnAzureV2\CCOnAzureV2-CreatePoliciesAndScopes&gt;
							 
						
					
				
			
		

		
			You can now run terraform apply, to remit these changes, and your infrastructure matches the initial State again:
		 

		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
			
				
					
						
							
								PS C:\_TERRAFORM\_CCOnAzureV2\CCOnAzureV2-CreatePoliciesAndScopes&gt; terraform apply 
								citrix_admin_scope.CreateMonitorScopeExample: Refreshing state... [id=74d03cc1-1fd1-430d-90b0-fe2a1c8f8d52] 
								... 
								citrix_policy_set.CreatePolicyExampleSet: Refreshing state... [id=3658867a-c55a-4a76-996a-03be1c0560a2] 
								 
								Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: 
								  ~ update in-place 
								 
								Terraform will perform the following actions: 
								 
								  # citrix_policy_set.CreatePolicyExampleSet will be updated in-place 
								  ~ resource "citrix_policy_set" "CreatePolicyExampleSet" { 
								      ~ assigned    = true -&gt; (known after apply) 
								        id          = "3658867a-c55a-4a76-996a-03be1c0560a2" 
								        name        = "PolSet-TF-GK-Default" 
								      ~ policies    = [ 
								          ~ { 
								              + access_control_filters      = (known after apply) 
								              + client_ip_filters           = (known after apply) 
								              + client_name_filters         = (known after apply) 
								              + delivery_group_type_filters = (known after apply) 
								                id                          = "dd887432-9b66-489d-a7a2-ea96d979c4f6" 
								                name                        = "Printing" 
								              + ou_filters                  = (known after apply) 
								              ~ policy_settings             = [ 
								                  - { 
								                      - name        = "UniversalPrintDriverUsage" -&gt; null 
								                      - use_default = false -&gt; null 
								                      - value       = "SpecificOnly" -&gt; null 
								                    }, 
								                  + { 
								                      + name        = "UniversalPrintDriverUsage" 
								                      + use_default = false 
								                      + value       = "FallbackToSpecific" 
								                    }, 
								                    # (1 unchanged element hidden) 
								                ] 
								              + tag_filters                 = (known after apply) 
								                # (4 unchanged attributes hidden) 
								            }, 
								          ~ { 
								              + access_control_filters      = (known after apply) 
								              + client_name_filters         = (known after apply) 
								              + delivery_group_type_filters = (known after apply) 
								                id                          = "5243115e-fb99-42e2-9139-31c83dd03bca" 
								                name                        = "HDX Graphics" 
								              + ou_filters                  = (known after apply) 
								              + tag_filters                 = (known after apply) 
								                # (6 unchanged attributes hidden) 
								            }, 
								          ~ { 
								              + access_control_filters      = (known after apply) 
								              + client_name_filters         = (known after apply) 
								              + delivery_group_type_filters = (known after apply) 
								                id                          = "723dc9c9-e3d4-414c-a65b-0137ae0face9" 
								                name                        = "Client Drives" 
								              + ou_filters                  = (known after apply) 
								              + tag_filters                 = (known after apply) 
								                # (6 unchanged attributes hidden) 
								            }, 
								        ] 
								        # (3 unchanged attributes hidden) 
								    } 
								 
								Plan: 0 to add, 1 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_policy_set.CreatePolicyExampleSet: Modifying... [id=3658867a-c55a-4a76-996a-03be1c0560a2] 
								citrix_policy_set.CreatePolicyExampleSet: Still modifying... [id=3658867a-c55a-4a76-996a-03be1c0560a2, 10s elapsed] 
								citrix_policy_set.CreatePolicyExampleSet: Still modifying... [id=3658867a-c55a-4a76-996a-03be1c0560a2, 20s elapsed] 
								citrix_policy_set.CreatePolicyExampleSet: Still modifying... [id=3658867a-c55a-4a76-996a-03be1c0560a2, 30s elapsed] 
								citrix_policy_set.CreatePolicyExampleSet: Modifications complete after 37s [id=3658867a-c55a-4a76-996a-03be1c0560a2] 
								 
								Apply complete! Resources: 0 added, 1 changed, 0 destroyed. 
								 
								PS C:\_TERRAFORM\_CCOnAzureV2\CCOnAzureV2-CreatePoliciesAndScopes&gt;
							 
						
					
				
			
		

		
			Terraform has successfully remitted the change to the original State. 
			Running terraform plan shows that the Infrastructure matches the original State again:
		 

		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
			
				
					
						
							
								PS C:\_TERRAFORM\_CCOnAzureV2\CCOnAzureV2-CreatePoliciesAndScopes&gt; terraform plan 
								citrix_admin_scope.CreateMonitorScopeExample: Refreshing state... [id=74d03cc1-1fd1-430d-90b0-fe2a1c8f8d52]... 
								citrix_policy_set.CreatePolicyExampleSet: Refreshing state... [id=3658867a-c55a-4a76-996a-03be1c0560a2] 
								 
								No changes. Your infrastructure matches the configuration. 
								 
								Terraform has compared your real infrastructure against your configuration and found no differences, so no changes are needed. 
								PS C:\_TERRAFORM\_CCOnAzureV2\CCOnAzureV2-CreatePoliciesAndScopes&gt;
							 
						
					
				
			
		

		
			 
		 

		
			Example 3: Automating the Checks using the Config Drift Notification Script
		

		
			Automating the check for Infrastructure changes might be helpful so you do not need to run terraform plan manually. There are various ways to do this, so use your preferred method according to your needs. 
		 

		
			
				Note:
			 

			
				You can automate this process using our Provider Config Drift Notification script on GitHub. 
				 
			 
		

		
			The output of the Config Drift Notification script shows the changes:
		 

		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
			
				
					
						
							 
							PS C:\_TERRAFORM\_CCOnAzureV2\CCOnAzureV2-DeployCCEntities&gt; .\config-drift.ps1 
							Terraform Config Drift Detected: 
							 --------------------------------------------------------------------------------------------------- 
							 
							  # citrix_delivery_group.CreateDG has changed 
							  ~ resource "citrix_delivery_group" "CreateDG" { 
							      ~ autoscale_settings          = { 
							          ~ autoscale_enabled                                     = true -&gt; false 
							            # (22 unchanged attributes hidden) 
							        } 
							        id                          = "3dee598d-3a66-4a43-b3ce-1d989e75a3c4" 
							        name                        = "DG-TMM-GK-TF-Azure" 
							        # (12 unchanged attributes hidden) 
							    } 
							 
							 
							PS C:\_TERRAFORM\_CCOnAzureV2\CCOnAzureV2-DeployCCEntities&gt;
						
					
				
			
		

		
			You can hook onto the output and take further steps, as we will show in the new script shortly. 
			 
		 

		
			Example 4: Using PowerShell to get Details about Changes
		

		
			After creating a repetitive task to check for Infrastructure changes, getting more details about the changes would make sense. 
			We assume you run a daily task to check for changes.
		 

		
			
				Note:
			 

			
				If you have configured Configuration Logging, you could get more insights about an unwanted change. Check the Configuration Logging events in Citrix WebStudio for details or use PowerShell or REST-API calls to retrieve them. 
				 
			 
		

		
			You must install the Citrix Remote PowerShell SDK to retrieve the Configuration Logging events.
		 

		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
			
				
					
						
							
								Add-PSSnapin Citrix.* 
								$Date = Get-Date -Format "yyyy-MM-dd" 
								$OutDirFull = "C:\_TEMP\ConfigDrift\" + $Date 
								$StartRange = $Date + " 00:00" 
								$EndRange = $Date + " 23:59" 
								Set-XDCredentials -StoreAs TACG -ProfileType CloudApi -CustomerId xXxXxXxXxXxXx -APIKey yYyYyYyYyYyYyYy -SecretKey zZzZzZzZzZzZzZz 
								Get-XDAuthentication -ProfileName TACG 
								New-Item -Path $OutDirFull -ItemType "directory" -Force 
								Export-LogReportHtml -OutputDirectory $OutDirFull -StartDateRange $StartRange -EndDateRange $EndRange
							 
						
					
				
			
		

		
			The HTML-based reports contain all changes to your Infrastructure on the particular day (example):
		 

		
			
				
					
						
							18.11.2024 10:30:41
						 
					
					
						
							tmm-xxxxx@az.the-austrian-citrix-guy.at
						 
					
					
						
							Edit Delivery Group 'DG-TMM-GK-AZ-W11-SS'
						 
					
					
						
							Erfolgreich
						 
					
					
						
							172.29.77.84
						 
					
					
						
							Details
						 
					
				
				
					
						
							18.11.2024 10:45:35
						 
					
					
						
							tmm-xxxxx@az.the-austrian-citrix-guy.at
						 
					
					
						
							Edit Delivery Group 'DG-TMM-GK-TF-Azure'
						 
					
					
						
							Erfolgreich
						 
					
					
						
							172.29.77.84
						 
					
					
						
							Details
						 
					
				
				
					
						
							18.11.2024 11:08:20
						 
					
					
						
							tmm-xxxxx@az.the-austrian-citrix-guy.at
						 
					
					
						
							Edit Delivery Group 'DG-TMM-GK-TF-Azure'
						 
					
					
						
							Erfolgreich
						 
					
					
						
							172.29.77.84
						 
					
					
						
							Details
						 
					
				
				
					
						
							18.11.2024 11:10:02
						 
					
					
						
							tmm-xxxxx@az.the-austrian-citrix-guy.at
						 
					
					
						
							Edit Delivery Group 'DG-TMM-GK-TF-Azure'
						 
					
					
						
							Erfolgreich
						 
					
					
						
							172.29.77.84
						 
					
					
						
							Details
						 
					
				
				
					
						
							18.11.2024 11:31:39
						 
					
					
						
							tmm-xxxxx@az.the-austrian-citrix-guy.at
						 
					
					
						
							Edit Delivery Group 'DG-TMM-GK-TF-Azure'
						 
					
					
						
							Erfolgreich
						 
					
					
						
							172.29.77.84
						 
					
					
						
							Details
						 
					
				
				
					
						
							18.11.2024 11:33:35
						 
					
					
						
							tmm-xxxxx@az.the-austrian-citrix-guy.at
						 
					
					
						
							Edit Delivery Group 'DG-TMM-GK-TF-Azure'
						 
					
					
						
							Erfolgreich
						 
					
					
						
							172.29.77.84
						 
					
					
						
							Details
						 
					
				
				
					
						
							18.11.2024 11:33:36
						 
					
					
						
							tmm-xxxxx@az.the-austrian-citrix-guy.at
						 
					
					
						
							Edit 'TMM-GK-TF-RebootSchedule'
						 
					
					
						
							Erfolgreich
						 
					
					
						
							172.29.77.84
						 
					
					
						
							Details
						 
					
				
				
					
						
							18.11.2024 11:38:37
						 
					
					
						
							tmm-xxxxx@az.the-austrian-citrix-guy.at
						 
					
					
						
							Edit Delivery Group 'DG-TMM-GK-TF-Azure'
						 
					
					
						
							Erfolgreich
						 
					
					
						
							172.29.77.84
						 
					
					
						
							Details
						 
					
				
				
					
						
							18.11.2024 11:38:37
						 
					
					
						
							tmm-xxxxx@az.the-austrian-citrix-guy.at
						 
					
					
						
							Remove Power Time Scheme 'DG-TMM-GK-TF-Azure_AS-TMM-GK-TF-Azure-Weekday'
						 
					
					
						
							Erfolgreich
						 
					
					
						
							172.29.77.84
						 
					
					
						
							Details
						 
					
				
				
					
						
							18.11.2024 11:38:37
						 
					
					
						
							tmm-xxxxx@az.the-austrian-citrix-guy.at
						 
					
					
						
							Create Power Time Scheme 'DG-TMM-GK-TF-Azure_AS-TMM-GK-TF-Azure-Weekday'
						 
					
					
						
							Erfolgreich
						 
					
					
						
							172.29.77.84
						 
					
					
						
							Details
						 
					
				
				
					
						
							18.11.2024 11:38:38
						 
					
					
						
							tmm-xxxxx@az.the-austrian-citrix-guy.at
						 
					
					
						
							Remove 'TMM-GK-TF-RebootSchedule'
						 
					
					
						
							Erfolgreich
						 
					
					
						
							172.29.77.84
						 
					
					
						
							Details
						 
					
				
				
					
						
							18.11.2024 11:38:38
						 
					
					
						
							tmm-xxxxx@az.the-austrian-citrix-guy.at
						 
					
					
						
							Create 'TMM-GK-TF-RebootSchedule'
						 
					
					
						
							Erfolgreich
						 
					
					
						
							172.29.77.84
						 
					
					
						
							Details
						 
					
				
				
					
						
							18.11.2024 11:38:56
						 
					
					
						
							tmm-xxxxx@az.the-austrian-citrix-guy.at
						 
					
					
						
							Edit Delivery Group 'DG-TMM-GK-TF-Azure'
						 
					
					
						
							Erfolgreich
						 
					
					
						
							172.29.77.84
						 
					
					
						
							Details
						 
					
				
				
					
						
							18.11.2024 11:39:29
						 
					
					
						
							tmm-xxxxx@az.the-austrian-citrix-guy.at
						 
					
					
						
							Edit Delivery Group 'DG-TMM-GK-TF-Azure'
						 
					
					
						
							Erfolgreich
						 
					
					
						
							172.29.77.84
						 
					
					
						
							Details
						 
					
				
				
					
						
							18.11.2024 11:39:29
						 
					
					
						
							tmm-xxxxx@az.the-austrian-citrix-guy.at
						 
					
					
						
							Remove Power Time Scheme 'DG-TMM-GK-TF-Azure_AS-TMM-GK-TF-Azure-Weekday'
						 
					
					
						
							Erfolgreich
						 
					
					
						
							172.29.77.84
						 
					
					
						
							Details
						 
					
				
				
					
						
							18.11.2024 11:39:29
						 
					
					
						
							tmm-xxxxx@az.the-austrian-citrix-guy.at
						 
					
					
						
							Create Power Time Scheme 'DG-TMM-GK-TF-Azure_AS-TMM-GK-TF-Azure-Weekday'
						 
					
					
						
							Erfolgreich
						 
					
					
						
							172.29.77.84
						 
					
					
						
							Details
						 
					
				
				
					
						
							18.11.2024 11:39:30
						 
					
					
						
							tmm-xxxxx@az.the-austrian-citrix-guy.at
						 
					
					
						
							Remove 'TMM-GK-TF-RebootSchedule'
						 
					
					
						
							Erfolgreich
						 
					
					
						
							172.29.77.84
						 
					
					
						
							Details
						 
					
				
				
					
						
							18.11.2024 11:39:30
						 
					
					
						
							tmm-xxxxx@az.the-austrian-citrix-guy.at
						 
					
					
						
							Create 'TMM-GK-TF-RebootSchedule'
						 
					
					
						
							Erfolgreich
						 
					
					
						
							172.29.77.84
						 
					
					
						
							Details
						 
					
				
				
					
						
							18.11.2024 11:53:45
						 
					
					
						
							tmm-xxxxx@az.the-austrian-citrix-guy.at
						 
					
					
						
							Edit Delivery Group 'DG-TMM-GK-TF-Azure'
						 
					
					
						
							Erfolgreich
						 
					
					
						
							172.29.77.84
						 
					
					
						
							Details
						 
					
				
				
					
						
							18.11.2024 11:54:16
						 
					
					
						
							tmm-xxxxx@az.the-austrian-citrix-guy.at
						 
					
					
						
							Edit Delivery Group 'DG-TMM-GK-TF-Azure'
						 
					
					
						
							Erfolgreich
						 
					
					
						
							172.29.77.84
						 
					
					
						
							Details
						 
					
				
				
					
						
							18.11.2024 12:05:46
						 
					
					
						
							tmm-xxxxx@az.the-austrian-citrix-guy.at
						 
					
					
						
							Edit Delivery Group 'DG-TMM-GK-TF-Azure'
						 
					
					
						
							Erfolgreich
						 
					
					
						
							172.29.77.84
						 
					
					
						
							Details
						 
					
				
			
		

		
			You can now align the events in the report with the changes detected by Terraform to get more information. 
			 
		 

		
			Example 5: Using Azure Automation to periodically check for Changes and Receiving Notifications
		

		
			The most convenient and recommended way is to automate the checks and the remediation using a script and, in this case, Azure Automation. 
			We are currently combining all functionalities of the 3 previously mentioned examples in one new script, which will run on Azure Automation. 
			We will update this section as soon as all tests are completed. Stay tuned.
		 

		
			 
		 

		
			Altering an Infrastructure after the Initial Deployment
		

		
			You can leverage Terraform to check your Infrastructure for alterations/changes that might not be wanted or accidentally happened and alter it as new needs arise programmatically. 
			You can alter any Terraform-supporting entity in all layers: the Compute Layer, the Resource Layer, the Control Layer, the Access Layer, and the User Layer. 
			Therefore, you can leverage different providers in the same code – like this example from a current project: 
			
		 

		
			   
		 

		
			Example 1: User Layer: Reconfiguring the Citrix Workspace App/Enterprise Browser
		

		
			In this example, we will reconfigure the Citrix Workspace App by altering the settings used by the Citrix Global App Configuration service. You can access the current settings using Citrix WebStudio: 
			
		 

		
			If you click Configure, you can choose the Endpoint app – Citrix Workspace App or Citrix Enterprise Browser. All corresponding settings of the respective application can be shown by clicking on View configured changes: 
			
		 

		
			Leverage Terraform to alter the settings – for example, showing the Connection Center option in the Advance Preferences of the Workspace App – this setting is currently set to hide it:
		 

		
			
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}			
			
				
					
						
							
								
									# Configuring Citrix Cloud Global App Configuration Service
								 

								
									## Create Settings
								 

								
									resource "citrix_gac_settings" "SetWorkspaceAppConfiguration" {
								 

								
									    service_url = "${var.GACS-ServiceURL}"
								 

								
									    name        = "${var.GACS-Settings-Name}"
								 

								
									    description = "${var.GACS-Settings-Description}"
								 

								
									 
								 

								
									    app_settings = {
								 

								
									        windows = [
								 

								
									            {
								 

								
									                user_override = false,
								 

								
									                category = "Root",
								 

								
									                settings = [
								 

								
									                    {
								 

								
									                        name = "Hide advanced preferences",
								 

								
									                        value_string = "true"
								 

								
									                    },
								 

								
									                   {
								 

								
									                        name = "Hide connection center",
								 

								
									                        value_string = "true"
								 

								
									                    }   
								 

								
									                ]
								 

								
									            },
								 

								
									...
								 
							
						
					
				
			
		

		
			To change this setting, comment the corresponding code segment and apply the Terraform code using terraform apply.
		 

		
			
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}			
			
				
					
						
							
								
									# Configuring Citrix Cloud Global App Configuration Service
								 

								
									## Create Settings
								 

								
									resource "citrix_gac_settings" "SetWorkspaceAppConfiguration" {
								 

								
									    service_url = "${var.GACS-ServiceURL}"
								 

								
									    name        = "${var.GACS-Settings-Name}"
								 

								
									    description = "${var.GACS-Settings-Description}"
								 

								
									 
								 

								
									    app_settings = {
								 

								
									        windows = [
								 

								
									            {
								 

								
									                user_override = false,
								 

								
									                category = "Root",
								 

								
									                settings = [
								 

								
									                    {
								 

								
									                        name = "Hide advanced preferences",
								 

								
									                        value_string = "true"
								 

								
									                    },
								 

								
									                  /*  {
								 

								
									                        name = "Hide connection center",
								 

								
									                        value_string = "true"
								 

								
									                    }    */
								 

								
									                ]
								 
							
						
					
				
			
		

		
			Terraform changes the setting as intended:
		 

		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
			
				
					
						
							
								PS C:\_TERRAFORM\_TMM-CC-GACS&gt; terraform apply 
								citrix_gac_settings.test_settings_configuration: Refreshing state... [name=GACS-TMM-GK-TF-Settings] 
								 
								Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: 
								  ~ update in-place 
								 
								Terraform will perform the following actions: 
								 
								  # citrix_gac_settings.test_settings_configuration will be updated in-place 
								  ~ resource "citrix_gac_settings" "test_settings_configuration" { 
								      ~ app_settings       = { 
								          ~ windows = [ 
								              -               - { 
								                  - category      = "root" -&gt; null 
								                  - settings      = [ 
								                      - { 
								                          - name         = "hide advanced preferences" -&gt; null 
								                          - value_string = "true" -&gt; null 
								                        }, 
								                      - { 
								                          - name         = "hide connection center" -&gt; null 
								                          - value_string = "true" -&gt; null 
								                        }, 
								                    ] -&gt; null 
								                  - user_override = false -&gt; null 
								                }, 
								              + { 
								                  + category      = "Root" 
								                  + settings      = [ 
								                      + { 
								                          + name         = "Hide advanced preferences" 
								                          + value_string = "true" 
								                        }, 
								                    ] 
								                  + user_override = false 
								                }, 
								                # (1 unchanged element hidden) 
								            ] 
								            # (2 unchanged attributes hidden) 
								        } 
								        name               = "GACS-TMM-GK-TF-Settings" 
								        # (3 unchanged attributes hidden) 
								    } 
								 
								Plan: 0 to add, 1 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_gac_settings.test_settings_configuration: Modifying... [name=GACS-TMM-GK-TF-Settings] 
								citrix_gac_settings.test_settings_configuration: Modifications complete after 4s [name=GACS-TMM-GK-TF-Settings]
							 

							
								  
								Apply complete! Resources: 0 added, 1 changed, 0 destroyed. 
								PS C:\_TERRAFORM\_TMM-CC-GACS&gt;
							 
						
					
				
			
		

		
			The settings in WebStudio reflect the change: 
			
		 

		
			The changed configuration item is no longer shown. Terraform has successfully changed a Workspace App setting. 
			 
		 

		
			Example 2: Platform Layer: Updating the Azure Machine Types
		

		
			In this example, we will update the Machine Types of the Cloud Connector Machines. 
			
		 

		
			We have defined the Machine Type in a Terraform Variable:
		 

		
			
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}			
			
				
					
						
							
								
									... 
									   "TACG-TMM-CCX-VM-Size":"Standard_D2s_v5",
								 
								...
							
						
					
				
			
		

		
			We change this variable´s value to a new value:
		 

		
			
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}			
			
				
					
						
							
								
									... 
									   "TACG-TMM-CCX-VM-Size":"Standard_D4s_v5",
								 
								...
							
						
					
				
			
		

		
			terraform plan shows that it can update the VMs in place and change the size from Standard_D2s_v5 to Standard_D4s_v5 – nothing will be destroyed or re-created:
		 

		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
			
				
					
						
							
								PS C:\_TERRAFORM\_CCOnAzureV2\CCOnAzureV2-ChangeVMSize&gt; terraform plan 
								data.azurerm_virtual_network.TACG-TMM-VNet: Reading... 
								data.azurerm_network_security_group.TACG-TMM-NSG: Reading... 
								data.azurerm_network_interface.TACG-TMM-TF-CC1-NIC: Reading... 
								data.azurerm_network_interface.TACG-TMM-TF-CC2-NIC: Reading... 
								data.azurerm_network_interface.TACG-TMM-TF-CC1-NIC: Read complete after 0s [id=/subscriptions/893a4812-27ad-4135-a95a-6e1d6e0bd793/resourceGroups/tmm_gerhard_westeurope/providers/Microsoft.Network/networkInterfaces/TACG-TMM-TF-CC1-NIC] 
								data.azurerm_network_security_group.TACG-TMM-NSG: Read complete after 0s [id=/subscriptions/893a4812-27ad-4135-a95a-6e1d6e0bd793/resourceGroups/tmm_gerhard_westeurope/providers/Microsoft.Network/networkSecurityGroups/TMM-GK-CC1-nsg] 
								data.azurerm_network_interface.TACG-TMM-TF-CC2-NIC: Read complete after 0s [id=/subscriptions/893a4812-27ad-4135-a95a-6e1d6e0bd793/resourceGroups/tmm_gerhard_westeurope/providers/Microsoft.Network/networkInterfaces/TACG-TMM-TF-CC2-NIC] 
								azurerm_windows_virtual_machine.TACG-TMM-CC1: Refreshing state... [id=/subscriptions/893a4812-27ad-4135-a95a-6e1d6e0bd793/resourceGroups/tmm_gerhard_westeurope/providers/Microsoft.Compute/virtualMachines/TACG-TMM-TF-CC1] 
								azurerm_windows_virtual_machine.TACG-TMM-CC2: Refreshing state... [id=/subscriptions/893a4812-27ad-4135-a95a-6e1d6e0bd793/resourceGroups/tmm_gerhard_westeurope/providers/Microsoft.Compute/virtualMachines/TACG-TMM-TF-CC2] 
								data.azurerm_virtual_network.TACG-TMM-VNet: Read complete after 0s [id=/subscriptions/893a4812-27ad-4135-a95a-6e1d6e0bd793/resourceGroups/tmm_gerhard_westeurope/providers/Microsoft.Network/virtualNetworks/TMM-GK-WestEurope] 
								data.azurerm_subnet.TACG-TMM-Subnet: Reading... 
								data.azurerm_subnet.TACG-TMM-Subnet: Read complete after 0s [id=/subscriptions/893a4812-27ad-4135-a95a-6e1d6e0bd793/resourceGroups/tmm_gerhard_westeurope/providers/Microsoft.Network/virtualNetworks/TMM-GK-WestEurope/subnets/default] 
								 
								Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: 
								  ~ update in-place 
								 
								Terraform will perform the following actions: 
								 
								  # azurerm_windows_virtual_machine.TACG-TMM-CC1 will be updated in-place 
								  ~ resource "azurerm_windows_virtual_machine" "TACG-TMM-CC1" { 
								        id                                                     = "/subscriptions/893a4812-27ad-4135-a95a-6e1d6e0bd793/resourceGroups/tmm_gerhard_westeurope/providers/Microsoft.Compute/virtualMachines/TACG-TMM-TF-CC1" 
								        name                                                   = "TACG-TMM-TF-CC1" 
								      ~ size                                                   = "Standard_D2s_v5" -&gt; "Standard_D4s_v5" 
								        tags                                                   = { 
								            "Environment" = "TMM-Gerhard" 
								        } 
								        # (41 unchanged attributes hidden) 
								 
								        # (2 unchanged blocks hidden) 
								    } 
								 
								  # azurerm_windows_virtual_machine.TACG-TMM-CC2 will be updated in-place 
								  ~ resource "azurerm_windows_virtual_machine" "TACG-TMM-CC2" { 
								        id                                                     = "/subscriptions/893a4812-27ad-4135-a95a-6e1d6e0bd793/resourceGroups/tmm_gerhard_westeurope/providers/Microsoft.Compute/virtualMachines/TACG-TMM-TF-CC2" 
								        name                                                   = "TACG-TMM-TF-CC2" 
								      ~ size                                                   = "Standard_D2s_v5" -&gt; "Standard_D4s_v5" 
								        tags                                                   = { 
								            "Environment" = "TMM-Gerhard" 
								        } 
								        # (41 unchanged attributes hidden) 
								 
								        # (2 unchanged blocks hidden) 
								    } 
								 
								Plan: 0 to add, 2 to change, 0 to destroy. 
								 
								────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 
								 
								PS C:\_TERRAFORM\_CCOnAzureV2\CCOnAzureV2-ChangeVMSize&gt;
							 
						
					
				
			
		

		
			terraform apply will change the VM size in-place:
		 

		
			 
		 

		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
			
				
					
						
							
								Terraform will perform the following actions: 
								 
								  # azurerm_windows_virtual_machine.TACG-TMM-CC1 will be updated in-place 
								  ~ resource "azurerm_windows_virtual_machine" "TACG-TMM-CC1" { 
								        id                                                     = "/subscriptions/893a4812-27ad-4135-a95a-6e1d6e0bd793/resourceGroups/tmm_gerhard_westeurope/providers/Microsoft.Compute/virtualMachines/TACG-TMM-TF-CC1" 
								        name                                                   = "TACG-TMM-TF-CC1" 
								      ~ size                                                   = "Standard_D2s_v5" -&gt; "Standard_D4s_v5" 
								        tags                                                   = { 
								            "Environment" = "TMM-Gerhard" 
								        } 
								        # (41 unchanged attributes hidden) 
								 
								        # (2 unchanged blocks hidden) 
								    } 
								 
								  # azurerm_windows_virtual_machine.TACG-TMM-CC2 will be updated in-place 
								  ~ resource "azurerm_windows_virtual_machine" "TACG-TMM-CC2" { 
								        id                                                     = "/subscriptions/893a4812-27ad-4135-a95a-6e1d6e0bd793/resourceGroups/tmm_gerhard_westeurope/providers/Microsoft.Compute/virtualMachines/TACG-TMM-TF-CC2" 
								        name                                                   = "TACG-TMM-TF-CC2" 
								      ~ size                                                   = "Standard_D2s_v5" -&gt; "Standard_D4s_v5" 
								        tags                                                   = { 
								            "Environment" = "TMM-Gerhard" 
								        } 
								        # (41 unchanged attributes hidden) 
								 
								        # (2 unchanged blocks hidden) 
								    } 
								 
								Plan: 0 to add, 2 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 
								 
								azurerm_windows_virtual_machine.TACG-TMM-CC2: Modifying... [id=/subscriptions/893a4812-27ad-4135-a95a-6e1d6e0bd793/resourceGroups/tmm_gerhard_westeurope/providers/Microsoft.Compute/virtualMachines/TACG-TMM-TF-CC2] 
								azurerm_windows_virtual_machine.TACG-TMM-CC1: Modifying... [id=/subscriptions/893a4812-27ad-4135-a95a-6e1d6e0bd793/resourceGroups/tmm_gerhard_westeurope/providers/Microsoft.Compute/virtualMachines/TACG-TMM-TF-CC1] 
								azurerm_windows_virtual_machine.TACG-TMM-CC1: Still modifying... [id=/subscriptions/893a4812-27ad-4135-a95a-...ompute/virtualMachines/TACG-TMM-TF-CC1, 10s elapsed] 
								azurerm_windows_virtual_machine.TACG-TMM-CC2: Still modifying... [id=/subscriptions/893a4812-27ad-4135-a95a-...ompute/virtualMachines/TACG-TMM-TF-CC2, 10s elapsed] 
								azurerm_windows_virtual_machine.TACG-TMM-CC1: Modifications complete after 12s [id=/subscriptions/893a4812-27ad-4135-a95a-6e1d6e0bd793/resourceGroups/tmm_gerhard_westeurope/providers/Microsoft.Compute/virtualMachines/TACG-TMM-TF-CC1] 
								azurerm_windows_virtual_machine.TACG-TMM-CC2: Modifications complete after 12s [id=/subscriptions/893a4812-27ad-4135-a95a-6e1d6e0bd793/resourceGroups/tmm_gerhard_westeurope/providers/Microsoft.Compute/virtualMachines/TACG-TMM-TF-CC2] 
								 
								Apply complete! Resources: 0 added, 2 changed, 0 destroyed. 
								 
								PS C:\_TERRAFORM\_CCOnAzureV2\CCOnAzureV2-ChangeVMSize&gt;
							 
						
					
				
			
		

		
			Terraform has successfully changed the VM size. 
			 
			 
		 

		
			Example 3: Resource Layer: Updating a Machine Catalog – Deploy a New Master Image Version
		

		
			In this example, we will update a Machine Catalog and deploy a new Master Image version. 
			You can access the current settings using Citrix WebStudio: 
			
		 

		
			The current Master Image version is W11-BaseImageDefinition-1-baseDisk-3w0te. 
			This is also reflected in the initially deployed Terraform code:
		 

		
			
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}			
			
				
					
						
							
								
									#### Creating the Machine Catalog based on Image Definitions and Image Versions
								 

								
									resource "citrix_machine_catalog" "CreateAzureImgDefMCSCatalog" {
								 

								
									  depends_on            = [ time_sleep.wait_30_seconds1 ]
								 

								
									    name                        = "${var.CC-Azure-MCImgDef-Name}"
								 

								
									    description                 = "${var.CC-Azure-MCImgDef-Description}"
								 

								
									    allocation_type             = "${var.CC-Azure-MCImgDef-AllocationType}"
								 

								
									    session_support             = "${var.CC-Azure-MCImgDef-SessionType}"
								 

								
									    provisioning_type           = "MCS"
								 

								
									    #zone                = data.local_file.LoadZoneID.content
								 

								
									    zone                = data.citrix_zone.GetTFAzureZoneID.id
								 

								
									    provisioning_scheme         =   {
								 

								
									        hypervisor               = citrix_azure_hypervisor.CreateAzureHostingConnection.id
								 

								
									        hypervisor_resource_pool = citrix_azure_hypervisor_resource_pool.CreateAzureHostingConnectionPool.id
								 

								
									        identity_type            = "${var.CC-Azure-MCImgDef-IDPType}"
								 

								
									 
								 

								
									        machine_domain_identity  = {
								 

								
									            domain                   = "${var.CC-Azure-MCImgDef-Domain}"
								 

								
									            domain_ou                = "${var.CC-Azure-MCImgDef-DomainOU}"
								 

								
									            service_account          = "${var.CC-Azure-MCImgDef-DomainAdmin-Username-UPN}"
								 

								
									            service_account_password = "${var.CC-Azure-MCImgDef-DomainAdmin-Password}"
								 

								
									        }
								 

								
									 
								 

								
									        azure_machine_config = {
								 

								
									            storage_type             = "Standard_LRS"
								 

								
									            use_managed_disks        = true
								 

								
									            service_offering         = "${var.CC-Azure-MCImgDef-VMSize}"
								 

								
									 
								 

								
									            azure_master_image = {
								 

								
									                resource_group       = "${var.CC-Azure-MCImgDef-MasterImage-RG}"
								 

								
									                master_image         = "${var.CC-Azure-MCImgDef-MasterImage}"
								 

								
									                #master_image         = "${var.CC-Azure-MCImgDef-MasterImage-V3}"
								 

								
									            }
								 

								
									 
								 

								
									            machine_profile = {
								 

								
									              machine_profile_resource_group = "${var.TACG-TMM-ResourceGroup-Name}"
								 

								
									              machine_profile_vm_name        = "${var.CC-Azure-MCImgDef-VMProfileName}"
								 

								
									            }
								 

								
									 
								 

								
									        }
								 

								
									 
								 

								
									       number_of_total_machines                =  1
								 

								
									      
								 

								
									       machine_account_creation_rules          = {
								 

								
									            naming_scheme      = "${var.CC-Azure-MCImgDef-NamingScheme-Name}"
								 

								
									            naming_scheme_type = "${var.CC-Azure-MCImgDef-NamingScheme-Type}"
								 

								
									        }
								 

								
									    }
								 

								
									}
								 
							
						
					
				
			
		

		
			The Terraform code references to a variable which holds the current Master Image name:
		 

		
			
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}			
			
				
					
						
							
								
									master_image         = "${var.CC-Azure-MCImgDef-MasterImage}"
								 
							
						
					
				
			
		

		
			This variable´s value is:
		 

		
			
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}			
			
				
					
						
							
								
									...
								 

								
									"CC-Azure-MCImgDef-MasterImage":"W11-BaseImageDefinition-1-baseDisk-3w0te",
								 

								
									...
								 
							
						
					
				
			
		

		
			We now change the Terraform code to use a new Master Image version:
		 

		
			
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}			
			
				
					
						
							
								
									azure_master_image = {
								 

								
									                resource_group       = "${var.CC-Azure-MCImgDef-MasterImage-RG}"
								 

								
									                master_image         = "${var.CC-Azure-MCImgDef-MasterImage}"
								 

								
									                #master_image         = "${var.CC-Azure-MCImgDef-MasterImage-V3}"
								 

								
									            }
								 
							
						
					
				
			
		

		
			to 
		 

		
			
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}			
			
				
					
						
							
								
									
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}									
									
										
											
												
													
														
															azure_master_image = {
														 

														
															                resource_group       = "${var.CC-Azure-MCImgDef-MasterImage-RG}"
														 

														
															                #master_image         = "${var.CC-Azure-MCImgDef-MasterImage}"
														 

														
															                master_image         = "${var.CC-Azure-MCImgDef-MasterImage-V3}"
														 

														
															            }
														 
													
												
											
										
									
								
							
						
					
				
			
		

		
			The new variable´s value is:
		 

		
			
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}			
			
				
					
						
							
								
									...
								 

								
									"CC-Azure-MCImgDef-MasterImage-V3":" W11-BaseImageDefinition-3-baseDisk-my3dm",
								 

								
									...
								 
							
						
					
				
			
		

		
			Running terraform apply will change the Machine Catalog and apply the new Image version:
		 

		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
			
				
					
						
							
								PS C:\_TERRAFORM\_CCOnAzureV2\CCOnAzureV2-DeployCCEntities&gt; terraform apply 
								data.citrix_zone.GetTFAzureZoneID: Reading... 
								data.citrix_zone.GetTFAzureZoneID: Read complete after 0s [id=12f9de5d-fa7f-4739-aaa2-8338daabe878] 
								citrix_azure_hypervisor.CreateAzureHostingConnection: Refreshing state... [id=903f991f-5eb1-45b7-b85a-8e6b35529a64] 
								time_sleep.wait_30_seconds: Refreshing state... [id=2024-10-23T17:42:53Z] 
								citrix_azure_hypervisor_resource_pool.CreateAzureHostingConnectionPool: Refreshing state... [id=d10c0943-ec66-4435-982f-1ec88e6d83d9] 
								time_sleep.wait_30_seconds1: Refreshing state... [id=2024-10-23T17:44:59Z] 
								citrix_machine_catalog.CreateAzureImgDefMCSCatalog: Refreshing state... [id=c7364b7a-c045-41cc-af81-7a3da2c5d84f] 
								data.azurerm_shared_image_gallery.GetAzureSIG: Reading... 
								data.azurerm_shared_image_gallery.GetAzureSIG: Read complete after 1s [id=/subscriptions/893a4812-27ad-4135-a95a-6e1d6e0bd793/resourceGroups/tmm_gerhard_westeurope/providers/Microsoft.Compute/galleries/TMM_GK_TF_SIG] 
								data.azurerm_shared_image.GetAzureSIGDefinition: Reading... 
								data.azurerm_shared_image.GetAzureSIGDefinition: Read complete after 0s [id=/subscriptions/893a4812-27ad-4135-a95a-6e1d6e0bd793/resourceGroups/tmm_gerhard_westeurope/providers/Microsoft.Compute/galleries/TMM_GK_TF_SIG/images/TMM-GK-CC-W11-SS] 
								data.azurerm_shared_image_version.GetAzureSIGDefinitionVersion: Reading... 
								data.azurerm_shared_image_version.GetAzureSIGDefinitionVersion: Read complete after 0s [id=/subscriptions/893a4812-27ad-4135-a95a-6e1d6e0bd793/resourceGroups/tmm_gerhard_westeurope/providers/Microsoft.Compute/galleries/TMM_GK_TF_SIG/images/TMM-GK-CC-W11-SS/versions/1.0.0] 
								citrix_machine_catalog.CreateAzureMCSCatalog: Refreshing state... [id=50f78ac2-906f-490f-b25f-2bb921df7041] 
								time_sleep.wait_30_seconds_2: Refreshing state... [id=2024-10-23T18:24:28Z] 
								citrix_delivery_group.CreateDG: Refreshing state... [id=3dee598d-3a66-4a43-b3ce-1d989e75a3c4] 
								 
								Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: 
								  ~ update in-place 
								 
								Terraform will perform the following actions: 
								 
								  # citrix_machine_catalog.CreateAzureImgDefMCSCatalog will be updated in-place 
								  ~ resource "citrix_machine_catalog" "CreateAzureImgDefMCSCatalog" { 
								        id                       = "c7364b7a-c045-41cc-af81-7a3da2c5d84f" 
								      ~ inherited_scopes         = [] -&gt; (known after apply) 
								        name                     = "MC-TMM-GK-TF-Azure-ImgDef-Win11" 
								      ~ provisioning_scheme      = { 
								          ~ azure_machine_config           = { 
								              ~ azure_master_image = { 
								                  ~ master_image   = "W11-BaseImageDefinition-1-baseDisk-3w0te" -&gt; "W11-BaseImageDefinition-3-baseDisk-my3dm" 
								                    # (1 unchanged attribute hidden) 
								                } 
								                # (5 unchanged attributes hidden) 
								            } 
								            # (6 unchanged attributes hidden) 
								        } 
								      + tenants                  = (known after apply) 
								        # (8 unchanged attributes hidden) 
								    } 
								 
								Plan: 0 to add, 1 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_machine_catalog.CreateAzureImgDefMCSCatalog: Modifying... [id=c7364b7a-c045-41cc-af81-7a3da2c5d84f] 
								citrix_machine_catalog.CreateAzureImgDefMCSCatalog: Still modifying... [id=c7364b7a-c045-41cc-af81-7a3da2c5d84f, 10s elapsed] 
								citrix_machine_catalog.CreateAzureImgDefMCSCatalog: Still modifying... [id=c7364b7a-c045-41cc-af81-7a3da2c5d84f, 20s elapsed] 
								citrix_machine_catalog.CreateAzureImgDefMCSCatalog: Still modifying... [id=c7364b7a-c045-41cc-af81-7a3da2c5d84f, 30s elapsed] 
								... 
								citrix_machine_catalog.CreateAzureImgDefMCSCatalog: Still modifying... [id=c7364b7a-c045-41cc-af81-7a3da2c5d84f, 7m30s elapsed] 
								citrix_machine_catalog.CreateAzureImgDefMCSCatalog: Still modifying... [id=c7364b7a-c045-41cc-af81-7a3da2c5d84f, 7m40s elapsed] 
								citrix_machine_catalog.CreateAzureImgDefMCSCatalog: Still modifying... [id=c7364b7a-c045-41cc-af81-7a3da2c5d84f, 7m50s elapsed] 
								citrix_machine_catalog.CreateAzureImgDefMCSCatalog: Modifications complete after 7m53s [id=c7364b7a-c045-41cc-af81-7a3da2c5d84f] 
								 
								Apply complete! Resources: 0 added, 1 changed, 0 destroyed. 
								PS C:\_TERRAFORM\_CCOnAzureV2\CCOnAzureV2-DeployCCEntities&gt;
							 
						
					
				
			
		

		
			Terraform has successfully changed the Master Image version. 
			WebStudio reflects this change: 
			 
			 
		 

		
			Adding/Removing Entities to/from an Infrastructure after the initial Deployment
		

		
			At last, you can leverage Terraform to add or remove whole entities if needed. 
			 
		 

		
			Example 1: Adding a Delivery Group
		

		
			We will add a new Delivery Group to our environment in this example. You can access the current Delivery Groups using Citrix WebStudio: 
			
		 

		
			We just need to alter the Terraform code to add the new Delivery Group – currently, it is uncommented, and this needs to be removed:
		 

		
			
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}			
			
				
					
						
							
								
									#### Create the Delivery Group based on the Image Definition-based Machine Catalog
								 

								
									/* resource "citrix_delivery_group" "CreateImgDefDG" {
								 

								
									    name                                    = "${var.CC-Azure-DGImgDef-Name}"
								 

								
									    associated_machine_catalogs             = [
								 

								
									        {
								 

								
									            machine_catalog                 = citrix_machine_catalog.CreateAzureImgDefMCSCatalog.id
								 

								
									            machine_count                   = "${var.CC-Azure-MCImgDef-Machine_Count}"
								 

								
									        }
								 

								
									    ]
								 

								
									    desktops                                = [
								 

								
									        {
								 

								
									            published_name                  = "${var.CC-Azure-DGImgDef-PublishedDesktopName}"
								 

								
									            description                     = "${var.CC-Azure-DGImgDef-Description}"
								 

								
									            restricted_access_users         = {
								 

								
									                                               allow_list = [ "xXxXxXxXxXxXxXxXx", ]
								 

								
									                                              }
								 

								
									            enabled                         = true
								 

								
									        }
								 

								
									        
								 

								
									    ] 
								 

								
									    autoscale_settings                      = {
								 

								
									            autoscale_enabled                                   = true
								 

								
									            disconnect_peak_idle_session_after_seconds          = 60
								 

								
									            disconnect_off_peak_idle_session_after_seconds      = 60
								 

								
									            log_off_off_peak_disconnected_session_after_seconds = 60
								 

								
									            log_off_peak_disconnected_session_after_seconds     = 60
								 

								
									 
								 

								
									            peak_log_off_action                             = "Suspend"
								 

								
									            off_peak_log_off_action                         = "Suspend" 
								 

								
									            peak_disconnect_action                          = "Suspend"
								 

								
									            off_peak_disconnect_action                      = "Suspend" 
								 

								
									 
								 

								
									            power_time_schemes              = [
								 

								
									                                              {
								 

								
									                                               days_of_week = [
								 

								
									                                                              "Monday",
								 

								
									                                                              "Tuesday",
								 

								
									                                                              "Wednesday",
								 

								
									                                                              "Thursday",
								 

								
									                                                              "Friday"
								 

								
									                                                              ]
								 

								
									                name                        = "${var.CC-Azure-DGImgDef-AS-Name}"
								 

								
									                display_name                = "${var.CC-Azure-DGImgDef-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 = [ "xXxXxXxXxXx", ]
								 

								
									                                              }
								 

								
									    reboot_schedules                        = [
								 

								
									                                               {
								 

								
									                                                 name = "TMM-GK-TF-RebootSchedule1"
								 

								
									                                                 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               = "3658867a-c55a-4a76-996a-03be1c0560a2" 
								 

								
									 
								 

								
									}
								 

								
									*/
								 
							
						
					
				
			
		

		
			terraform apply adds the Delivery Group to the Infrastructure:
		 

		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
			
				
					
						
							
								PS C:\_TERRAFORM\_CCOnAzureV2\CCOnAzureV2-DeployCCEntities&gt; terraform apply 
								data.azurerm_shared_image_gallery.GetAzureSIG: Reading... 
								data.azurerm_shared_image_gallery.GetAzureSIG: Read complete after 1s [id=/subscriptions/893a4812-27ad-4135-a95a-6e1d6e0bd793/resourceGroups/tmm_gerhard_westeurope/providers/Microsoft.Compute/galleries/TMM_GK_TF_SIG] 
								... 
								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.CreateImgDefDG will be created 
								  + resource "citrix_delivery_group" "CreateImgDefDG" { 
								      + associated_machine_catalogs = [ 
								          + { 
								              + machine_catalog = "c7364b7a-c045-41cc-af81-7a3da2c5d84f" 
								              + machine_count   = 1 
								            }, 
								        ] 
								      + autoscale_settings          = { 
								          + autoscale_enabled                                     = true 
								          + disconnect_off_peak_idle_session_after_seconds        = 60 
								          + disconnect_peak_idle_session_after_seconds            = 60 
								          + log_off_off_peak_disconnected_session_after_seconds   = 60 
								          + log_off_peak_disconnected_session_after_seconds       = 60 
								          + off_peak_buffer_size_percent                          = 0 
								          + off_peak_disconnect_action                            = "Suspend" 
								          + off_peak_disconnect_timeout_minutes                   = 0 
								          + off_peak_extended_disconnect_action                   = "Nothing" 
								          + off_peak_extended_disconnect_timeout_minutes          = 0 
								          + off_peak_log_off_action                               = "Suspend" 
								          + off_peak_log_off_timeout_minutes                      = 0 
								          + peak_autoscale_assigned_power_on_idle_action          = "Nothing" 
								          + peak_autoscale_assigned_power_on_idle_timeout_minutes = 0 
								          + peak_buffer_size_percent                              = 0 
								          + peak_disconnect_action                                = "Suspend" 
								          + peak_disconnect_timeout_minutes                       = 0 
								          + peak_extended_disconnect_action                       = "Nothing" 
								          + peak_extended_disconnect_timeout_minutes              = 0 
								          + peak_log_off_action                                   = "Suspend" 
								          + peak_log_off_timeout_minutes                          = 0 
								          + power_off_delay_minutes                               = 30 
								          + power_time_schemes                                    = [ 
								              + { 
								                  + days_of_week          = [ 
								                      + "Friday", 
								                      + "Monday", 
								                      + "Thursday", 
								                      + "Tuesday", 
								                      + "Wednesday", 
								                    ] 
								                  + display_name          = "AS-TMM-GK-TF-Azure-ImgDef-Weekday" 
								                  + peak_time_ranges      = [ 
								                      + "09:00-17:00", 
								                    ] 
								                  + pool_size_schedules   = [ 
								                      + { 
								                          + pool_size  = 1 
								                          + time_range = "09:00-17:00" 
								                        }, 
								                    ] 
								                  + pool_using_percentage = false 
								                }, 
								            ] 
								        } 
								      + built_in_scopes             = (known after apply) 
								      + default_desktop_icon        = "1" 
								      + description                 = "" 
								      + desktops                    = [ 
								          + { 
								              + description             = "Terraform-based Delivery Group based on Image Definitions and Image Versions running on Azure" 
								              + enabled                 = true 
								              + published_name          = "DG-TMM-TACG-TF-Azure-ImgDef" 
								              + restricted_access_users = { 
								                  + allow_list = [ 
								                      + "xXxXxXxXxXxXxXxXx", 
								                    ] 
								                } 
								            }, 
								        ] 
								      + id                          = (known after apply) 
								      + inherited_scopes            = (known after apply) 
								      + minimum_functional_level    = "L7_20" 
								      + name                        = "DG-TMM-GK-TF-Azure-ImgDef" 
								      + policy_set_id               = "3658867a-c55a-4a76-996a-03be1c0560a2" 
								      + reboot_schedules            = [ 
								          + { 
								              + days_in_week            = [ 
								                  + "Sunday", 
								                ] 
								              + frequency               = "Weekly" 
								              + frequency_factor        = 1 
								              + ignore_maintenance_mode = true 
								              + name                    = "TMM-GK-TF-RebootSchedule1" 
								              + natural_reboot_schedule = false 
								              + reboot_duration_minutes = 0 
								              + reboot_schedule_enabled = true 
								              + start_date              = "2024-01-01" 
								              + start_time              = "02:00" 
								                # (1 unchanged attribute hidden) 
								            }, 
								        ] 
								      + restricted_access_users     = { 
								          + allow_list = [ 
								              + "xXxXxXxXxXxXxXxXx", 
								            ] 
								        } 
								      + scopes                      = [] 
								      + tenants                     = (known after apply) 
								      + total_machines              = (known after apply) 
								    } 
								 
								Plan: 1 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_delivery_group.CreateImgDefDG: Creating... 
								citrix_delivery_group.CreateImgDefDG: Still creating... [10s elapsed] 
								citrix_delivery_group.CreateImgDefDG: Creation complete after 10s [id=4d9e2b43-1364-44e1-9884-2e7b9a128e2f] 
								 
								Apply complete! Resources: 1 added, 0 changed, 0 destroyed. 
								PS C:\_TERRAFORM\_CCOnAzureV2\CCOnAzureV2-DeployCCEntities&gt;
							 
						
					
				
			

			
				Terraform has successfully added the Delivery Group in 10 seconds - compare this to the time it would take to manually create it using WebStudio. 
				WebStudio reflects the addition: 
				 
				 
			 

			
				Example 2: Removing the Delivery Group
			

			
				In this example, we will remove the just created Delivery Group from our environment to show that Terraform can decommission and completely wipe entities or whole infrastructures.
			 

			
				
					Caution:
				 

				
					You cannot stop the process as soon as you give consent to Terraform's application to destroy the entity/entities. Terraform will decommission whatever it has to. 
					 
				 
			

			
				We only need to uncomment or remove the snippet from the Terraform code to remove the just-created Delivery Group.
			 

			
				We just need to alter the Terraform code to add the new Delivery Group – currently, it is uncommented, and this needs to be removed:
			 

			
				
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}				
				
					
						
							
								
									
										#### Create the Delivery Group based on the Image Definition-based Machine Catalog
									 

									
										/* resource "citrix_delivery_group" "CreateImgDefDG" {
									 

									
										    name                                    = "${var.CC-Azure-DGImgDef-Name}"
									 

									
										    associated_machine_catalogs             = [
									 

									
										        {
									 

									
										            machine_catalog                 = citrix_machine_catalog.CreateAzureImgDefMCSCatalog.id
									 

									
										            machine_count                   = "${var.CC-Azure-MCImgDef-Machine_Count}"
									 

									
										        }
									 

									
										    ]
									 

									
										    desktops                                = [
									 

									
										        {
									 

									
										            published_name                  = "${var.CC-Azure-DGImgDef-PublishedDesktopName}"
									 

									
										            description                     = "${var.CC-Azure-DGImgDef-Description}"
									 

									
										            restricted_access_users         = {
									 

									
										                                               allow_list = [ "xXxXxXxXxXxXxXxXx", ]
									 

									
										                                              }
									 

									
										            enabled                         = true
									 

									
										        }
									 

									
										        
									 

									
										    ] 
									 

									
										    autoscale_settings                      = {
									 

									
										            autoscale_enabled                                   = true
									 

									
										            disconnect_peak_idle_session_after_seconds          = 60
									 

									
										            disconnect_off_peak_idle_session_after_seconds      = 60
									 

									
										            log_off_off_peak_disconnected_session_after_seconds = 60
									 

									
										            log_off_peak_disconnected_session_after_seconds     = 60
									 

									
										 
									 

									
										            peak_log_off_action                             = "Suspend"
									 

									
										            off_peak_log_off_action                         = "Suspend" 
									 

									
										            peak_disconnect_action                          = "Suspend"
									 

									
										            off_peak_disconnect_action                      = "Suspend" 
									 

									
										 
									 

									
										            power_time_schemes              = [
									 

									
										                                              {
									 

									
										                                               days_of_week = [
									 

									
										                                                              "Monday",
									 

									
										                                                              "Tuesday",
									 

									
										                                                              "Wednesday",
									 

									
										                                                              "Thursday",
									 

									
										                                                              "Friday"
									 

									
										                                                              ]
									 

									
										                name                        = "${var.CC-Azure-DGImgDef-AS-Name}"
									 

									
										                display_name                = "${var.CC-Azure-DGImgDef-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 = [ "xXxXxXxXxXx", ]
									 

									
										                                              }
									 

									
										    reboot_schedules                        = [
									 

									
										                                               {
									 

									
										                                                 name = "TMM-GK-TF-RebootSchedule1"
									 

									
										                                                 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               = "3658867a-c55a-4a76-996a-03be1c0560a2" 
									 

									
										 
									 

									
										}
									 

									
										*/
									 
								
							
						
					
				
			

			
				Terraform recognizes that the Delivery Group is not part of the IaC anymore and decommissions the Delivery Group:
			 

			
				
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}				
				
					
						
							
								
									PS C:\_TERRAFORM\_CCOnAzureV2\CCOnAzureV2-DeployCCEntities&gt; terraform apply 
									data.citrix_zone.GetTFAzureZoneID: Reading... 
									citrix_delivery_group.CreateImgDefDG: Refreshing state... [id=4d9e2b43-1364-44e1-9884-2e7b9a128e2f] 
									data.citrix_zone.GetTFAzureZoneID: Read complete after 0s [id=12f9de5d-fa7f-4739-aaa2-8338daabe878] 
									... 
									Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: 
									  - destroy 
									 
									Terraform will perform the following actions: 
									 
									  # citrix_delivery_group.CreateImgDefDG will be destroyed 
									  # (because citrix_delivery_group.CreateImgDefDG is not in configuration) 
									  - resource "citrix_delivery_group" "CreateImgDefDG" { 
									      - associated_machine_catalogs = [ 
									          - { 
									              - machine_catalog = "c7364b7a-c045-41cc-af81-7a3da2c5d84f" -&gt; null 
									              - machine_count   = 1 -&gt; null 
									            }, 
									        ] -&gt; null 
									      - autoscale_settings          = { 
									          - autoscale_enabled                                     = true -&gt; null 
									          - disconnect_off_peak_idle_session_after_seconds        = 60 -&gt; null 
									          - disconnect_peak_idle_session_after_seconds            = 60 -&gt; null 
									          - log_off_off_peak_disconnected_session_after_seconds   = 60 -&gt; null 
									          - log_off_peak_disconnected_session_after_seconds       = 60 -&gt; null 
									          - off_peak_buffer_size_percent                          = 0 -&gt; null 
									          - off_peak_disconnect_action                            = "Suspend" -&gt; null 
									          - off_peak_disconnect_timeout_minutes                   = 0 -&gt; null 
									          - off_peak_extended_disconnect_action                   = "Nothing" -&gt; null 
									          - off_peak_extended_disconnect_timeout_minutes          = 0 -&gt; null 
									          - off_peak_log_off_action                               = "Suspend" -&gt; null 
									          - off_peak_log_off_timeout_minutes                      = 0 -&gt; null 
									          - peak_autoscale_assigned_power_on_idle_action          = "Nothing" -&gt; null 
									          - peak_autoscale_assigned_power_on_idle_timeout_minutes = 0 -&gt; null 
									          - peak_buffer_size_percent                              = 0 -&gt; null 
									          - peak_disconnect_action                                = "Suspend" -&gt; null 
									          - peak_disconnect_timeout_minutes                       = 0 -&gt; null 
									          - peak_extended_disconnect_action                       = "Nothing" -&gt; null 
									          - peak_extended_disconnect_timeout_minutes              = 0 -&gt; null 
									          - peak_log_off_action                                   = "Suspend" -&gt; null 
									          - peak_log_off_timeout_minutes                          = 0 -&gt; null 
									          - power_off_delay_minutes                               = 30 -&gt; null 
									          - power_time_schemes                                    = [ 
									              - { 
									                  - days_of_week          = [ 
									                      - "Friday", 
									                      - "Monday", 
									                      - "Thursday", 
									                      - "Tuesday", 
									                      - "Wednesday", 
									                    ] -&gt; null 
									                  - display_name          = "AS-TMM-GK-TF-Azure-ImgDef-Weekday" -&gt; null 
									                  - peak_time_ranges      = [ 
									                      - "09:00-17:00", 
									                    ] -&gt; null 
									                  - pool_size_schedules   = [ 
									                      - { 
									                          - pool_size  = 1 -&gt; null 
									                          - time_range = "09:00-17:00" -&gt; null 
									                        }, 
									                    ] -&gt; null 
									                  - pool_using_percentage = false -&gt; null 
									                }, 
									            ] -&gt; null 
									        } -&gt; null 
									      - built_in_scopes             = [ 
									          - "00000000-0000-0000-0000-000000000000", 
									        ] -&gt; null 
									      - default_desktop_icon        = "1" -&gt; null 
									      - description                 = "" -&gt; null 
									      - desktops                    = [ 
									          - { 
									              - description             = "Terraform-based Delivery Group based on Image Definitions and Image Versions running on Azure" -&gt; null 
									              - enabled                 = true -&gt; null 
									              - published_name          = "DG-TMM-TACG-TF-Azure-ImgDef" -&gt; null 
									              - restricted_access_users = { 
									                  - allow_list = [ 
									                      - "xXxXxXxXxXxXxXxXx", 
									                    ] -&gt; null 
									                } -&gt; null 
									            }, 
									        ] -&gt; null 
									      - id                          = "4d9e2b43-1364-44e1-9884-2e7b9a128e2f" -&gt; null 
									      - inherited_scopes            = [] -&gt; null 
									      - minimum_functional_level    = "L7_20" -&gt; null 
									      - name                        = "DG-TMM-GK-TF-Azure-ImgDef" -&gt; null 
									      - policy_set_id               = "3658867a-c55a-4a76-996a-03be1c0560a2" -&gt; null 
									      - reboot_schedules            = [ 
									          - { 
									              - days_in_week            = [ 
									                  - "Sunday", 
									                ] -&gt; null 
									              - frequency               = "Weekly" -&gt; null 
									              - frequency_factor        = 1 -&gt; null 
									              - ignore_maintenance_mode = true -&gt; null 
									              - name                    = "TMM-GK-TF-RebootSchedule1" -&gt; null 
									              - natural_reboot_schedule = false -&gt; null 
									              - reboot_duration_minutes = 0 -&gt; null 
									              - reboot_schedule_enabled = true -&gt; null 
									              - start_date              = "2024-01-01" -&gt; null 
									              - start_time              = "02:00" -&gt; null 
									                # (1 unchanged attribute hidden) 
									            }, 
									        ] -&gt; null 
									      - restricted_access_users     = { 
									          - allow_list = [ 
									              - "xXxXxXxXxXxXxXxXx", 
									            ] -&gt; null 
									        } -&gt; null 
									      - scopes                      = [] -&gt; null 
									      - total_machines              = 1 -&gt; null 
									    } 
									 
									Plan: 0 to add, 0 to change, 1 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_delivery_group.CreateImgDefDG: Destroying... [id=4d9e2b43-1364-44e1-9884-2e7b9a128e2f] 
									citrix_delivery_group.CreateImgDefDG: Destruction complete after 3s 
									 
									Apply complete! Resources: 0 added, 0 changed, 1 destroyed. 
									PS C:\_TERRAFORM\_CCOnAzureV2\CCOnAzureV2-DeployCCEntities&gt;
								 
							
						
					
				
			

			
				Terraform has decommissioned the Delivery Group and its adjacent entities. 
				 
			 

			
				Summary
			

			
				Using Terraform for daily administration tasks can significantly streamline your workflow and improve efficiency:
			 

			
				Automation: 
				Terraform automates repetitive tasks, such as provisioning and managing infrastructure. This reduces the need for manual intervention and minimizes the risk of human error.
			 

			
				Consistency: 
				By defining your IaC, Terraform ensures that your environments are consistent and reproducible. This is particularly useful for maintaining multiple environments like development, staging, and production.
			 

			
				Scalability: 
				Terraform makes it easy to scale your infrastructure up or down based on demand. You can define your desired state, and Terraform will handle the necessary changes to achieve that state.
			 

			
				Collaboration: 
				Terraform configurations can be stored in version control systems like Git, enabling team collaboration. Team members can review, comment on, and approve changes before they are applied.
			 

			
				Visibility: 
				Terraform provides a clear and auditable record of changes to your infrastructure. This makes it easier to track changes, troubleshoot issues, and ensure compliance with organizational policies.
			 

			
				Integration: 
				Terraform integrates with various tools and services, such as CI/CD pipelines, monitoring systems, and configuration management tools. This allows you to create a seamless, automated workflow for managing your infrastructure.
			 

			
				Cost Management: 
				By automating resource provisioning and de-provisioning, Terraform helps you optimize resource usage and control costs. You can easily identify and eliminate unused or underutilized resources.
			 

			
				Using Terraform for daily administration tasks can help you achieve greater efficiency, consistency, and control over your infrastructure.]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_11/azurestoragecontainer.png.0759ea9b9fd5aaa0e1edc85c2ec7f7e2.png" length="63574" type="image/png"/><pubDate>Tue, 26 Nov 2024 14:40:37 +0000</pubDate></item><item><title>Image Management - A new way of deploying Machine Catalogs and reducing Master Image Complexity</title><link>https://community.stage.citrix.com/tech-zone/build/deployment-guides/mcs-image-mgmt/</link><description><![CDATA[Overview



	Citrix Machine Creation Services (MCS) has seen many advancements since its inception in XenDesktop 5.0, and the team continues to innovate within Citrix Virtual Apps and Desktops product line. 
 


	With the Image Management functionality, MCS separates the mastering phase from the overall provisioning workflow.
 


	You can prepare various MCS-Image versions (aka Prepared Images) from a single Master image and use it across multiple, different MCS-based Machine Catalogs, bringing both version management and logical grouping to Citrix MCS.
 


	This implementation significantly reduces the storage cost, the needed deployment time, and the administrative burden for VM deployment and image updating.
 


	The benefits of using Image Management are:
 


	
		Generate prepared images in advance without creating a Machine Catalog.
	
	
		Reuse prepared images in multiple scenarios, such as creating and updating a Machine Catalog.
	
	
		Significantly reduce the time needed to create or update a Machine Catalog.
	
	
		Sharing a managed Image between different Hosting Connection Resource Pools/Hosting Units
	



	
		NOTE:
	 

	
		This feature is currently applicable only to Azure and VMware virtualization environments. 
		You can still create an MCS-based Machine Catalog without using Prepared Images - you cannot get the benefits of Image Management after creation. 
		 
	 



	Requirements



	
		For Windows Master Images, a VDA with version 2311 or later and the Machine Creation Service (MCS) Storage Optimization feature enabled are supported. 
	
	
		You need to use Citrix WebStudio version 2402 or later.
	



	 
 


	
 


	
 


	 
 


	Limitations



	The following limitations apply:
 


	
		Persistent data disks in Machine Catalogs using Image Management are not supported.
	
	
		The Backup VM configuration feature and Confidential VMs are not supported.
	
	
		Multiple NICs in Azure-based VMs are not supported.
	
	
		Hibernation for Multi-Session-typed VMs is not supported.
	
	
		VHD- and VHDX-based Master Disks on Azure are not supported. The Master Image in Azure must be a snapshot, a managed disk, or an Azure Compute Gallery Image.
	






	
		CAUTION:
	 

	
		The Master Image cannot be deleted after preparing an Image if an Image Version based on it is needed! 
		 
	 



	 
 


	Use cases



	The benefits of using Image Management and Prepared Images include:
 


	
		Centralize Management: Manage all Images in one place using the Image node in Citrix Webstudio.
	
	
		Share Easily: Share prepared Images with multiple Machine Catalogs.
	
	
		Deploy Quickly: Prepare Images ahead of time for faster catalog provisioning.
	
	
		Save Costs: Save time and storage costs.
	
	
		Control Versions: Continuously update and manage the Image versions.
	
	
		Reduce Upgrade Risks: Use canary Image rollout for safer upgrades and fast rollbacks.
	
	
		Version Management: Manage different iterations or updates of a Master Image and maintain multiple versions for other purposes.
	
	
		Logical Grouping: Logically group Image versions based on various criteria such as project, department, or application and desktop type
	
	
		Sharing a managed Image between different Hosting Connection Resource Pools/Hosting Units
	



	 
 


	Image Management Components



	 
	 



	Prepared Images



	This new technology decouples the mastering phase from the Machine Catalog creation process or update workflow. It enables administrators to generate published Images from source Master Images without relying on Machine Catalog creation or updates. 
	This functionality empowers admins to produce numerous published Images from a single Source Image and manage them accordingly.  
	Additionally, you can use Citrix Studio to streamline administrator tasks, facilitating image browsing, property viewing, and action-taking.
 


	 
	Image Definitions
 


	Image definitions are a logical grouping of versions of an Image. 
	An Image Definition holds information about:
 


	
		The used Operating System.
	
	
		Support for Single- or Multi-Session Machine types.
	



	 
	Image Version
 


	Image Versions manage the versions of the Image Definitions. 
	An Image Definition can have multiple Image Versions. 
 


	Over time, somebody can apply updates, patches, or improvements to the Images. 
 


	You can use the same Image Version to provision multiple Machine Catalogs while easily tracking the associations.
 


	 
	Master Image
 


	When creating an Image Version, you choose the Master Image for this version.
 


	
		NOTE:
	 

	
		Important for vSphere-based environments: You can use only existing snapshots of a Master VM -  you cannot use the VM's base disk. 
		 
	 



	 
 


	
		NOTE:
	 

	
		Important for Azure-based environments: VHD- and VHDX-based Master Disks on Azure are not supported. The Master Image in Azure must be a snapshot, a managed disk, or an Azure Compute Gallery Image. 
		 
	 



	 
	Machine Profiles
 


	When creating an Image version, you can also choose a Machine Profile - the Image Version can inherit the settings from e.g. a chosen vSphere Machine Profile like:
 


	
		Tags placed on the template
	
	
		Custom attributes
	
	
		vSAN Storage Policies
	
	
		Virtual Hardware versions
	
	
		vSphere Virtual TPMs (vTPMs)
	
	
		CPU counts and Cores per socket
	
	
		NIC counts 
		 
	



	Machine Specification



	You can change some settings of the inherited Master Image/Machine Profile properties.
 


	
		For Azure: 
	 

	
		You can select a VM size. If you select a Machine Profile on the Image page, the wizard selects the VM size of the Machine Profile by default. 
		The CMEK setting can also be modified. 
		 
	 



	 
 


	
		For vSphere: 
	 

	
		If you select a Machine Profile, you can see the Virtual CPU count derived from the machine profile, which is unchangeable. 
		If you do not select a Machine Profile, you can only see and alter the memory size derived from the Master Image. 
		 
	 



	 
 


	NICs



	You can select or add NICs to prepare the Image. 
	For each NIC, select an associated Virtual Network.
 


	
		For vSphere: 
	 

	
		If you did not select a Machine Profile, then the NIC associated with the master image is selected by default. 
		If you select a Machine Profile, the NICs are derived from the Machine Profile, and the NIC count is unchangeable. 
		 
	 



	 
 


	
		For Azure: 
	 

	
		Multiple NICS are NOT SUPPORTED in Azure! 
		 
	 



	 
 


	Disk Settings



	This page is only applicable to Azure-based environments. 
	You can select the customer-managed encryption key (CMEK). If the Machine Profile doesn’t have a CMEK but the Master Image has, it pre-selects the CMEK from the Master Image. 
	 
 


	Create an MCS-based Machine Catalog on Microsoft Azure (Example)



	Create different Hosting Connection Resource Pools/Hosting Units in a Hosting Connection



	If you create different Hosting Connection Pools/Hosting Units using the same Hosting Connection, you can distribute the Resources, e.g., between different Azure locations. 
	That enables you to share the Managed Image automatically between these Hosting Units or, e.g., between the different Azure Locations.
 


	In this example, we have configured different Hosting Connection Resource Pools/Hosting Units in 2 different Azure locations - one in EastUS, and one in WestEurope:
 


	 
	 
 


	Create a Prepared Image/an Image Definition



	
		A new node in WebStudio named Images enables you to create and manage Prepared Images. Click on “Create Image Definition” to start creating a Prepared Image. 
		 
		 
	
	
		A Pop-Up shows you an Overview of the process ahead. 
		 
		 
	
	
		Choose the OS type and Session type suitable to your needs. 
		
	



	
		NOTE:
	 

	
		These Image Definition settings cannot be changed later or in later versions. 
		If you need another OS type or Session type, you must create a whole new Image Definition! 
		 
	 



	
		Choose the Hosting Connection and the adjacent Hosting Connection Resource Pool/Hosting Unit. 
		
	
	 
	
		Choose if a new Resource Group should be created or choose an existing Resource Group where the Image should be deployed: 
		 
		 
		 
		 
	
	
		Choose if the wizard should create a new Azure Compute Gallery or if you want to use an existing Compute Gallery. 
		The Image Definition and its versions will be stored in the chosen Compute Gallery and enable the sharing of the Managed Image between the different Hosting Connection Resource Pools/Hosting Units: 
		 
		 
		If you choose to create a new Azure Compute Gallery, the wizard will create it and store the Image Definition and all its adjacent versions in it: 
		
	



	 
 


	
		Choose the Resources and Master Image you want to include. Choose the Resource where the shared Image is available and which is most suitable to your needs.  
		
	



	 
 


	
		The list shows all available snapshots: 
		 
		 
	



	
		CAUTION:
	 

	
		The Virtual Machine containing the snapshot must NOT be running to ensure a smooth creation of the following steps! 
		 
	 



	
		You can select a Machine Profile if needed (see explanations earlier in this guide). 
		 
		 
	
	
		Choose the Storage and License Types according to your needs: 
		 
		 
	
	
		You can change some settings for the image version using the Machine specifications: 
		
	



	 
 


	
		Choose the NIC settings according to your needs: 
		 
		 
	
	
		Choose the Disk Settings according to your needs: 
		 
		 
	
	
		You can enter a description to pin detailed information about this version of the Image Definition. 
		
	



	 
 


	
		Name the definition: 
		
	



	 
 


	
		Now, the Image Definition is created. Depending on the underlying system, it takes a few minutes: 
		 
		 
	
	
		The Image Definition, together with the first Image Version, is successfully created: 
		
	



	 
 


	Sharing the Image 
 


	After successfully creating the Image Definition, you can share the Image between all Hosting Connection Resource Pools/Hosting Units adjacent to the chosen Hosting Connection.
 


	
		IMPORTANT: 
	 

	
		If you created the Image Definition/Image Versions in the Tech Preview stage, you cannot share the Managed Image - the adjacent "Manage Image Share" button is greyed out. 
		 
	 



	 
	 
 


	If you created the Image Definition recently, the "Manage Image Share" button is available: 
	 
	 
 


	Click on the "Manage Image Share" button to check the status - in this example screenshot, you see, that the Image is currently not shared between all available Hosting Connection Resource Pools/Hosting Units adjacent to the Hosting Connection: 
	
 


	Choose the available Hosting Connection Resource Pools/Hosting Units adjacent to the Hosting Connection to enable Image sharing: 
	
 


	 
	The wizard is now automatically updating all needed entities to enable Image sharing: 
	
 


	 
	After some time, Image sharing is enabled: 
	
 


	 
	You can now deploy the shared Image while creating the Machine Catalog. 
	 
 


	Create a new Image Version



	
		IMPORTANT: 
	 

	
		You can change the Hosting Connection Resource Pool/Hosting Unit of the new version adjacent to your needs. 
		 
	 



	If you want the Image Version configured differently from the initial configuration, configure the applicable settings on the Image, Storage, and License Types, Machine Specification, NICs, and Disk Settings pages of the Create Image Version dialog.
 


	
		IMPORTANT: 
	 

	
		These settings are Hypervisor-/Hyperscaler-dependent! Please be aware that not all settings are available for each Hypervisor/Hyperscaler! 
		 
	 



	 
	To create a new Image Version, follow these steps:
 


	
		Click “Create Image Version” to generate a new version.  
		 
		 
		 
		 
	
	
		The wizard now lists all possible Hosting Connection Resource Pool/Hosting Units. Choose a Hosting Connection Resource Pool/Hosting Unit for the new version that is adjacent to your needs: 
		 
		 
	
	
		Choose the Master Image disk/snapshot that applies to this new version. See explanations earlier in this guide. 
		 
		 
	
	
		Choose the Machine Profile. 
		 
		 
	
	
		Choose the Storage and License Types according to our needs: 
		 
		 
	
	
		Alter the Machine Specification if necessary: 
		 
		 
	
	
		Alter the NICs settings if necessary:: 
		 
		 
	
	
		Alter the Disk Settings if necessary: 
		 
		 
	
	
		Enter a description if needed: 
		
	
	
		Depending on the underlying system, it takes a few minutes to create. The creation is complete. The new Image Version is shown in the Image Definition tab: 
		 
		 
	



	Create an MCS-based Machine Catalog based on an Image Definition



	After creating an Image Definition and an Image Version we can now create a MCS-based Machine Catalog. 
	You will notice that creating a Machine Catalog based on a Managed Image is far quicker than creating a Machine Catalog based on an unmanaged Image:
 


	
		Example: Creation time using a Managed Image
	 

	
		
	 



	 
 


	
		Example: Creation time using an Unmanaged Image
	 

	
		
	 



	 
 


	The steps of creating a Machine Catalog based on a Managed Image are very similar to creating a “normal” Machine Catalog: 
 


	
		Click “Create Machine Catalog” to start the wizard. Follow the instructions: 
		
	



	
		IMPORTANT:
	 

	
		You cannot change the Machine type. It is determined by the Image Definition. 
		 
	 



	
		Choose the Machine Management type. As this is a Shared Image, you can choose the Host Connection Resource Pool/Hosting Unit in the Resource drop-down field: 
		 
		 
		If you create a Machine Catalog based on a non-Shared Image, you will NOT be able to choose the Host Connection Resource Pool/Hosting Unit in the Resource drop-down field: 
		 
		 
	
	
		Choose the Desktop Experience: 
		 
		 
	
	
		The Image is already determined, you can alter the Machine Profile according to your needs and select the correct Functional level of the MC: 
		 
		 
		 
		 
	
	
		Select the correct Storage and License type according to your needs: 
		 
		 
	
	
		Select the number of VMs and the settings that are needed further. Complete the MCS wizard. 
		 
		 
		 
		 
		 
		 
		 
		 
		 
		 
		 
		 
		 
		 
		 
		 
	
	
		Creation complete. The Machine Catalog is now ready for use 
		.
	



	 
 


	Update an existing MCS-based Machine Catalog with a new Image Definition/Image Version



	After creating a Machine Catalog, you can change the Image Definition/Image Version to another Image Version.
 


	
		IMPORTANT:
	 

	
		Deleting an Image Definition is only possible if no Image Version exists.
	 

	
		Deleting an Image Version is only possible if no Machine Catalog uses it. 
		 
	 



	 
 


	
		IMPORTANT:
	 

	
		You cannot change the Image Type. 
		 
	 



	 
	Updating an existing Machine Catalog is a straightforward process:



	
 


	The current version of the used Image contains LibreOffice. 
	Let´s assume we want to change LibreOffice to Office 365, so we must change the Image Version. 
	 
 


	
 


	
		The current Image Version has LibreOffice installed.
	



	
 


	
		Choose the Prepared Image containing Office 365.
	



	
 


	 
 


	
 


	 
 


	
 


	
		Choose the rollout strategy you want to use to deploy the new Image Version.
	



	
 


	
		Changing the Image Version is complete.
	



	
 


	
		The Machine Catalog is now running on an Image Version containing Office 365. 
		All VMs got the new version.
	



	
 


	 
 


	Create an Image Definition and an MCS-based Machine Catalog on vSphere



	In this part of the guide, we will explore using PowerShell to automate the creation of a new Image Definition and a new MCS-based Machine Catalog based on this Image Definition.
 


	The underlying infrastructure is vSphere.
 


	
		NOTE:
	 

	
		We will only mention the differences in Powershell cmdlets for an Azure-based infrastructure—a complete walkthrough is not yet provided.  
		 
	 



	 
 


	Create a Prepared Image/an Image Definition using PowerShell



	Before creating the Image Definition/Image Version, we need to get more details about the environment - like the name of the Hosting Unit, the name of the Network to use, and the snapshot we want to use.
 


	 
 


	We can retrieve these properties using PowerShell.
 


	Run the following cmdlets on one of the Delivery Controllers:
 


	PS C:\_TACG&gt; Add-PSSnapin Citrix.*
 


	PS C:\_TACG&gt; Get-ChildItem XDHyp:\Hostingunits
 


	Write down the values of HostingUnitName and NetworkPath or save these in variables for later use.
 


	
		
	
	
		
			
				
					#Load the Citrix Powershell Snap-In 
					PS C:\_TACG&gt; Add-PSSnapin Citrix.*
				 

				
					#Get all HostingUnits registered 
					PS C:\_TACG&gt; Get-ChildItem XDHyp:\Hostingunits
				 

				
					PSPath                 : Citrix.Host.Admin.V2\Citrix.Hypervisor::XDHyp:\Hostingunits\VMN 
					PSParentPath           : Citrix.Host.Admin.V2\Citrix.Hypervisor::XDHyp:\Hostingunits 
					PSChildName            : VMN 
					PSDrive                : XDHyp 
					PSProvider             : Citrix.Host.Admin.V2\Citrix.Hypervisor 
					PSIsContainer          : True 
					HostingUnitName        : VMN 
					HostingUnitUid         : 5f09bb65-3ce5-4f68-aab4-bcc8503519ff 
					HypervisorConnection   : vcenter tacg 
					Metadata               : {} 
					MetadataMap            : {} 
					NetworkId              : Network:network-12 
					NetworkPath            : XDHyp:\Connections\vcenter tacg\TACG.datacenter\10.10.110.12.computeresource\VM Network.network 
					PermittedNetworks      : {XDHyp:\Connections\vcenter tacg\TACG.datacenter\10.10.110.12.computeresource\VM Network.network} 
					RootId                 : domain-s5048 
					RootPath               : XDHyp:\Connections\vcenter tacg\TACG.datacenter\10.10.110.12.computeresource 
					Storage                : {XDHyp:\Connections\vcenter tacg\TACG.datacenter\10.10.110.12.computeresource\datastore1 (1).storage} 
					VMTaggingEnabled       : True 
					UseLocalStorageCaching : False 
					PersonalvDiskStorage   : {} 
					GpuType                : {} 
					AvailabilityZones      : {} 
					AdditionalStorage      : {TemporaryStorage} 
					CustomProperties       : 
					FullPath               : XDHyp:\Hostingunits\VMN
				 

				
					PS C:\_TACG&gt;
				 
			
		
	



	Now that we have the HostingUnitName and the NetworkPath, we need to determine the path of the snapshot we want to use.
 


	Make sure you use the correct HostingUnitName in the -LiteralPath setting of the cmdlet:
 


	PS C:\_TACG&gt; Get-HypInventoryItem -LiteralPath "XDHyp:\HostingUnits\VMN" -ResourceType snapshot -MaxRecords 100 | Select-Object FullPath | Format-Table -AutoSize
 


	
		
	
	
		
			
				
					#Get all available snapshots 
					PS C:\_TACG&gt; Get-HypInventoryItem -LiteralPath "XDHyp:\HostingUnits\VMN" -ResourceType snapshot -MaxRecords 100 | Select-Object FullPath | Format-Table -AutoSize 
					FullPath 
					-------- 
					XDHyp:\HostingUnits\VMN\XXXXXXXXXXXXXXXXXXXX 
					XDHyp:\HostingUnits\VMN\XXXXXXXXXXXXXXXXXXXX 
					XDHyp:\HostingUnits\VMN\XXXXXXXXXXXXXXXXXXXX 
					XDHyp:\HostingUnits\VMN\XXXXXXXXXXXXXXXXXXXX 
					XDHyp:\HostingUnits\VMN\XXXXXXXXXXXXXXXXXXXX 
					XDHyp:\HostingUnits\VMN\TACG.datacenter\10.10.110.12.computeresource\TACG-VSP-W11-M.vm\main.snapshot\mit libreoffice.snapshot\with office.snapshot\nach office.snapshot 
					XDHyp:\HostingUnits\VMN\XXXXXXXXXXXXXXXXXXXX 
					XDHyp:\HostingUnits\VMN\XXXXXXXXXXXXXXXXXXXX 
					XDHyp:\HostingUnits\VMN\XXXXXXXXXXXXXXXXXXXX 
					XDHyp:\HostingUnits\VMN\XXXXXXXXXXXXXXXXXXXX
				 

				
					PS C:\_TACG&gt; 
					 
				 
			
		
	



	 
 


	
		IMPORTANT:
	 

	
		You need to alter the format or the full path for the following steps to work:
	 

	
		The original path looks like this:
	 

	XDHyp:\HostingUnits\VMN\TACG.datacenter\10.10.110.12.computeresource\TACG-VSP-W11-M.vm\main.snapshot\mit libreoffice.snapshot\with office.snapshot\nach office.snapshot

	
		Remove the references to the Datacenter- and Host-name, so the usable path must look like this:
	 

	XDHyp:\HostingUnits\VMN\TACG-VSP-W11-M.vm\main.snapshot\mit libreoffice.snapshot\with office.snapshot\nach office.snapshot

	
		 
	 



	 
 


	Write down the values of the full path of the snapshot you want to use or save it in a variable for later use.
 


	All prerequisite variables are available now, so we can start the creation of the Image Definition/Image Version:
 


	
		#Check if the desired name is available 
		PS C:\_TACG&gt; Test-ProvImageDefinitionNameAvailable -ImageDefinitionName "PoSH-ImgDef-Test"
	 

	
		Name             Available 
		----             --------- 
		PoSH-ImgDef-Test      True
	 

	
		PS C:\_TACG&gt;
	 



	This cmdlet checks if the desired name for the Image Definition is available:
 


	
		
	
	
		
			
				
					#Check if the desired name is available 
					PS C:\_TACG&gt; Test-ProvImageDefinitionNameAvailable -ImageDefinitionName "PoSH-ImgDef-Test"
				 

				
					Name             Available 
					----             --------- 
					PoSH-ImgDef-Test      True
				 

				
					PS C:\_TACG&gt; 
					 
				 
			
		
	



	As the name is available, we can continue.
 


	 
 


	
		# Create Image Definition 
		PS C:\_TACG&gt; $ImageDefinition = New-ProvImageDefinition -ImageDefinitionName "PoSH-ImgDef-Test" -OsType Windows -VdaSessionSupport SingleSession
	 

	
		PS C:\_TACG&gt; $ImageDefinition 
		CreationTime        : 31.05.2024 10:20:56 
		Description         : 
		ImageDefinitionName : PowerShell-ImgDef-Test 
		ImageDefinitionUid  : ad362330-98af-4f1f-a662-5eb12f7388ff 
		LatestVersion       : 0 
		Metadata            : {} 
		OsType              : Windows 
		VDASessionSupport   : SingleSession 
		VersionsCount       : 0
	 



	 
 


	
		
	
	
		
			
				
					# Create Image Definition 
					PS C:\_TACG&gt; $ImageDefinition = New-ProvImageDefinition -ImageDefinitionName "PoSH-ImgDef-Test" -OsType Windows -VdaSessionSupport SingleSession
				 

				
					PS C:\_TACG&gt; $ImageDefinition 
					CreationTime        : 31.05.2024 10:20:56 
					Description         : 
					ImageDefinitionName : PoSH-ImgDef-Test 
					ImageDefinitionUid  : ad362330-98af-4f1f-a662-5eb12f7388ff 
					LatestVersion       : 0 
					Metadata            : {} 
					OsType              : Windows 
					VDASessionSupport   : SingleSession 
					VersionsCount       : 0
				 

				
					PS C:\_TACG&gt; 
					 
				 
			
		
	



	The Image Definition was successful. Now we can create the Image Version:
 


	
		#Create Image Version 
		PS C:\_TACG&gt; $ImageVersion = New-ProvImageVersion -ImageDefinitionName $ImageDefinition.ImageDefinitionName -Description "V1"
	 

	
		PS C:\_TACG&gt; $ImageVersion
	 

	
		CreationTime           : 31.05.2024 10:41:14 
		Description            : V1 
		ImageDefinitionName    : PoSH-ImgDef-Test 
		ImageDefinitionUid     : 310ea5ff-910e-472b-b1f6-29ee948853bf 
		ImageVersionNumber     : 1 
		ImageVersionSpecsCount : 0 
		ImageVersionUid        : c25b2662-2c3a-474e-a116-d98d27f1321f 
		Metadata               : {}
	 

	
		PS C:\_TACG&gt;
	 



	 
 


	
		
	
	
		
			
				
					#Create Image Version 
					PS C:\_TACG&gt; $ImageVersion = New-ProvImageVersion -ImageDefinitionName $ImageDefinition.ImageDefinitionName -Description "V1"
				 

				
					PS C:\_TACG&gt; $ImageVersion
				 

				
					CreationTime           : 31.05.2024 10:41:14 
					Description            : V1 
					ImageDefinitionName    : PoSH-ImgDef-Test 
					ImageDefinitionUid     : 310ea5ff-910e-472b-b1f6-29ee948853bf 
					ImageVersionNumber     : 1 
					ImageVersionSpecsCount : 0 
					ImageVersionUid        : c25b2662-2c3a-474e-a116-d98d27f1321f 
					Metadata               : {}
				 

				
					PS C:\_TACG&gt;
				 
			
		
	



	We created the Image Version, and now we can set the needed properties:
 


	
		#Set the properties for the Image Version 
		PS C:\_TACG&gt; $MasterImageVersionSpec = Add-ProvImageVersionSpec -ImageDefinitionName $ImageVersion.ImageDefinitionName -ImageVersionNumber $ImageVersion.ImageVersionNumber -HostingUnitName VMN -MasterImagePath $MasterImagePath
	 

	
		PS C:\_TACG&gt; $MasterImageVersionSpec
	 

	
		Context                   : {"PluginFactoryName":"VmwareFactory"} 
		CreationTime              : 31.05.2024 10:47:42 
		DiskSize                  : 60 
		Error                     : 
		HostingUnitName           : VMN 
		HostingUnitUid            : 5f09bb65-3ce5-4f68-aab4-bcc8503519ff 
		ImageDefinitionName       : PoSH-ImgDef-Test 
		ImageDefinitionUid        : 310ea5ff-910e-472b-b1f6-29ee948853bf 
		ImageInstances            : {} 
		ImageRuntimeEnvironment   : 
		ImageVersionNumber        : 1 
		ImageVersionSpecStatus    : Success 
		ImageVersionSpecUid       : 9eef9112-e2a1-444f-88b0-b31d64cafe7d 
		ImageVersionUid           : c25b2662-2c3a-474e-a116-d98d27f1321f 
		ImportType                : Customer 
		IsCitrixManaged           : False 
		IsPrepared                : False 
		MasterImagePath           : /TACG.datacenter/10.10.110.12.computeresource/TACG-VSP-W11-M.vm/main.snapshot/mit libreoffice.snapshot/with office.snapshot/nach office.snapshot 
		Metadata                  : {} 
		PreparationType           : None 
		ProvisioningSchemeCount   : 0 
		SourceImageVersionSpecUid : 
		Warnings                  : {} 
		ZoneUid                   : d9bdd2c6-52de-41ff-839f-298258e4c862
	 

	
		PS C:\_TACG&gt;
	 



	 
 


	
		
	
	
		
			
				
					#Set the properties for the Image Version 
					PS C:\_TACG&gt; $MasterImageVersionSpec = Add-ProvImageVersionSpec -ImageDefinitionName $ImageVersion.ImageDefinitionName -ImageVersionNumber $ImageVersion.ImageVersionNumber -HostingUnitName VMN -MasterImagePath $MasterImagePath
				 

				
					PS C:\_TACG&gt; $MasterImageVersionSpec
				 

				
					Context                   : {"PluginFactoryName":"VmwareFactory"} 
					CreationTime              : 31.05.2024 10:47:42 
					DiskSize                  : 60 
					Error                     : 
					HostingUnitName           : VMN 
					HostingUnitUid            : 5f09bb65-3ce5-4f68-aab4-bcc8503519ff 
					ImageDefinitionName       : PoSH-ImgDef-Test 
					ImageDefinitionUid        : 310ea5ff-910e-472b-b1f6-29ee948853bf 
					ImageInstances            : {} 
					ImageRuntimeEnvironment   : 
					ImageVersionNumber        : 1 
					ImageVersionSpecStatus    : Success 
					ImageVersionSpecUid       : 9eef9112-e2a1-444f-88b0-b31d64cafe7d 
					ImageVersionUid           : c25b2662-2c3a-474e-a116-d98d27f1321f 
					ImportType                : Customer 
					IsCitrixManaged           : False 
					IsPrepared                : False 
					MasterImagePath           : /TACG.datacenter/10.10.110.12.computeresource/TACG-VSP-W11-M.vm/main.snapshot/mit libreoffice.snapshot/with office.snapshot/nach office.snapshot 
					Metadata                  : {} 
					PreparationType           : None 
					ProvisioningSchemeCount   : 0 
					SourceImageVersionSpecUid : 
					Warnings                  : {} 
					ZoneUid                   : d9bdd2c6-52de-41ff-839f-298258e4c862
				 

				
					PS C:\_TACG&gt;
				 
			
		
	



	All properties are set - we can start with the creation of the configured Image Version:
 


	
		#Create the task to generate the Image Version asynchronously 
		#Be aware that the network mapping path is in the proper format – look at the corresponding HostingUnit property and change it into the correct format
	 

	
		PS C:\_TACG&gt; $Task = New-ProvImageVersionSpec -SourceImageVersionSpecUid  $MasterImageVersionSpec.ImageVersionSpecUid -NetworkMapping @{"0"="XDHyp:\HostingUnits\VMN\VM Network.network"} -VMCpuCount 2 -VMMemoryMB 4096 -RunAsynchronously
	 

	
		PS C:\_TACG&gt; $Task 
		Guid 
		---- 
		86a7d87e-ab44-4786-a059-27c2040fabb1
	 



	 
 


	
		
	
	
		
			
				
					#Create the task to generate the Image Version asynchronously 
					#Be aware that the network mapping path is in the correct format – look at the corresponding HostingUnit property and change it into the correct format
				 

				
					PS C:\_TACG&gt; $Task = New-ProvImageVersionSpec -SourceImageVersionSpecUid  $MasterImageVersionSpec.ImageVersionSpecUid -NetworkMapping @{"0"="XDHyp:\HostingUnits\VMN\VM Network.network"} -VMCpuCount 2 -VMMemoryMB 4096 -RunAsynchronously
				 

				
					PS C:\_TACG&gt; $Task 
					Guid 
					---- 
					86a7d87e-ab44-4786-a059-27c2040fabb1 
					PS C:\_TACG&gt;
				 
			
		
	



	 
 


	As the task is running asynchronously, we need to use a cmdlet to see the progress:
 


	
		PS C:\_TACG&gt; Get-ProvTask -TaskId 86a7d87e-ab44-4786-a059-27c2040fabb1
	 



	Here is the output of a still-running task - the TaskProgress property shows the percentage of completeness:
 


	
		
			
				
					PS C:\_TACG&gt; Get-ProvTask -TaskId   86a7d87e-ab44-4786-a059-27c2040fabb1 
					TaskId                    : 86a7d87e-ab44-4786-a059-27c2040fabb1 
					Active                    : True 
					Host                      : TACG-IPS-DDC 
					DateStarted               : 31.05.2024 10:51:32 
					Metadata                  : {} 
					ImageVersionSpecUid       : 4079e3ee-e1ec-4b1d-ba32-b373f11b6ed3 
					SourceImageVersionSpecUid : 9eef9112-e2a1-444f-88b0-b31d64cafe7d 
					ImageDefinitionName       : PoSH-ImgDef-Test 
					ImageDefinitionUid        : 310ea5ff-910e-472b-b1f6-29ee948853bf 
					ImageVersionNumber        : 1 
					ImageVersionUid           : c25b2662-2c3a-474e-a116-d98d27f1321f 
					MasterImage               : /TACG-VSP-W11-M.vm/main.snapshot/mit libreoffice.snapshot/with office.snapshot/nach office.snapshot 
					DiskSize                  : 60 
					MachineProfile            : 
					HostingUnitName           : VMN 
					HostingUnitUid            : 5f09bb65-3ce5-4f68-aab4-bcc8503519ff 
					PreparationType           : Mcs 
					CustomProperties          : 
					NetworkMaps               : {0} 
					ServiceOffering           : 
					CpuCount                  : 2 
					MemoryMB                  : 4096 
					Storage                   : {/TACG.datacenter/10.10.110.12.computeresource/datastore1 (1).storage} 
					Warnings                  : 
					TaskState                 : ConsolidatingMasterImage 
					TaskType                  : NewProvImageVersionSpec 
					LastUpdateTime            : 31.05.2024 10:51:32 
					CurrentOperation          : ConsolidatingMasterImage 
					TerminatingError          : 
					TaskProgress              : 17,1 
					WorkflowStatus            : Running 
					DateFinished              : 01.01.0001 01:00:00 
					TaskExpectedCompletion    : 
					ActiveElapsedTime         : 0 
					Status                    : Running 
					TaskStateInformation      : ConsolidatingMasterImage 
					Type                      : NewProvImageVersionSpec
				 

				
					PS C:\_TACG&gt; 
					 
				 
			
		
	



	Here is the output of a successfully finished task:
 


	
		
	
	
		
			
				
					PS C:\_TACG&gt; Get-ProvTask -TaskId   86a7d87e-ab44-4786-a059-27c2040fabb1 
					TaskId                    : 86a7d87e-ab44-4786-a059-27c2040fabb1 
					Active                    : False 
					Host                      : TACG-IPS-DDC 
					DateStarted               : 31.05.2024 10:51:32 
					Metadata                  : {} 
					ImageVersionSpecUid       : 4079e3ee-e1ec-4b1d-ba32-b373f11b6ed3 
					SourceImageVersionSpecUid : 9eef9112-e2a1-444f-88b0-b31d64cafe7d 
					ImageDefinitionName       : PoSH-ImgDef-Test 
					ImageDefinitionUid        : 310ea5ff-910e-472b-b1f6-29ee948853bf 
					ImageVersionNumber        : 1 
					ImageVersionUid           : c25b2662-2c3a-474e-a116-d98d27f1321f 
					MasterImage               : /TACG-VSP-W11-M.vm/main.snapshot/mit libreoffice.snapshot/with office.snapshot/nach office.snapshot 
					DiskSize                  : 60 
					MachineProfile            : 
					HostingUnitName           : VMN 
					HostingUnitUid            : 5f09bb65-3ce5-4f68-aab4-bcc8503519ff 
					PreparationType           : Mcs 
					CustomProperties          : 
					NetworkMaps               : {0} 
					ServiceOffering           : 
					CpuCount                  : 2 
					MemoryMB                  : 4096 
					Storage                   : {/TACG.datacenter/10.10.110.12.computeresource/datastore1 (1).storage} 
					Warnings                  : {} 
					TaskState                 : Finished 
					TaskType                  : NewProvImageVersionSpec 
					LastUpdateTime            : 31.05.2024 11:02:46 
					CurrentOperation          : 
					TerminatingError          : 
					TaskProgress              : 100 
					WorkflowStatus            : Completed 
					DateFinished              : 31.05.2024 11:02:46 
					TaskExpectedCompletion    : 31.05.2024 11:02:46 
					ActiveElapsedTime         : 673 
					Status                    : Finished 
					TaskStateInformation      : Completed 
					Type                      : NewProvImageVersionSpec 
					 
					PS C:\_TACG&gt;
				 

				
					 
				 
			
		
	



	 
 


	We can now see the created Image Definition/Image Version in Web Studio:
 


	
 


	 
 


	Differences in the Cmdlets between vSphere and Azure:



	 
 


	Here are the example cmdlets for vSphere:



	 
 


	
		Test-ProvImageDefinitionNameAvailable -ImageDefinitionName "NameOfImageDefinition"
	 

	
		$ImageDefinition = New-ProvImageDefinition -ImageDefinitionName "NameOfImageDefinition" -OsType Windows -VdaSessionSupport SingleSession
	 

	
		$ImageVersion = New-ProvImageVersion -ImageDefinitionName $ImageDefinition.ImageDefinitionName -Description "VersionNumber" 
	 

	
		$MasterImagePath = "XDHyp:\HostingUnits\HostingUnitName\VMName.vm\SnapshotName.snapshot"
	 

	
		$MasterImageVersionSpec = Add-ProvImageVersionSpec -ImageDefinitionName $ImageVersion.ImageDefinitionName -ImageVersionNumber $ImageVersion.ImageVersionNumber -HostingUnitName HostingUnitName -MasterImagePath $MasterImagePath
	 

	
		$Task = New-ProvImageVersionSpec -SourceImageVersionSpecUid  $MasterImageVersionSpec.ImageVersionSpecUid -NetworkMapping @{"0"="XDHyp:\HostingUnits\HostingUnitName\VMNetworkName.network"} -VMCpuCount 2 -VMMemoryMB 4096 -RunAsynchronously
	 



	 
 


	Here are the example cmdlets for Azure:



	 
 


	
		Test-ProvImageDefinitionNameAvailable -ImageDefinitionName "NameOfImageDefinition"
	 

	
		$ImageDefinition = New-ProvImageDefinition -ImageDefinitionName "NameOfImageDefinition" -OsType Windows -VdaSessionSupport MultiSession
	 

	
		$ImageVersion = New-ProvImageVersion -ImageDefinitionName $ImageDefintion.ImageDefinitionName -Description "VersionNumber"  
	 

	
		$MasterImagePath = "XDHyp:\HostingUnits\AzureHostingUniteName\ImageName.folder\AzureResourceGroupName.resourcegroup\SnapshotName.snapshot"
	 

	
		$MasterImageVersionSpec = Add-ProvImageVersionSpec -ImageDefinitionName $ImageVersion.ImageDefinitionName -ImageVersionNumber $ImageVersion.ImageVersionNumber -HostingUnitName AzureHostingUnitName  -MasterImagePath $MasterImagePath
	 

	
		$Task = New-ProvImageVersionSpec -SourceImageVersionSpecUid  $MasterImageVersionSpec.ImageVersionSpecUid -NetworkMapping @{"0"="XDHyp:\HostingUnits\AzureHostingUnitName \virtualprivatecloud.folder\AzureResourceGroupName.resourcegroup\NetworkName.virtualprivatecloud\NetworkName.network"} -ServiceOffering "XDHyp:\HostingUnits\AzureHostingUnitName\serviceoffering.folder\MachineSize.serviceoffering" -CustomProperties "&lt;CustomProperties xmlns=`"http://schemas.citrix.com/2014/xd/machinecreation`" xmlns:xsi=`"http://www.w3.org/2001/XMLSchema-instance`"&gt;&lt;/CustomProperties&gt;" -RunAsynchronously
	 



	 
 


	Create an MCS-based Machine Catalog on vSphere with the Image Management feature enabled using PowerShell



	The difference between creating a “normal” MCS-based Machine Catalog and an MCS-based Machine Catalog based on a Managed Image is to reference the Managed Image in the New-ProvScheme cmdlet. 
	 
 


	“Normal” Machine Catalog:



	
		New-ProvScheme -HostingUnitName Name -IdentityPoolName Name -MasterImageVM 'XDHyp:\HostingUnits\PathToMasterImage' 
	 

	
		-ProvisioningSchemeName Name -Scope @() -NetworkMapping @{"0"="XDHyp:\HostingUnits\NetworkPathAndName.network"} -RunAsynchronously
	 



	Machine Catalog with Managed Image:



	
		$PreparedImageVersionSpec = Get-ProvImageVersionSpec -ImageDefinitionName image2 -ImageVersionNumber 1 
	 

	
		 
	 

	
		New-ProvScheme -ProvisioningSchemeName Name -ImageVersionSpecUid $PreparedImageVersionSpec.ImageVersionSpecUid -HostingUnitName Name -IdentityPoolName Name -CleanOnBoot -Scope @() -SecurityGroup @() -NetworkMapping @{"0"="XDHyp:\HostingUnits\NetworkPathAndName.network"} -VMCpuCount 2 -VMMemoryMB 4096 -RunAsynchronously
	 



	 
 


	For a complete guide to creating Machine Catalogs using PowerShell, please look at https://community.citrix.com/tech-zone/build/deployment-guides/citrix-azure-hibernation-posh/. You can find all the needed cmdlets and workflows there.]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_06/MCSIO.png.56f405cb8098ebdeebc39424bcba7196.png" length="125428" type="image/png"/><pubDate>Mon, 18 Nov 2024 08:30:00 +0000</pubDate></item><item><title>Citrix Integration with Nutanix Prism Central</title><link>https://community.stage.citrix.com/tech-zone/learn/tech-insights/citrix-integration-nutanix-pc/</link><description>The Citrix integration with Nutanix Prism Central on AHV makes it easier for Citrix Administrators to manage Citrix workloads on Nutanix's hybrid multi-cloud environments. With this new integration, Citrix's capabilities work directly with Nutanix Prism Central hybrid and multi-cloud environments, including on-premises AHV, NC2 on Azure, and NC2 on AWS.  Find out more in this Citrix Tech Insight!
 


	
		Citrix_TechInsight_NutanixPlugin.mp4</description><pubDate>Fri, 15 Nov 2024 15:02:09 +0000</pubDate></item><item><title>PoC Guide: Deploying Citrix DaaS and Amazon WorkSpaces Core using Terraform</title><link>https://community.stage.citrix.com/tech-zone/learn/poc-guides/daas-and-awc-terraform/</link><description><![CDATA[PoC Guide: Deploying Citrix DaaS and Amazon WorkSpaces Core using Terraform 
	 



	Overview



	AWS WorkSpaces Core is a managed virtual desktop infrastructure designed to work with third-party VDI solutions such as Citrix DaaS. It is the compute layer of AWS workloads that the Citrix DaaS control plane can help orchestrate and manage to deliver HDX-optimized apps anywhere. AWS WorkSpaces Core and Citrix improve cost savings, simplify cloud management, and provide a superior user experience.
 


	Citrix DaaS treats AWS WorkSpaces Core as another Resource Location option to leverage within your deployment. It helps your IT teams manage and provision WorkSpace Core desktops directly from the Citrix platform, reducing application delivery complexity and simplifying operations. With security features like Zero Trust Network Access (ZTNA), contextual access policies, and secure browser redirection, Citrix protects your business from cyber threats and data leaks.
 


	Citrix improves the WorkSpaces Core experience with advanced HDX graphics, Unified Communication optimizations, USB redirection, and support for 3D workloads, ensuring smooth performance on any device or network. With centralized management, automated patching, and streamlined updates, Citrix optimizes your budget by reducing IT costs and complexity.
 


	This Proof of Concept guide will help you deploy Citrix DaaS and Amazon WorkSpaces Core using Terraform.
 


	
		 Note: 
	 

	
		Please find a description of installing Terraform and initial configurations in our Tech Zone Deployment Guide: Installing Terraform and Configuring the Citrix Terraform Provider. 
		 
	 



	The following is covered in this guide:
 


	
		Deploying all needed entities for Citrix DaaS:

		
			
				Deploying a Resource Location and its Cloud Connectors
			
		
	
	
		Deploying all needed entities for Amazon WorkSpaces Core:
		
			
				Deploying a VPC
			
			
				Deploying the Subnets and Gateways
			
			
				Setting the IAM permissions
			
			
				Deploying all needed instances like the Jumphost and a Domain Controller
			
		
	
	
		Creating the Deployment of Citrix DaaS on Amazon WorkSpaces Core 
		 
	



	
		Note: 
	 

	
		To deploy Citrix DaaS on Amazon WorkSpaces Core using the GUI, please review our PoC Guide: Deploying Citrix DaaS and Amazon Workspaces Core.  
		 
	 



	 
 


	Deploying Citrix DaaS and Amazon WorkSpaces Core



	Before deploying Citrix DaaS and Amazon WorkSpaces Core, we must create and configure all necessary prerequisites.
 


	We assume you already have a Citrix Cloud tenant with an active trial or a paid subscription to the Citrix DaaS service and an active AWS account. 
	 
 


	Installing AWS Tools for PowerShell and AWS CLI



	This guide uses AWS CLI and PowerShell cmdlets to determine further needed information.
 


	
 


	
 


	Install all needed AWS Tools modules you need – in this example, we install three different modules:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						
							
								PS C:\_TERRAFORM\_AWSCore&gt; Install-AWSToolsModule AWS.Tools.EC2 -Force 
								Installing module AWS.Tools.Common version 4.1.693.0 
								Installing module AWS.Tools.EC2 version 4.1.693.0 
								Installing module AWS.Tools.IdentityManagement version 4.1.693.0 
								Installing module AWS.Tools.ServiceQuotas version 4.1.693.0 
								Installing module AWS.Tools.SimpleSystemsManagement version 4.1.693.0 
								Installing module AWS.Tools.WorkSpaces version 4.1.693.0 
								PS C:\_TERRAFORM\_AWSCore&gt; 
								PS C:\_TERRAFORM\_AWSCore&gt; Install-AWSToolsModule AWS.Tools.S3 -Force 
								Installing module AWS.Tools.S3 version 4.1.693.0 
								PS C:\_TERRAFORM\_AWSCore&gt; 
								PS C:\_TERRAFORM\_AWSCore&gt; Install-AWSToolsModule AWS.Tools.Workspaces -Force 
								PS C:\_TERRAFORM\_AWSCore&gt;
							 
						
					
				
			
		
	



	You can check if all needed modules are installed:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\_TERRAFORM\_AWSCore&gt; Get-AWSPowerShellVersion -ListServiceVersionInfo 
						 
						AWS Tools for PowerShell 
						Version 4.1.693 
						Copyright 2012-2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. 
						 
						Amazon Web Services SDK for .NET 
						Core Runtime Version 3.7.400.46 
						Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 
						 
						Release notes: https://github.com/aws/aws-tools-for-powershell/blob/master/CHANGELOG.md 
						 
						This software includes third party software subject to the following copyrights: 
						- Logging from log4net, Apache License 
						[http://logging.apache.org/log4net/license.html] 
						 
						 
						Service                                               Noun Prefix   Module Name                                   SDK Assembly Version 
						-------                                               -----------   -----------                                   -------------------- 
						AWS IAM Access Analyzer                               IAMAA         AWS.Tools.AccessAnalyzer                      3.7.400.46 
						AWS Account                                           ACCT          AWS.Tools.Account                             3.7.400.46 
						AWS Certificate Manager Private Certificate Authority PCA           AWS.Tools.ACMPCA                              3.7.400.47 
						AWS Amplify                                           AMP           AWS.Tools.Amplify                             3.7.402.11 
						Amplify Backend                                       AMPB          AWS.Tools.AmplifyBackend                      3.7.400.46 
						AWS Amplify UI Builder                                AMPUI         AWS.Tools.AmplifyUIBuilder                    3.7.400.46 
						Amazon API Gateway                                    AG            AWS.Tools.APIGateway                          3.7.400.47 
						Amazon API Gateway Management API                     AGM           AWS.Tools.ApiGatewayManagementApi             3.7.400.46 
						Amazon API Gateway V2                                 AG2           AWS.Tools.ApiGatewayV2                        3.7.400.46 
						AWS AppConfig                                         APPC          AWS.Tools.AppConfig                           3.7.402.7 
						AWS AppConfig Data                                    ACD           AWS.Tools.AppConfigData                       3.7.400.46 
						Amazon Web Services AppFabric                         AFAB          AWS.Tools.AppFabric                           3.7.400.46 
						Amazon Appflow                                        AF            AWS.Tools.Appflow                             3.7.400.47 
						Amazon AppIntegrations Service                        AIS           AWS.Tools.AppIntegrationsService              3.7.401.43 
						Application Auto Scaling                              AAS           AWS.Tools.ApplicationAutoScaling              3.7.401.46 
						Amazon ApplicationCostProfiler                        ACP           AWS.Tools.ApplicationCostProfiler             3.7.400.46 
						AWS Application Discovery Service                     ADS           AWS.Tools.ApplicationDiscoveryService         3.7.400.46 
						Amazon CloudWatch Application Insights                CWAI          AWS.Tools.ApplicationInsights                 3.7.401.10 
						Amazon CloudWatch Application Signals                 CWAS          AWS.Tools.ApplicationSignals                  3.7.402.31 
						AWS App Mesh                                          AMSH          AWS.Tools.AppMesh                             3.7.400.46 
						AWS Service Catalog App Registry                      SCAR          AWS.Tools.AppRegistry                         3.7.400.46 
						AWS App Runner                                        AAR           AWS.Tools.AppRunner                           3.7.400.46 
						Amazon AppStream                                      APS           AWS.Tools.AppStream                           3.7.403.18 
						AWS AppSync                                           ASYN          AWS.Tools.AppSync                             3.7.402.3 
						AWS Mainframe Modernization Application Testing       AT            AWS.Tools.AppTest                             3.7.400.46 
						AWS ARC - Zonal Shift                                 AZS           AWS.Tools.ARCZonalShift                       3.7.400.46 
						AWS Artifact                                          ART           AWS.Tools.Artifact                            3.7.400.46 
						Amazon Athena                                         ATH           AWS.Tools.Athena                              3.7.402.10 
						AWS Audit Manager                                     AUDM          AWS.Tools.AuditManager                        3.7.400.46 
						Amazon Augmented AI (A2I) Runtime                     A2IR          AWS.Tools.AugmentedAIRuntime                  3.7.400.46 
						AWS Auto Scaling                                      AS            AWS.Tools.AutoScaling                         3.7.404.0 
						AWS Auto Scaling Plans                                ASP           AWS.Tools.AutoScalingPlans                    3.7.400.46 
						AWS Health                                            HLTH          AWS.Tools.AWSHealth                           3.7.400.46 
						AWS Marketplace Commerce Analytics                    MCA           AWS.Tools.AWSMarketplaceCommerceAnalytics     3.7.400.46 
						AWS Marketplace Metering                              MM            AWS.Tools.AWSMarketplaceMetering              3.7.400.46 
						AWS Support                                           ASA           AWS.Tools.AWSSupport                          3.7.400.47 
						AWS B2B Data Interchange                              B2BI          AWS.Tools.B2bi                                3.7.401.18 
						AWS Backup                                            BAK           AWS.Tools.Backup                              3.7.401.33 
						AWS Backup Gateway                                    BUGW          AWS.Tools.BackupGateway                       3.7.400.46 
						AWS Batch                                             BAT           AWS.Tools.Batch                               3.7.402.2 
						AWSBillingAndCostManagementDataExports                BCMDE         AWS.Tools.BCMDataExports                      3.7.400.46 
						Amazon Bedrock                                        BDR           AWS.Tools.Bedrock                             3.7.409.4 
						Agents for Amazon Bedrock                             AAB           AWS.Tools.BedrockAgent                        3.7.409.0 
						Amazon Bedrock Agent Runtime                          BAR           AWS.Tools.BedrockAgentRuntime                 3.7.406.10 
						Amazon Bedrock Runtime                                BDRR          AWS.Tools.BedrockRuntime                      3.7.408.0 
						AWSBillingConductor                                   ABC           AWS.Tools.BillingConductor                    3.7.400.46 
						Amazon Braket                                         BRKT          AWS.Tools.Braket                              3.7.400.46 
						AWS Budgets                                           BGT           AWS.Tools.Budgets                             3.7.401.23 
						AWS Certificate Manager                               ACM           AWS.Tools.CertificateManager                  3.7.400.46 
						AWS Chatbot                                           CHAT          AWS.Tools.Chatbot                             3.7.402.21 
						Amazon Chime                                          CHM           AWS.Tools.Chime                               3.7.400.46 
						Amazon Chime SDK Identity                             CHMID         AWS.Tools.ChimeSDKIdentity                    3.7.400.46 
						Amazon Chime SDK Media Pipelines                      CHMMP         AWS.Tools.ChimeSDKMediaPipelines              3.7.400.46 
						Amazon Chime SDK Meetings                             CHMTG         AWS.Tools.ChimeSDKMeetings                    3.7.400.46 
						Amazon Chime SDK Messaging                            CHMMG         AWS.Tools.ChimeSDKMessaging                   3.7.400.46 
						Amazon Chime SDK Voice                                CHMVO         AWS.Tools.ChimeSDKVoice                       3.7.400.46 
						AWS Clean Rooms Service                               CRS           AWS.Tools.CleanRooms                          3.7.402.0 
						CleanRoomsML                                          CRML          AWS.Tools.CleanRoomsML                        3.7.401.0 
						AWS Cloud9                                            C9            AWS.Tools.Cloud9                              3.7.400.46 
						AWS Cloud Control API                                 CCA           AWS.Tools.CloudControlApi                     3.7.400.46 
						Amazon Cloud Directory                                CDIR          AWS.Tools.CloudDirectory                      3.7.400.47 
						AWS CloudFormation                                    CFN           AWS.Tools.CloudFormation                      3.7.400.46 
						Amazon CloudFront                                     CF            AWS.Tools.CloudFront                          3.7.400.46 
						Amazon CloudFront KeyValueStore                       CFKV          AWS.Tools.CloudFrontKeyValueStore             3.7.400.46 
						AWS CloudHSM V2                                       HSM2          AWS.Tools.CloudHSMV2                          3.7.400.46 
						Amazon CloudSearch                                    CS            AWS.Tools.CloudSearch                         3.7.400.47 
						Amazon CloudSearch Domain                             CSD           AWS.Tools.CloudSearchDomain                   3.7.400.46 
						AWS CloudTrail                                        CT            AWS.Tools.CloudTrail                          3.7.400.46 
						AWS CloudTrail Data Service                           CTD           AWS.Tools.CloudTrailData                      3.7.400.46 
						Amazon CloudWatch                                     CW            AWS.Tools.CloudWatch                          3.7.401.44 
						Amazon CloudWatch Evidently                           CWEVD         AWS.Tools.CloudWatchEvidently                 3.7.400.46 
						Amazon CloudWatch Logs                                CWL           AWS.Tools.CloudWatchLogs                      3.7.406.1 
						CloudWatch RUM                                        CWRUM         AWS.Tools.CloudWatchRUM                       3.7.400.46 
						AWS CodeArtifact                                      CA            AWS.Tools.CodeArtifact                        3.7.401.19 
						AWS CodeBuild                                         CB            AWS.Tools.CodeBuild                           3.7.406.1 
						AWS CodeCatalyst                                      CCAT          AWS.Tools.CodeCatalyst                        3.7.400.46 
						AWS CodeCommit                                        CC            AWS.Tools.CodeCommit                          3.7.401.46 
						AWS CodeConnections                                   CCON          AWS.Tools.CodeConnections                     3.7.401.25 
						AWS CodeDeploy                                        CD            AWS.Tools.CodeDeploy                          3.7.400.46 
						Amazon CodeGuru Profiler                              CGP           AWS.Tools.CodeGuruProfiler                    3.7.400.46 
						Amazon CodeGuru Reviewer                              CGR           AWS.Tools.CodeGuruReviewer                    3.7.400.46 
						Amazon CodeGuru Security                              CGS           AWS.Tools.CodeGuruSecurity                    3.7.400.46 
						AWS CodePipeline                                      CP            AWS.Tools.CodePipeline                        3.7.404.12 
						AWS CodeStar Connections                              CSTC          AWS.Tools.CodeStarconnections                 3.7.400.46 
						AWS CodeStar Notifications                            CSTN          AWS.Tools.CodeStarNotifications               3.7.400.46 
						Amazon Cognito Identity                               CGI           AWS.Tools.CognitoIdentity                     3.7.401.28 
						Amazon Cognito Identity Provider                      CGIP          AWS.Tools.CognitoIdentityProvider             3.7.403.27 
						Amazon Cognito Sync                                   CGIS          AWS.Tools.CognitoSync                         3.7.400.46 
						Amazon Comprehend                                     COMP          AWS.Tools.Comprehend                          3.7.400.46 
						AWS Comprehend Medical                                CMPM          AWS.Tools.ComprehendMedical                   3.7.400.46 
						AWS Compute Optimizer                                 CO            AWS.Tools.ComputeOptimizer                    3.7.400.47 
						AWS Config                                            CFG           AWS.Tools.ConfigService                       3.7.401.42 
						Amazon Connect Service                                CONN          AWS.Tools.Connect                             3.7.408.3 
						Amazon Connect Campaign Service                       CCS           AWS.Tools.ConnectCampaignService              3.7.400.46 
						Amazon Connect Cases                                  CCAS          AWS.Tools.ConnectCases                        3.7.400.46 
						Amazon Connect Contact Lens                           CCL           AWS.Tools.ConnectContactLens                  3.7.400.46 
						Amazon Connect Participant Service                    CONNP         AWS.Tools.ConnectParticipant                  3.7.400.46 
						Amazon Connect Wisdom Service                         WSDM          AWS.Tools.ConnectWisdomService                3.7.400.46 
						AWS Control Catalog                                   CLCAT         AWS.Tools.ControlCatalog                      3.7.401.44 
						AWS Control Tower                                     ACT           AWS.Tools.ControlTower                        3.7.400.47 
						AWS Cost and Usage Report                             CUR           AWS.Tools.CostAndUsageReport                  3.7.400.46 
						AWS Cost Explorer                                     CE            AWS.Tools.CostExplorer                        3.7.401.25 
						Cost Optimization Hub                                 COH           AWS.Tools.CostOptimizationHub                 3.7.401.43 
						Amazon Connect Customer Profiles                      CPF           AWS.Tools.CustomerProfiles                    3.7.401.20 
						AWS Database Migration Service                        DMS           AWS.Tools.DatabaseMigrationService            3.7.402.10 
						AWS Data Exchange                                     DTEX          AWS.Tools.DataExchange                        3.7.401.10 
						AWS Data Pipeline                                     DP            AWS.Tools.DataPipeline                        3.7.400.46 
						AWS DataSync                                          DSYN          AWS.Tools.DataSync                            3.7.401.3 
						Amazon DataZone                                       DZ            AWS.Tools.DataZone                            3.7.406.10 
						Amazon DynamoDB Accelerator (DAX)                     DAX           AWS.Tools.DAX                                 3.7.400.46 
						AWSDeadlineCloud                                      ADC           AWS.Tools.Deadline                            3.7.402.15 
						Amazon Detective                                      DTCT          AWS.Tools.Detective                           3.7.400.46 
						AWS Device Farm                                       DF            AWS.Tools.DeviceFarm                          3.7.401.34 
						Amazon DevOps Guru                                    DGURU         AWS.Tools.DevOpsGuru                          3.7.400.46 
						AWS Direct Connect                                    DC            AWS.Tools.DirectConnect                       3.7.400.46 
						AWS Directory Service                                 DS            AWS.Tools.DirectoryService                    3.7.401.25 
						AWS Directory Service Data                            DSD           AWS.Tools.DirectoryServiceData                3.7.400.25 
						Amazon Data Lifecycle Manager                         DLM           AWS.Tools.DLM                                 3.7.400.46 
						Amazon DocumentDB (with MongoDB compatibility)        DOC           AWS.Tools.DocDB                               3.7.401.40 
						Amazon DocumentDB Elastic Clusters                    DOCE          AWS.Tools.DocDBElastic                        3.7.401.1 
						Elastic Disaster Recovery Service                     EDRS          AWS.Tools.Drs                                 3.7.400.46 
						Amazon DynamoDB                                       DDB           AWS.Tools.DynamoDBv2                          3.7.402.10 
						Amazon EBS                                            EBS           AWS.Tools.EBS                                 3.7.400.46 
						Amazon Elastic Compute Cloud (EC2)                    EC2           AWS.Tools.EC2                                 3.7.414.3 
						AWS EC2 Instance Connect                              EC2IC         AWS.Tools.EC2InstanceConnect                  3.7.400.46 
						Amazon EC2 Container Registry                         ECR           AWS.Tools.ECR                                 3.7.404.25 
						Amazon Elastic Container Registry Public              ECRP          AWS.Tools.ECRPublic                           3.7.400.46 
						Amazon EC2 Container Service                          ECS           AWS.Tools.ECS                                 3.7.404.3 
						Amazon Elastic Container Service for Kubernetes       EKS           AWS.Tools.EKS                                 3.7.403.10 
						Amazon EKS Auth                                       EKSAU         AWS.Tools.EKSAuth                             3.7.400.46 
						Amazon ElastiCache                                    EC            AWS.Tools.ElastiCache                         3.7.401.14 
						AWS Elastic Beanstalk                                 EB            AWS.Tools.ElasticBeanstalk                    3.7.400.46 
						Amazon Elastic File System                            EFS           AWS.Tools.ElasticFileSystem                   3.7.400.46 
						Amazon Elastic Inference                              EI            AWS.Tools.ElasticInference                    3.7.400.47 
						Elastic Load Balancing                                ELB           AWS.Tools.ElasticLoadBalancing                3.7.401.44 
						Elastic Load Balancing V2                             ELB2          AWS.Tools.ElasticLoadBalancingV2              3.7.406.2 
						Amazon Elastic MapReduce                              EMR           AWS.Tools.ElasticMapReduce                    3.7.402.13 
						Amazon Elasticsearch                                  ES            AWS.Tools.Elasticsearch                       3.7.400.46 
						Amazon Elastic Transcoder                             ETS           AWS.Tools.ElasticTranscoder                   3.7.400.46 
						Amazon EMR Containers                                 EMRC          AWS.Tools.EMRContainers                       3.7.401.35 
						EMR Serverless                                        EMRServerless AWS.Tools.EMRServerless                       3.7.401.24 
						AWS EntityResolution                                  ERES          AWS.Tools.EntityResolution                    3.7.401.36 
						Amazon EventBridge                                    EVB           AWS.Tools.EventBridge                         3.7.401.44 
						FinSpace User Environment Management Service          FINSP         AWS.Tools.Finspace                            3.7.400.46 
						FinSpace Public API                                   FNSP          AWS.Tools.FinSpaceData                        3.7.400.46 
						AWS Fault Injection Simulator                         FIS           AWS.Tools.FIS                                 3.7.402.32 
						Firewall Management Service                           FMS           AWS.Tools.FMS                                 3.7.401.10 
						Amazon Forecast Query Service                         FRCQ          AWS.Tools.ForecastQueryService                3.7.400.46 
						Amazon Forecast Service                               FRC           AWS.Tools.ForecastService                     3.7.400.46 
						Amazon Fraud Detector                                 FD            AWS.Tools.FraudDetector                       3.7.400.46 
						AWS Free Tier                                         FT            AWS.Tools.FreeTier                            3.7.400.46 
						Amazon FSx                                            FSX           AWS.Tools.FSx                                 3.7.400.46 
						Amazon GameLift Service                               GML           AWS.Tools.GameLift                            3.7.401.31 
						Amazon Location Service Maps V2                       GEOM          AWS.Tools.GeoMaps                             3.7.400.3 
						Amazon Location Service Places V2                     GEOP          AWS.Tools.GeoPlaces                           3.7.400.3 
						Amazon Location Service Routes V2                     GEOR          AWS.Tools.GeoRoutes                           3.7.400.3 
						Amazon Glacier                                        GLC           AWS.Tools.Glacier                             3.7.400.46 
						AWS Global Accelerator                                GACL          AWS.Tools.GlobalAccelerator                   3.7.400.46 
						AWS Glue                                              GLUE          AWS.Tools.Glue                                3.7.409.2 
						AWS Glue DataBrew                                     GDB           AWS.Tools.GlueDataBrew                        3.7.400.46 
						AWS Greengrass                                        GG            AWS.Tools.Greengrass                          3.7.400.46 
						AWS GreengrassV2                                      GGV2          AWS.Tools.GreengrassV2                        3.7.400.46 
						AWS Ground Station                                    GS            AWS.Tools.GroundStation                       3.7.400.47 
						Amazon GuardDuty                                      GD            AWS.Tools.GuardDuty                           3.7.404.1 
						Amazon HealthLake                                     AHL           AWS.Tools.HealthLake                          3.7.400.46 
						IAM Roles Anywhere                                    IAMRA         AWS.Tools.IAMRolesAnywhere                    3.7.401.44 
						AWS Identity and Access Management                    IAM           AWS.Tools.IdentityManagement                  3.7.402.40 
						AWS Identity Store                                    IDS           AWS.Tools.IdentityStore                       3.7.400.46 
						EC2 Image Builder                                     EC2IB         AWS.Tools.Imagebuilder                        3.7.401.9 
						AWS Import/Export                                     IE            AWS.Tools.ImportExport                        3.7.400.46 
						Amazon Inspector                                      INS           AWS.Tools.Inspector                           3.7.400.46 
						Inspector2                                            INS2          AWS.Tools.Inspector2                          3.7.402.35 
						Inspector Scan                                        ISCAN         AWS.Tools.InspectorScan                       3.7.400.46 
						Amazon CloudWatch Internet Monitor                    CWIM          AWS.Tools.InternetMonitor                     3.7.401.34 
						AWS IoT                                               IOT           AWS.Tools.IoT                                 3.7.402.17 
						AWS IoT Core Device Advisor                           IOTDA         AWS.Tools.IoTDeviceAdvisor                    3.7.401.18 
						AWS IoT Events                                        IOTE          AWS.Tools.IoTEvents                           3.7.400.46 
						AWS IoT Events Data                                   IOTED         AWS.Tools.IoTEventsData                       3.7.400.46 
						AWS IoT Fleet Hub                                     IOTFH         AWS.Tools.IoTFleetHub                         3.7.400.46 
						AWS IoT FleetWise                                     IFW           AWS.Tools.IoTFleetWise                        3.7.402.4 
						AWS IoT Jobs Data Plane                               IOTJ          AWS.Tools.IoTJobsDataPlane                    3.7.400.46 
						AWS IoT Secure Tunneling                              IOTST         AWS.Tools.IoTSecureTunneling                  3.7.400.46 
						AWS IoT SiteWise                                      IOTSW         AWS.Tools.IoTSiteWise                         3.7.401.35 
						AWS IoT Things Graph                                  IOTTG         AWS.Tools.IoTThingsGraph                      3.7.400.46 
						AWS IoT TwinMaker                                     IOTTM         AWS.Tools.IoTTwinMaker                        3.7.400.46 
						AWS IoT Wireless                                      IOTW          AWS.Tools.IoTWireless                         3.7.400.46 
						Amazon Interactive Video Service                      IVS           AWS.Tools.IVS                                 3.7.401.11 
						Amazon Interactive Video Service Chat                 IVSC          AWS.Tools.Ivschat                             3.7.400.46 
						Amazon Interactive Video Service RealTime             IVSRT         AWS.Tools.IVSRealTime                         3.7.402.18 
						Amazon Managed Streaming for Apache Kafka (MSK)       MSK           AWS.Tools.Kafka                               3.7.401.29 
						Managed Streaming for Kafka Connect                   MSKC          AWS.Tools.KafkaConnect                        3.7.400.46 
						Amazon Kendra                                         KNDR          AWS.Tools.Kendra                              3.7.400.46 
						Amazon Kendra Intelligent Ranking                     KNRK          AWS.Tools.KendraRanking                       3.7.400.46 
						AWS Key Management Service                            KMS           AWS.Tools.KeyManagementService                3.7.400.46 
						Amazon Keyspaces                                      KS            AWS.Tools.Keyspaces                           3.7.401.3 
						Amazon Kinesis                                        KIN           AWS.Tools.Kinesis                             3.7.402.23 
						Amazon Kinesis Analytics V2                           KINA2         AWS.Tools.KinesisAnalyticsV2                  3.7.401.31 
						Amazon Kinesis Firehose                               KINF          AWS.Tools.KinesisFirehose                     3.7.400.46 
						Amazon Kinesis Video Streams                          KV            AWS.Tools.KinesisVideo                        3.7.400.46 
						Amazon Kinesis Video Streams Media                    KVM           AWS.Tools.KinesisVideoMedia                   3.7.400.46 
						Amazon Kinesis Video Signaling Channels               KVSC          AWS.Tools.KinesisVideoSignalingChannels       3.7.400.46 
						Amazon Kinesis Video WebRTC Storage                   KVWS          AWS.Tools.KinesisVideoWebRTCStorage           3.7.401.43 
						AWS Lake Formation                                    LKF           AWS.Tools.LakeFormation                       3.7.401.1 
						AWS Lambda                                            LM            AWS.Tools.Lambda                              3.7.406.6 
						AWS Launch Wizard                                     LWIZ          AWS.Tools.LaunchWizard                        3.7.400.46 
						Amazon Lex                                            LEX           AWS.Tools.Lex                                 3.7.400.46 
						Amazon Lex Model Building Service                     LMB           AWS.Tools.LexModelBuildingService             3.7.400.46 
						Amazon Lex Model Building V2                          LMBV2         AWS.Tools.LexModelsV2                         3.7.402.27 
						Amazon Lex Runtime V2                                 LRSV2         AWS.Tools.LexRuntimeV2                        3.7.400.46 
						AWS License Manager                                   LICM          AWS.Tools.LicenseManager                      3.7.400.46 
						AWS License Manager - Linux Subscriptions             LLMS          AWS.Tools.LicenseManagerLinuxSubscriptions    3.7.400.46 
						AWS License Manager User Subscription                 LMUS          AWS.Tools.LicenseManagerUserSubscriptions     3.7.400.46 
						Amazon Lightsail                                      LS            AWS.Tools.Lightsail                           3.7.400.46 
						Amazon Location Service                               LOC           AWS.Tools.LocationService                     3.7.400.46 
						Amazon Lookout for Equipment                          L4E           AWS.Tools.LookoutEquipment                    3.7.400.46 
						Amazon Lookout for Vision                             LFV           AWS.Tools.LookoutforVision                    3.7.400.46 
						Amazon Lookout for Metrics                            LOM           AWS.Tools.LookoutMetrics                      3.7.400.46 
						Amazon Machine Learning                               ML            AWS.Tools.MachineLearning                     3.7.400.46 
						Amazon Macie 2                                        MAC2          AWS.Tools.Macie2                              3.7.400.46 
						Amazon SES Mail Manager                               MMGR          AWS.Tools.MailManager                         3.7.402.12 
						M2                                                    AMM           AWS.Tools.MainframeModernization              3.7.401.9 
						Amazon Managed Blockchain                             MBC           AWS.Tools.ManagedBlockchain                   3.7.400.46 
						Amazon Managed Blockchain Query                       MBCQ          AWS.Tools.ManagedBlockchainQuery              3.7.400.46 
						Amazon Managed Grafana                                MGRF          AWS.Tools.ManagedGrafana                      3.7.400.46 
						AWS Marketplace Agreement Service                     MAS           AWS.Tools.MarketplaceAgreement                3.7.400.46 
						AWS Marketplace Catalog Service                       MCAT          AWS.Tools.MarketplaceCatalog                  3.7.400.46 
						AWS Marketplace Deployment Service                    MD            AWS.Tools.MarketplaceDeployment               3.7.400.46 
						AWS Marketplace Entitlement Service                   MES           AWS.Tools.MarketplaceEntitlementService       3.7.400.46 
						AWS Marketplace Reporting Service                     MR            AWS.Tools.MarketplaceReporting                3.7.400.17 
						AWS Elemental MediaConnect                            EMCN          AWS.Tools.MediaConnect                        3.7.401.33 
						AWS Elemental MediaConvert                            EMC           AWS.Tools.MediaConvert                        3.7.402.25 
						AWS Elemental MediaLive                               EML           AWS.Tools.MediaLive                           3.7.405.25 
						AWS Elemental MediaPackage                            EMP           AWS.Tools.MediaPackage                        3.7.400.46 
						AWS Elemental MediaPackage v2                         MPV2          AWS.Tools.MediaPackageV2                      3.7.402.5 
						AWS Elemental MediaPackage VOD                        EMPV          AWS.Tools.MediaPackageVod                     3.7.400.46 
						AWS Elemental MediaStore                              EMS           AWS.Tools.MediaStore                          3.7.400.46 
						AWS Elemental MediaStore Data Plane                   EMSD          AWS.Tools.MediaStoreData                      3.7.400.46 
						AWS Elemental MediaTailor                             EMT           AWS.Tools.MediaTailor                         3.7.400.46 
						Amazon Medical Imaging Service                        MIS           AWS.Tools.MedicalImaging                      3.7.400.46 
						Amazon MemoryDB                                       MDB           AWS.Tools.MemoryDB                            3.7.401.14 
						Application Migration Service                         MGN           AWS.Tools.Mgn                                 3.7.400.46 
						AWS Migration Hub                                     MH            AWS.Tools.MigrationHub                        3.7.400.46 
						AWS Migration Hub Config                              MHC           AWS.Tools.MigrationHubConfig                  3.7.400.46 
						AWS Migration Hub Orchestrator                        MHO           AWS.Tools.MigrationHubOrchestrator            3.7.400.46 
						AWS Migration Hub Refactor Spaces                     MHRS          AWS.Tools.MigrationHubRefactorSpaces          3.7.400.46 
						Migration Hub Strategy Recommendations                MHS           AWS.Tools.MigrationHubStrategyRecommendations 3.7.400.46 
						Amazon MQ                                             MQ            AWS.Tools.MQ                                  3.7.400.46 
						Amazon MTurk Service                                  MTR           AWS.Tools.MTurk                               3.7.400.46 
						AmazonMWAA                                            MWAA          AWS.Tools.MWAA                                3.7.401.8 
						Amazon Neptune                                        NPT           AWS.Tools.Neptune                             3.7.401.24 
						Amazon NeptuneData                                    NEPT          AWS.Tools.Neptunedata                         3.7.400.46 
						Amazon Neptune Graph                                  NEPTG         AWS.Tools.NeptuneGraph                        3.7.402.13 
						AWS Network Firewall                                  NWFW          AWS.Tools.NetworkFirewall                     3.7.402.3 
						AWS Network Manager                                   NMGR          AWS.Tools.NetworkManager                      3.7.400.46 
						Amazon CloudWatch Network Monitor                     CWNM          AWS.Tools.NetworkMonitor                      3.7.400.46 
						CloudWatch Observability Access Manager               CWOAM         AWS.Tools.OAM                                 3.7.400.46 
						Amazon Omics                                          OMICS         AWS.Tools.Omics                               3.7.401.35 
						OpenSearch Serverless                                 OSS           AWS.Tools.OpenSearchServerless                3.7.402.3 
						Amazon OpenSearch Service                             OS            AWS.Tools.OpenSearchService                   3.7.402.3 
						AWS OpsWorks                                          OPS           AWS.Tools.OpsWorks                            3.7.400.46 
						AWS OpsWorksCM                                        OWCM          AWS.Tools.OpsWorksCM                          3.7.400.46 
						AWS Organizations                                     ORG           AWS.Tools.Organizations                       3.7.402.21 
						Amazon OpenSearch Ingestion                           OSIS          AWS.Tools.OSIS                                3.7.400.46 
						AWS Outposts                                          OUTP          AWS.Tools.Outposts                            3.7.402.13 
						AWS Panorama                                          PAN           AWS.Tools.Panorama                            3.7.400.46 
						Payment Cryptography Control Plane                    PAYCC         AWS.Tools.PaymentCryptography                 3.7.401.8 
						Payment Cryptography Data                             PAYCD         AWS.Tools.PaymentCryptographyData             3.7.402.8 
						Pca Connector Ad                                      PCAAD         AWS.Tools.PcaConnectorAd                      3.7.400.46 
						Private CA Connector for SCEP                         PCASCEP       AWS.Tools.PcaConnectorScep                    3.7.400.46 
						AWS Parallel Computing Service                        PCS           AWS.Tools.PCS                                 3.7.400.34 
						AWS Personalize                                       PERS          AWS.Tools.Personalize                         3.7.401.34 
						Amazon Personalize Events                             PERSE         AWS.Tools.PersonalizeEvents                   3.7.400.46 
						Amazon Personalize Runtime                            PERSR         AWS.Tools.PersonalizeRuntime                  3.7.400.46 
						AWS Performance Insights                              PI            AWS.Tools.PI                                  3.7.400.46 
						Amazon Pinpoint                                       PIN           AWS.Tools.Pinpoint                            3.7.400.46 
						Amazon Pinpoint Email                                 PINE          AWS.Tools.PinpointEmail                       3.7.400.46 
						Amazon Pinpoint SMS Voice V2                          SMSV          AWS.Tools.PinpointSMSVoiceV2                  3.7.402.10 
						Amazon EventBridge Pipes                              PIPES         AWS.Tools.Pipes                               3.7.402.10 
						Amazon Polly                                          POL           AWS.Tools.Polly                               3.7.401.35 
						AWS Price List Service                                PLS           AWS.Tools.Pricing                             3.7.400.47 
						AWS Private 5G                                        PV5G          AWS.Tools.Private5G                           3.7.400.46 
						Amazon Prometheus Service                             PROM          AWS.Tools.PrometheusService                   3.7.401.2 
						AWS Proton                                            PRO           AWS.Tools.Proton                              3.7.400.46 
						Amazon Q Apps                                         qapps         AWS.Tools.QApps                               3.7.402.1 
						Amazon QBusiness                                      QBUS          AWS.Tools.QBusiness                           3.7.403.7 
						Amazon Q Connect                                      QC            AWS.Tools.QConnect                            3.7.401.15 
						Amazon QLDB                                           QLDB          AWS.Tools.QLDB                                3.7.400.46 
						Amazon QLDB Session                                   QLDBS         AWS.Tools.QLDBSession                         3.7.400.46 
						Amazon QuickSight                                     QS            AWS.Tools.QuickSight                          3.7.408.0 
						AWS Resource Access Manager (RAM)                     RAM           AWS.Tools.RAM                                 3.7.400.46 
						Amazon Relational Database Service                    RDS           AWS.Tools.RDS                                 3.7.406.5 
						AWS RDS DataService                                   RDSD          AWS.Tools.RDSDataService                      3.7.400.46 
						Amazon Recycle Bin                                    RBIN          AWS.Tools.RecycleBin                          3.7.400.46 
						Amazon Redshift                                       RS            AWS.Tools.Redshift                            3.7.403.3 
						Redshift Data API Service                             RSD           AWS.Tools.RedshiftDataAPIService              3.7.402.4 
						Redshift Serverless                                   RSS           AWS.Tools.RedshiftServerless                  3.7.401.3 
						Amazon Rekognition                                    REK           AWS.Tools.Rekognition                         3.7.400.46 
						AWS re:Post Private                                   RESP          AWS.Tools.Repostspace                         3.7.401.9 
						AWS Resilience Hub                                    RESH          AWS.Tools.ResilienceHub                       3.7.402.11 
						AWS Resource Explorer                                 AREX          AWS.Tools.ResourceExplorer2                   3.7.402.0 
						AWS Resource Groups                                   RG            AWS.Tools.ResourceGroups                      3.7.401.20 
						AWS Resource Groups Tagging API                       RGT           AWS.Tools.ResourceGroupsTaggingAPI            3.7.400.46 
						AWS RoboMaker                                         ROBO          AWS.Tools.RoboMaker                           3.7.400.47 
						Amazon Route 53                                       R53           AWS.Tools.Route53                             3.7.403.3 
						Amazon Route 53 Domains                               R53D          AWS.Tools.Route53Domains                      3.7.400.46 
						Amazon Route 53 Profiles                              R53P          AWS.Tools.Route53Profiles                     3.7.400.46 
						Route53 Recovery Cluster                              RRC           AWS.Tools.Route53RecoveryCluster              3.7.400.46 
						AWS Route53 Recovery Control Config                   R53RC         AWS.Tools.Route53RecoveryControlConfig        3.7.400.46 
						AWS Route53 Recovery Readiness                        PD            AWS.Tools.Route53RecoveryReadiness            3.7.400.46 
						Amazon Route 53 Resolver                              R53R          AWS.Tools.Route53Resolver                     3.7.401.13 
						Amazon Simple Storage Service (S3)                    S3            AWS.Tools.S3                                  3.7.405.10 
						Amazon S3 Control                                     S3C           AWS.Tools.S3Control                           3.7.402.1 
						Amazon S3 Outposts                                    S3O           AWS.Tools.S3Outposts                          3.7.400.46 
						Amazon SageMaker Service                              SM            AWS.Tools.SageMaker                           3.7.414.2 
						Amazon Sagemaker Edge Manager                         SME           AWS.Tools.SagemakerEdgeManager                3.7.400.46 
						Amazon SageMaker Feature Store Runtime                SMFS          AWS.Tools.SageMakerFeatureStoreRuntime        3.7.400.46 
						SageMaker Geospatial                                  SMGS          AWS.Tools.SageMakerGeospatial                 3.7.400.46 
						Amazon SageMaker Metrics Service                      SMM           AWS.Tools.SageMakerMetrics                    3.7.401.24 
						Amazon SageMaker Runtime                              SMR           AWS.Tools.SageMakerRuntime                    3.7.401.29 
						AWS Savings Plans                                     SP            AWS.Tools.SavingsPlans                        3.7.400.46 
						Amazon EventBridge Scheduler                          SCH           AWS.Tools.Scheduler                           3.7.400.46 
						Amazon EventBridge Schema Registry                    SCHM          AWS.Tools.Schemas                             3.7.400.46 
						AWS Secrets Manager                                   SEC           AWS.Tools.SecretsManager                      3.7.400.46 
						AWS Security Hub                                      SHUB          AWS.Tools.SecurityHub                         3.7.401.36 
						Amazon Security Lake                                  SLK           AWS.Tools.SecurityLake                        3.7.401.12 
						AWS Security Token Service (STS)                      STS           AWS.Tools.SecurityToken                       3.7.400.46 
						AWS Serverless Application Repository                 SAR           AWS.Tools.ServerlessApplicationRepository     3.7.400.46 
						AWS Server Migration Service                          SMS           AWS.Tools.ServerMigrationService              3.7.400.46 
						AWS Service Catalog                                   SC            AWS.Tools.ServiceCatalog                      3.7.400.46 
						AWS Cloud Map                                         SD            AWS.Tools.ServiceDiscovery                    3.7.400.46 
						AWS Service Quotas                                    SQ            AWS.Tools.ServiceQuotas                       3.7.400.46 
						AWS Shield                                            SHLD          AWS.Tools.Shield                              3.7.400.46 
						Amazon Simple Email Service (SES)                     SES           AWS.Tools.SimpleEmail                         3.7.401.36 
						Amazon Simple Email Service V2 (SES V2)               SES2          AWS.Tools.SimpleEmailV2                       3.7.404.2 
						Amazon Simple Notification Service (SNS)              SNS           AWS.Tools.SimpleNotificationService           3.7.400.46 
						AWS Systems Manager                                   SSM           AWS.Tools.SimpleSystemsManagement             3.7.402.25 
						AWS Simple Workflow Service (SWF)                     SWF           AWS.Tools.SimpleWorkflow                      3.7.400.46 
						AWS SimSpace Weaver                                   SSW           AWS.Tools.SimSpaceWeaver                      3.7.400.46 
						AWS Import/Export Snowball                            SNOW          AWS.Tools.Snowball                            3.7.400.46 
						AWS Snow Device Management                            SDMS          AWS.Tools.SnowDeviceManagement                3.7.400.46 
						AWS End User Messaging Social                         SOCIAL        AWS.Tools.SocialMessaging                     3.7.400.13 
						Amazon Simple Queue Service (SQS)                     SQS           AWS.Tools.SQS                                 3.7.400.46 
						AWS Systems Manager Incident Manager Contacts         SMC           AWS.Tools.SSMContacts                         3.7.400.46 
						AWS Systems Manager Incident Manager                  SSMI          AWS.Tools.SSMIncidents                        3.7.400.46 
						AWS Systems Manager QuickSetup                        SSMQS         AWS.Tools.SSMQuickSetup                       3.7.400.44 
						AWS Systems Manager for SAP                           SMSAP         AWS.Tools.SsmSap                              3.7.401.38 
						AWS Single Sign-On                                    SSO           AWS.Tools.SSO                                 3.7.400.46 
						AWS Single Sign-On Admin                              SSOADMN       AWS.Tools.SSOAdmin                            3.7.400.46 
						AWS Single Sign-On OIDC                               SSOOIDC       AWS.Tools.SSOOIDC                             3.7.400.46 
						AWS Step Functions                                    SFN           AWS.Tools.StepFunctions                       3.7.402.34 
						AWS Storage Gateway                                   SG            AWS.Tools.StorageGateway                      3.7.401.27 
						AWS Supply Chain                                      SUPCH         AWS.Tools.SupplyChain                         3.7.403.6 
						AWS Support App                                       SUP           AWS.Tools.SupportApp                          3.7.400.46 
						Amazon CloudWatch Synthetics                          CWSYN         AWS.Tools.Synthetics                          3.7.402.0 
						AWS Tax Settings                                      TSA           AWS.Tools.TaxSettings                         3.7.401.1 
						Amazon Textract                                       TXT           AWS.Tools.Textract                            3.7.400.46 
						Amazon Timestream InfluxDB                            TIDB          AWS.Tools.TimestreamInfluxDB                  3.7.403.13 
						Amazon Timestream Query                               TSQ           AWS.Tools.TimestreamQuery                     3.7.401.9 
						Amazon Timestream Write                               TSW           AWS.Tools.TimestreamWrite                     3.7.400.46 
						AWS Telco Network Builder                             TNB           AWS.Tools.Tnb                                 3.7.401.44 
						Amazon Transcribe Service                             TRS           AWS.Tools.TranscribeService                   3.7.400.46 
						AWS Transfer for SFTP                                 TFR           AWS.Tools.Transfer                            3.7.401.12 
						Amazon Translate                                      TRN           AWS.Tools.Translate                           3.7.400.46 
						Trusted Advisor                                       TA            AWS.Tools.TrustedAdvisor                      3.7.400.46 
						Amazon Verified Permissions                           AVP           AWS.Tools.VerifiedPermissions                 3.7.401.1 
						Amazon Voice ID                                       VID           AWS.Tools.VoiceID                             3.7.400.46 
						VPC Lattice                                           VPCL          AWS.Tools.VPCLattice                          3.7.400.46 
						AWS WAF                                               WAF           AWS.Tools.WAF                                 3.7.400.46 
						AWS WAF Regional                                      WAFR          AWS.Tools.WAFRegional                         3.7.401.44 
						AWS WAF V2                                            WAF2          AWS.Tools.WAFV2                               3.7.402.10 
						AWS Well-Architected Tool                             WAT           AWS.Tools.WellArchitected                     3.7.400.46 
						Amazon WorkDocs                                       WD            AWS.Tools.WorkDocs                            3.7.400.46 
						Amazon WorkMail                                       WM            AWS.Tools.WorkMail                            3.7.401.3 
						Amazon WorkMail Message Flow                          WMMF          AWS.Tools.WorkMailMessageFlow                 3.7.400.46 
						Amazon WorkSpaces                                     WKS           AWS.Tools.WorkSpaces                          3.7.404.10 
						Amazon WorkSpaces Thin Client                         WSTC          AWS.Tools.WorkSpacesThinClient                3.7.400.46 
						Amazon WorkSpaces Web                                 WSW           AWS.Tools.WorkSpacesWeb                       3.7.401.25 
						AWS X-Ray                                             XR            AWS.Tools.XRay                                3.7.400.46 
						 
						PS C:\_TERRAFORM\_AWSCore&gt; 
					 
				
			
		
	



	You should store your AWS credentials as a set of credentials on the credential store(s) on your machine. It is a best practice to avoid exposing your credentials - do not put literal credentials in the PowerShell commands. 
	To store your credentials, use the PowerShell snippet Set-AWSCredential:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\_TERRAFORM\_AWSCore&gt; Set-AWSCredential -AccessKey AK...Q -SecretKey lR...J -StoreAs AWC
					 
					PS C:\_TERRAFORM\_AWSCore&gt; Get-AWSCredential -ListProfileDetail 
					 
					ProfileName StoreTypeName         ProfileLocation 
					----------- -------------         --------------- 
					default     NetSDKCredentialsFile 
					AWC         NetSDKCredentialsFile 
					default     SharedCredentialsFile C:\Users\Gerhard\.aws\credentials 
					 
					PS C:\_TERRAFORM\_AWSCore&gt;
				
			
		
	
	 
	 



	
		Caution:
	 

	
		AWS credentials stored in the AWS SDK store are encrypted with the logged-in Windows user identity. They cannot be decrypted by using another account or on a device other than the one on which they were originally created. 
		 
	 



	To set the credentials for a PowerShell session, use the Set-AWSCredential snippet:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	



	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\_TERRAFORM\_AWSCore&gt; Set-AWSCredential -ProfileName AWC 
						PS C:\_TERRAFORM\_AWSCore&gt; 
					 
				
			
		
	



	 
 


	Deploy an Amazon VPC



	An Amazon Virtual Private Cloud (VPC) enables you to launch AWS resources into a virtual network that you have defined. 
	 
 


	
		Important: 
	 

	
		It is possible to have multiple VPCs in your AWS tenant. Create a dedicated VPC for your Citrix DaaS deployment. Tag all resources for easier management. The default VPC holds these IP address ranges: 172.31.0.0/16 
		 
	 



	Using a PowerShell snippet, you can check for existing VPCs:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	



	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					PS C:\_TERRAFORM\_AWSCore&gt;Get-EC2Vpc 
					 
					 
					CidrBlock                   : 172.31.0.0/16 
					CidrBlockAssociationSet     : {vpc-cidr-assoc-09e0d958d1c0af892} 
					DhcpOptionsId               : dopt-0a71f0bb911106c5f 
					InstanceTenancy             : default 
					Ipv6CidrBlockAssociationSet : {} 
					IsDefault                   : False 
					OwnerId                     : 968334184707 
					State                       : available 
					Tags                        : {Name} 
					VpcId                       : vpc-0a0c4e01213dca45a 
					 
					 
					PS C:\_TERRAFORM\_AWSCore&gt;
				
			
		
	



	There is already an existing VPC, but that does not matter. 
	In this guide, we will deploy a new VPC with all needed entities to show the possible coexistence with an existing environment.
 


	Use Terraform to create a new VPC and the required DHCP options:
 


	
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #1e1e1e;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}	
	
		
			
				
					
						
							## Create a VPC
						 

						
							resource "aws_vpc" "AWC-VPC" {
						 

						
							  cidr_block            = "${var.AWC-VPC-CIDR}"
						 

						
							  instance_tenancy      = "default"
						 

						
							  enable_dns_support    = "true"
						 

						
							  enable_dns_hostnames  = "true"
						 

						
							  tags                  = {
						 

						
							                              Name = "AWC-TF-VPC"
						 

						
							  }
						 

						
							}
						 

						
							 
						 

						
							### Create the DHCP options
						 

						
							resource "aws_vpc_dhcp_options" "AWC-DHCP" {
						 

						
							  domain_name                       = "${var.AWC-DHCP-DomainName}"
						 

						
							  domain_name_servers               = "${var.AWC-DHCP-DNS1}"
						 

						
							  ipv6_address_preferred_lease_time = 1440
						 

						
							  ntp_servers                       = "${var.AWC-DHCP-DNS1}"
						 

						
							  netbios_name_servers              = "${var.AWC-DHCP-DNS1}"
						 

						
							  netbios_node_type                 = 1
						 

						
							  tags                  = {
						 

						
							                              Name = "AWC-TF-DHCP"
						 

						
							  }
						 

						
							}
						 
					
				
			
		
	



	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	



	 
	Terraform will now create these entities:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\_TERRAFORM\_AWSCore&gt; terraform init 
						Initializing the backend... 
						Initializing provider plugins... 
						- Finding citrix/citrix versions matching "1.0.6"... 
						- Finding hashicorp/aws versions matching "&gt;= 5.4.0"... 
						- Finding latest version of hashicorp/local... 
						- Installing citrix/citrix v1.0.6... 
						- Installed citrix/citrix v1.0.6 (self-signed, key ID BD4BD0E690CB7D88) 
						- Installing hashicorp/aws v5.73.0... 
						- Installed hashicorp/aws v5.73.0 (signed by HashiCorp) 
						- Installing hashicorp/local v2.5.2... 
						- Installed hashicorp/local v2.5.2 (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:\_TERRAFORM\_AWSCore&gt; terraform plan
					 

					
						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_vpc.AWC-VPC will be created 
						  + resource "aws_vpc" "AWC-VPC" { 
						      + arn                                  = (known after apply) 
						      + cidr_block                           = "172.31.0.0/16" 
						      + default_network_acl_id               = (known after apply) 
						      + default_route_table_id               = (known after apply) 
						      + default_security_group_id            = (known after apply) 
						      + dhcp_options_id                      = (known after apply) 
						      + enable_dns_hostnames                 = true 
						      + enable_dns_support                   = true 
						      + enable_network_address_usage_metrics = (known after apply) 
						      + id                                   = (known after apply) 
						      + instance_tenancy                     = "default" 
						      + ipv6_association_id                  = (known after apply) 
						      + ipv6_cidr_block                      = (known after apply) 
						      + ipv6_cidr_block_network_border_group = (known after apply) 
						      + main_route_table_id                  = (known after apply) 
						      + owner_id                             = (known after apply) 
						      + tags                                 = { 
						          + "Name" = "AWC-TF-VPC" 
						        } 
						      + tags_all                             = { 
						          + "Name" = "AWC-TF-VPC" 
						        } 
						    } 
						 
						  # aws_vpc_dhcp_options.AWC-DHCP will be created 
						  + resource "aws_vpc_dhcp_options" "AWC-DHCP" { 
						      + arn                               = (known after apply) 
						      + domain_name                       = "pseaws.lab" 
						      + domain_name_servers               = [ 
						          + "172.31.20.19", 
						        ] 
						      + id                                = (known after apply) 
						      + ipv6_address_preferred_lease_time = "1440" 
						      + netbios_name_servers              = [ 
						          + "172.31.20.19", 
						        ] 
						      + netbios_node_type                 = "1" 
						      + ntp_servers                       = [ 
						          + "172.31.20.19", 
						        ] 
						      + owner_id                          = (known after apply) 
						      + tags                              = { 
						          + "Name" = "AWC-TF-DHCP" 
						        } 
						      + tags_all                          = { 
						          + "Name" = "AWC-TF-DHCP" 
						        } 
						    } 
						 
						Plan: 2 to add, 0 to change, 0 to destroy. 
						 
						─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 
						 
						PS C:\_TERRAFORM\_AWSCore&gt; terraform apply 
						 
						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_vpc.AWC-VPC will be created 
						  + resource "aws_vpc" "AWC-VPC" { 
						      + arn                                  = (known after apply) 
						      + cidr_block                           = "172.31.0.0/16" 
						      + default_network_acl_id               = (known after apply) 
						      + default_route_table_id               = (known after apply) 
						      + default_security_group_id            = (known after apply) 
						      + dhcp_options_id                      = (known after apply) 
						      + enable_dns_hostnames                 = true 
						      + enable_dns_support                   = true 
						      + enable_network_address_usage_metrics = (known after apply) 
						      + id                                   = (known after apply) 
						      + instance_tenancy                     = "default" 
						      + ipv6_association_id                  = (known after apply) 
						      + ipv6_cidr_block                      = (known after apply) 
						      + ipv6_cidr_block_network_border_group = (known after apply) 
						      + main_route_table_id                  = (known after apply) 
						      + owner_id                             = (known after apply) 
						      + tags                                 = { 
						          + "Name" = "AWC-TF-VPC" 
						        } 
						      + tags_all                             = { 
						          + "Name" = "AWC-TF-VPC" 
						        } 
						    } 
						 
						  # aws_vpc_dhcp_options.AWC-DHCP will be created 
						  + resource "aws_vpc_dhcp_options" "AWC-DHCP" { 
						      + arn                               = (known after apply) 
						      + domain_name                       = "pseaws.lab" 
						      + domain_name_servers               = [ 
						          + "172.31.20.19", 
						        ] 
						      + id                                = (known after apply) 
						      + ipv6_address_preferred_lease_time = "1440" 
						      + netbios_name_servers              = [ 
						          + "172.31.20.19", 
						        ] 
						      + netbios_node_type                 = "1" 
						      + ntp_servers                       = [ 
						          + "172.31.20.19", 
						        ] 
						      + owner_id                          = (known after apply) 
						      + tags                              = { 
						          + "Name" = "AWC-TF-DHCP" 
						        } 
						      + tags_all                          = { 
						          + "Name" = "AWC-TF-DHCP" 
						        } 
						    } 
						 
						Plan: 2 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_vpc.AWC-VPC: Creating... 
						aws_vpc_dhcp_options.AWC-DHCP: Creation complete after 1s [id=dopt-0c26e5d5880c8b208] 
						aws_vpc.AWC-VPC: Still creating... [10s elapsed] 
						aws_vpc.AWC-VPC: Creation complete after 11s [id=vpc-0a0c4e01213dca45a] 
						 
						 
						Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
					 
					PS C:\_TERRAFORM\_AWSCore&gt;  
					 
				
			
		
	



	Terraform successfully created the new VPC and its DHCP options.
 


	
		Note: 
	 

	
		For more information about VPCs and DHCP options, please visit Amazon´s Product Documentation pages. 
		 
	 



	 
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	



	Deploy Subnets, Internet Gateways, NAT Gateways, Route Tables and Security Groups



	After creating the VPC, we deploy all needed network components, such as Subnets, Internet Gateways, NAT Gateways, and Router Tables.  
	 
 


	Deploying the Subnets



	A Subnet is a range of IP addresses in your VPC. 
	You can create AWS resources in specific subnets, such as EC2 instances.
 


	The Subnet type is determined by how you configure routing for your Subnets:
 


	
		Public Subnet: The Subnet has a direct route to an Internet Gateway. 
		Resources in a Public Subnet can directly access the Internet.
	
	
		Private Subnet: The Subnet does not have a direct route to an Internet Gateway. Resources in a Private Subnet require a NAT device to access the Internet. 
		 
	



	
		Important: 
	 

	
		Each Subnet must be associated with a Route Table, specifying all allowed outbound traffic routes. Every Subnet is automatically associated with the main Route Table for the VPC. 
		Disable the Auto-assign IP settings, as otherwise, a public IPv4 address is automatically requested for each new Network Interface in this Subnet. 
		 
	 



	Use Terraform to create the Subnets:
 


	
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #1e1e1e;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #1e1e1e;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}	
	
		
			
				
					
						
							### Create a Public Subnet #1
						 

						
							resource "aws_subnet" "AWC-Subnet-Pub-1" {
						 

						
							  vpc_id                  = aws_vpc.AWC-VPC.id
						 

						
							  cidr_block              = "${var.AWC-Subnet-Pub-CIDR-1}"
						 

						
							  availability_zone       = "${var.AWC-AvailibilityRegion-1}"
						 

						
							  map_public_ip_on_launch = false
						 

						
							  tags                    = {
						 

						
							                              Name = "AWC-TF-PubSubnet1"
						 

						
							  }
						 

						
							}
						 

						
							 
						 

						
							### Create a Public Subnet #2
						 

						
							resource "aws_subnet" "AWC-Subnet-Pub-2" {
						 

						
							  vpc_id                  = aws_vpc.AWC-VPC.id
						 

						
							  cidr_block              = "${var.AWC-Subnet-Pub-CIDR-2}"
						 

						
							  availability_zone       = "${var.AWC-AvailibilityRegion-2}"
						 

						
							  map_public_ip_on_launch = false
						 

						
							  tags                    = {
						 

						
							                              Name = "AWC-TF-PubSubnet2"
						 

						
							  }
						 

						
							}
						 

						
							 
						 

						
							### Create a Private Subnet #1
						 

						
							resource "aws_subnet" "AWC-Subnet-Priv-1" {
						 

						
							  vpc_id                  = aws_vpc.AWC-VPC.id
						 

						
							  cidr_block              = "${var.AWC-Subnet-Priv-CIDR-1}"
						 

						
							  availability_zone       = "${var.AWC-AvailibilityRegion-1}"
						 

						
							  map_public_ip_on_launch = false
						 

						
							  tags                    = {
						 

						
							                              Name = "AWC-TF-PrivSubnet1"
						 

						
							  }
						 

						
							}
						 

						
							 
						 

						
							### Create a Private Subnet #2
						 

						
							resource "aws_subnet" "AWC-Subnet-Priv-2" {
						 

						
							  vpc_id                  = aws_vpc.AWC-VPC.id
						 

						
							  cidr_block              = "${var.AWC-Subnet-Priv-CIDR-2}"
						 

						
							  availability_zone       = "${var.AWC-AvailibilityRegion-2}"
						 

						
							  map_public_ip_on_launch = false
						 

						
							  tags                    = {
						 

						
							                              Name = "AWC-TF-PrivSubnet2"
						 

						
							  }
						 
						}
					
				
			
		
	



	 
	Terraform will now create these entities:
 


	
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #1e1e1e;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #1e1e1e;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}	



	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\_TERRAFORM\_AWSCore&gt; terraform plan
					 

					
						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_subnet.AWC-Subnet-Priv-1 will be created 
						  + resource "aws_subnet" "AWC-Subnet-Priv-1" { 
						      + arn                                            = (known after apply) 
						      + assign_ipv6_address_on_creation                = false 
						      + availability_zone                              = (sensitive value) 
						      + availability_zone_id                           = (known after apply) 
						      + cidr_block                                     = "172.31.5.0/24" 
						      + enable_dns64                                   = false 
						      + enable_resource_name_dns_a_record_on_launch    = false 
						      + enable_resource_name_dns_aaaa_record_on_launch = false 
						      + id                                             = (known after apply) 
						      + ipv6_cidr_block_association_id                 = (known after apply) 
						      + ipv6_native                                    = false 
						      + map_public_ip_on_launch                        = false 
						      + owner_id                                       = (known after apply) 
						      + private_dns_hostname_type_on_launch            = (known after apply) 
						      + tags                                           = { 
						          + "Name" = "AWC-TF-PrivSubnet1" 
						        } 
						      + tags_all                                       = { 
						          + "Name" = "AWC-TF-PrivSubnet1" 
						        } 
						      + vpc_id                                         = (known after apply) 
						    } 
						 
						  # aws_subnet.AWC-Subnet-Priv-2 will be created 
						  + resource "aws_subnet" "AWC-Subnet-Priv-2" { 
						      + arn                                            = (known after apply) 
						      + assign_ipv6_address_on_creation                = false 
						      + availability_zone                              = (sensitive value) 
						      + availability_zone_id                           = (known after apply) 
						      + cidr_block                                     = "172.31.6.0/24" 
						      + enable_dns64                                   = false 
						      + enable_resource_name_dns_a_record_on_launch    = false 
						      + enable_resource_name_dns_aaaa_record_on_launch = false 
						      + id                                             = (known after apply) 
						      + ipv6_cidr_block_association_id                 = (known after apply) 
						      + ipv6_native                                    = false 
						      + map_public_ip_on_launch                        = false 
						      + owner_id                                       = (known after apply) 
						      + private_dns_hostname_type_on_launch            = (known after apply) 
						      + tags                                           = { 
						          + "Name" = "AWC-TF-PrivSubnet2" 
						        } 
						      + tags_all                                       = { 
						          + "Name" = "AWC-TF-PrivSubnet2" 
						        } 
						      + vpc_id                                         = (known after apply) 
						    } 
						 
						  # aws_subnet.AWC-Subnet-Pub-1 will be created 
						  + resource "aws_subnet" "AWC-Subnet-Pub-1" { 
						      + arn                                            = (known after apply) 
						      + assign_ipv6_address_on_creation                = false 
						      + availability_zone                              = (sensitive value) 
						      + availability_zone_id                           = (known after apply) 
						      + cidr_block                                     = "172.31.1.0/24" 
						      + enable_dns64                                   = false 
						      + enable_resource_name_dns_a_record_on_launch    = false 
						      + enable_resource_name_dns_aaaa_record_on_launch = false 
						      + id                                             = (known after apply) 
						      + ipv6_cidr_block_association_id                 = (known after apply) 
						      + ipv6_native                                    = false 
						      + map_public_ip_on_launch                        = false 
						      + owner_id                                       = (known after apply) 
						      + private_dns_hostname_type_on_launch            = (known after apply) 
						      + tags                                           = { 
						          + "Name" = "AWC-TF-PubSubnet1" 
						        } 
						      + tags_all                                       = { 
						          + "Name" = "AWC-TF-PubSubnet1" 
						        } 
						      + vpc_id                                         = (known after apply) 
						    } 
						 
						  # aws_subnet.AWC-Subnet-Pub-2 will be created 
						  + resource "aws_subnet" "AWC-Subnet-Pub-2" { 
						      + arn                                            = (known after apply) 
						      + assign_ipv6_address_on_creation                = false 
						      + availability_zone                              = (sensitive value) 
						      + availability_zone_id                           = (known after apply) 
						      + cidr_block                                     = "172.31.2.0/24" 
						      + enable_dns64                                   = false 
						      + enable_resource_name_dns_a_record_on_launch    = false 
						      + enable_resource_name_dns_aaaa_record_on_launch = false 
						      + id                                             = (known after apply) 
						      + ipv6_cidr_block_association_id                 = (known after apply) 
						      + ipv6_native                                    = false 
						      + map_public_ip_on_launch                        = false 
						      + owner_id                                       = (known after apply) 
						      + private_dns_hostname_type_on_launch            = (known after apply) 
						      + tags                                           = { 
						          + "Name" = "AWC-TF-PubSubnet2" 
						        } 
						      + tags_all                                       = { 
						          + "Name" = "AWC-TF-PubSubnet2" 
						        } 
						      + vpc_id                                         = (known after apply) 
						    } 
						 
					 

					
						Plan: 4 to add, 0 to change, 0 to destroy. 
						 
						─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 
						 
						PS C:\_TERRAFORM\_AWSCore&gt; terraform apply 
						 
						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_subnet.AWC-Subnet-Priv-1 will be created 
						  + resource "aws_subnet" "AWC-Subnet-Priv-1" { 
						      + arn                                            = (known after apply) 
						      + assign_ipv6_address_on_creation                = false 
						      + availability_zone                              = (sensitive value) 
						      + availability_zone_id                           = (known after apply) 
						      + cidr_block                                     = "172.31.5.0/24" 
						      + enable_dns64                                   = false 
						      + enable_resource_name_dns_a_record_on_launch    = false 
						      + enable_resource_name_dns_aaaa_record_on_launch = false 
						      + id                                             = (known after apply) 
						      + ipv6_cidr_block_association_id                 = (known after apply) 
						      + ipv6_native                                    = false 
						      + map_public_ip_on_launch                        = false 
						      + owner_id                                       = (known after apply) 
						      + private_dns_hostname_type_on_launch            = (known after apply) 
						      + tags                                           = { 
						          + "Name" = "AWC-TF-PrivSubnet1" 
						        } 
						      + tags_all                                       = { 
						          + "Name" = "AWC-TF-PrivSubnet1" 
						        } 
						      + vpc_id                                         = (known after apply) 
						    } 
						 
						  # aws_subnet.AWC-Subnet-Priv-2 will be created 
						  + resource "aws_subnet" "AWC-Subnet-Priv-2" { 
						      + arn                                            = (known after apply) 
						      + assign_ipv6_address_on_creation                = false 
						      + availability_zone                              = (sensitive value) 
						      + availability_zone_id                           = (known after apply) 
						      + cidr_block                                     = "172.31.6.0/24" 
						      + enable_dns64                                   = false 
						      + enable_resource_name_dns_a_record_on_launch    = false 
						      + enable_resource_name_dns_aaaa_record_on_launch = false 
						      + id                                             = (known after apply) 
						      + ipv6_cidr_block_association_id                 = (known after apply) 
						      + ipv6_native                                    = false 
						      + map_public_ip_on_launch                        = false 
						      + owner_id                                       = (known after apply) 
						      + private_dns_hostname_type_on_launch            = (known after apply) 
						      + tags                                           = { 
						          + "Name" = "AWC-TF-PrivSubnet2" 
						        } 
						      + tags_all                                       = { 
						          + "Name" = "AWC-TF-PrivSubnet2" 
						        } 
						      + vpc_id                                         = (known after apply) 
						    } 
						 
						  # aws_subnet.AWC-Subnet-Pub-1 will be created 
						  + resource "aws_subnet" "AWC-Subnet-Pub-1" { 
						      + arn                                            = (known after apply) 
						      + assign_ipv6_address_on_creation                = false 
						      + availability_zone                              = (sensitive value) 
						      + availability_zone_id                           = (known after apply) 
						      + cidr_block                                     = "172.31.1.0/24" 
						      + enable_dns64                                   = false 
						      + enable_resource_name_dns_a_record_on_launch    = false 
						      + enable_resource_name_dns_aaaa_record_on_launch = false 
						      + id                                             = (known after apply) 
						      + ipv6_cidr_block_association_id                 = (known after apply) 
						      + ipv6_native                                    = false 
						      + map_public_ip_on_launch                        = false 
						      + owner_id                                       = (known after apply) 
						      + private_dns_hostname_type_on_launch            = (known after apply) 
						      + tags                                           = { 
						          + "Name" = "AWC-TF-PubSubnet1" 
						        } 
						      + tags_all                                       = { 
						          + "Name" = "AWC-TF-PubSubnet1" 
						        } 
						      + vpc_id                                         = (known after apply) 
						    } 
						 
						  # aws_subnet.AWC-Subnet-Pub-2 will be created 
						  + resource "aws_subnet" "AWC-Subnet-Pub-2" { 
						      + arn                                            = (known after apply) 
						      + assign_ipv6_address_on_creation                = false 
						      + availability_zone                              = (sensitive value) 
						      + availability_zone_id                           = (known after apply) 
						      + cidr_block                                     = "172.31.2.0/24" 
						      + enable_dns64                                   = false 
						      + enable_resource_name_dns_a_record_on_launch    = false 
						      + enable_resource_name_dns_aaaa_record_on_launch = false 
						      + id                                             = (known after apply) 
						      + ipv6_cidr_block_association_id                 = (known after apply) 
						      + ipv6_native                                    = false 
						      + map_public_ip_on_launch                        = false 
						      + owner_id                                       = (known after apply) 
						      + private_dns_hostname_type_on_launch            = (known after apply) 
						      + tags                                           = { 
						          + "Name" = "AWC-TF-PubSubnet2" 
						        } 
						      + tags_all                                       = { 
						          + "Name" = "AWC-TF-PubSubnet2" 
						        } 
						      + vpc_id                                         = (known after apply) 
						    } 
						 
					 

					
						Plan: 4 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_subnet.AWC-Subnet-Pub-1: Creating... 
						aws_subnet.AWC-Subnet-Pub-2: Creating... 
						aws_subnet.AWC-Subnet-Priv-2: Creating... 
						aws_subnet.AWC-Subnet-Priv-1: Creating... 
						aws_subnet.AWC-Subnet-Pub-1: Creation complete after 1s [id=subnet-0c66b831b007a95dc] 
						aws_subnet.AWC-Subnet-Pub-2: Creation complete after 1s [id=subnet-0218607b747ebabe1] 
						aws_subnet.AWC-Subnet-Priv-1: Creation complete after 1s [id=subnet-0ebd5082675ce4b86] 
						aws_subnet.AWC-Subnet-Priv-2: Creation complete after 1s [id=subnet-0aedb284d0a518375] 
						 
						Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
					 
					PS C:\_TERRAFORM\_AWSCore&gt; 
				
			
		
	



	Terraform successfully created the new VPC and its DHCP options.
 


	
		Note: 
	 

	
		For more information about Subnets, please visit Amazon´s Product Documentation pages. 
		 
	 



	After creating the Subnets, you must create an Internet Gateway to enable the Public Subnet and the Jumphost-VM to connect to the Internet. 
	 
 


	Deploying an Internet Gateway



	An Internet Gateway allows communication between your VPC and the Internet. It supports IPv4 and IPv6 traffic and provides a target in your VPC Route Tables. 
	Resources in your Public Subnets can connect to the Internet if they have a public IPv4 or IPv6 address. The communication flow is two-way—the resources can also be contacted from the Internet. 
	For communication using IPv4, the Internet Gateway also performs Network Address Translation (NAT).
 


	Use Terraform to create the Internet Gateway:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	



	
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #1e1e1e;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #1e1e1e;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}	
	
		
			
				
					
						
							### Create a Public Internet Gateway
						 

						
							resource "aws_internet_gateway" "AWC-InetGW" {
						 

						
							  vpc_id = aws_vpc.AWC-VPC.id
						 

						
							  tags                  = {
						 

						
							                              Name = "AWC-TF-InetGW-Pub"
						 

						
							  }
						 
						}
					
				
			
		
	



	 
	Terraform will now create these entities:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\_TERRAFORM\_AWSCore&gt; terraform plan
					 

					
						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_internet_gateway.AWC-InetGW will be created 
						  + resource "aws_internet_gateway" "AWC-InetGW" { 
						      + arn      = (known after apply) 
						      + id       = (known after apply) 
						      + owner_id = (known after apply) 
						      + tags     = { 
						          + "Name" = "AWC-TF-InetGW-Pub" 
						        } 
						      + tags_all = { 
						          + "Name" = "AWC-TF-InetGW-Pub" 
						        } 
						      + vpc_id   = (known after apply) 
						    } 
						 
						 
						Plan: 1 to add, 0 to change, 0 to destroy. 
						 
						─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 
						 
						PS C:\_TERRAFORM\_AWSCore&gt; terraform apply 
						 
						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_internet_gateway.AWC-InetGW will be created 
						  + resource "aws_internet_gateway" "AWC-InetGW" { 
						      + arn      = (known after apply) 
						      + id       = (known after apply) 
						      + owner_id = (known after apply) 
						      + tags     = { 
						          + "Name" = "AWC-TF-InetGW-Pub" 
						        } 
						      + tags_all = { 
						          + "Name" = "AWC-TF-InetGW-Pub" 
						        } 
						      + vpc_id   = (known after apply) 
						    } 
						 
						 
						Plan: 1 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_internet_gateway.AWC-InetGW: Creating... 
						aws_internet_gateway.AWC-InetGW: Creation complete after 1s [id=igw-07459e74a135ab85f] 
						 
						Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
					 
					PS C:\_TERRAFORM\_AWSCore&gt; 
				
			
		
	



	Terraform successfully created the new Internet Gateway.
 


	To connect the Public Subnets to the Internet, you need to adjust the adjacent Route Table. A Route Table contains a set of routes directing network traffic from your Subnet or Gateway. 
	You should use 0.0.0.0/0 as the Destination and choose the Internet Gateway you created as the Target. The Public Subnets now have a connection to the Internet.
 


	Use Terraform to create the Route Table and the needed Routes and Associations:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	



	
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #1e1e1e;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #1e1e1e;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}	
	
		
			
				
					
						
							### Create the Public Route Table
						 

						
							resource "aws_route_table" "AWC-RouteTable-Pub" {
						 

						
							  vpc_id = aws_vpc.AWC-VPC.id
						 

						
							  tags                  = {
						 

						
							                              Name = "AWC-TF-RouteTable-Pub"
						 

						
							  }
						 

						
							}
						 

						
							 
						 

						
							#### Create Routes in the Public Route Table
						 

						
							resource "aws_route" "AWC-Route-Public" {
						 

						
							  depends_on = [ aws_vpc.AWC-VPC, aws_internet_gateway.AWC-InetGW, aws_route_table.AWC-RouteTable-Pub ]
						 

						
							  route_table_id         = aws_route_table.AWC-RouteTable-Pub.id
						 

						
							  destination_cidr_block = "0.0.0.0/0"
						 

						
							  gateway_id             = aws_internet_gateway.AWC-InetGW.id
						 

						
							}
						 

						
							 
						 

						
							#### Create Subnet Association for Public Subnet #1
						 

						
							resource "aws_route_table_association" "AWC-RT-SN-Association-Pub-1" {
						 

						
							  subnet_id      = aws_subnet.AWC-Subnet-Pub-1.id
						 

						
							  route_table_id = aws_route_table.AWC-RouteTable-Pub.id
						 

						
							}
						 

						
							 
						 

						
							#### Create Subnet Association for Public Subnet #2
						 

						
							resource "aws_route_table_association" "AWC-RT-SN-Association-Pub-2" {
						 

						
							  subnet_id      = aws_subnet.AWC-Subnet-Pub-2.id
						 

						
							  route_table_id = aws_route_table.AWC-RouteTable-Pub.id
						 

						
							}
						 

						
							 
						 
					
				
			
		
	



	 
	Terraform will now create these entities:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\_TERRAFORM\_AWSCore&gt; terraform plan
					 

					
						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_route_table.AWC-RouteTable-Pub will be created 
						  + resource "aws_route_table" "AWC-RouteTable-Pub" { 
						      + arn              = (known after apply) 
						      + id               = (known after apply) 
						      + owner_id         = (known after apply) 
						      + propagating_vgws = (known after apply) 
						      + route            = (known after apply) 
						      + tags             = { 
						          + "Name" = "AWC-TF-RouteTable-Pub" 
						        } 
						      + vpc_id           = (known after apply) 
						    }
					 

					
						  # aws_route.AWC-Route-Public will be created 
						  + resource "aws_route" "AWC-Route-Public" { 
						      + destination_cidr_block = "0.0.0.0/0" 
						      + gateway_id             = (known after apply) 
						      + id                     = (known after apply) 
						      + instance_id            = (known after apply) 
						      + instance_owner_id      = (known after apply) 
						      + network_interface_id   = (known after apply) 
						      + origin                 = (known after apply) 
						      + route_table_id         = (known after apply) 
						      + state                  = (known after apply) 
						    }
					 

					
						  # aws_route_table_association.AWC-RT-SN-Association-Pub-1 will be created 
						  + resource "aws_route_table_association" "AWC-RT-SN-Association-Pub-1" { 
						      + id             = (known after apply) 
						      + route_table_id = (known after apply) 
						      + subnet_id      = (known after apply) 
						    } 
						 
						  # aws_route_table_association.AWC-RT-SN-Association-Pub-2 will be created 
						  + resource "aws_route_table_association" "AWC-RT-SN-Association-Pub-2" { 
						      + id             = (known after apply) 
						      + route_table_id = (known after apply) 
						      + subnet_id      = (known after apply) 
						    } 
						 
						Plan: 4 to add, 0 to change, 0 to destroy. 
						 
						─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 
						 
						PS C:\_TERRAFORM\_AWSCore&gt; terraform apply 
						 
						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_route_table.AWC-RouteTable-Pub will be created 
						  + resource "aws_route_table" "AWC-RouteTable-Pub" { 
						      + arn              = (known after apply) 
						      + id               = (known after apply) 
						      + owner_id         = (known after apply) 
						      + propagating_vgws = (known after apply) 
						      + route            = (known after apply) 
						      + tags             = { 
						          + "Name" = "AWC-TF-RouteTable-Pub" 
						        } 
						      + vpc_id           = (known after apply) 
						    }
					 

					
						  # aws_route.AWC-Route-Public will be created 
						  + resource "aws_route" "AWC-Route-Public" { 
						      + destination_cidr_block = "0.0.0.0/0" 
						      + gateway_id             = (known after apply) 
						      + id                     = (known after apply) 
						      + instance_id            = (known after apply) 
						      + instance_owner_id      = (known after apply) 
						      + network_interface_id   = (known after apply) 
						      + origin                 = (known after apply) 
						      + route_table_id         = (known after apply) 
						      + state                  = (known after apply) 
						    }
					 

					
						  # aws_route_table_association.AWC-RT-SN-Association-Pub-1 will be created 
						  + resource "aws_route_table_association" "AWC-RT-SN-Association-Pub-1" { 
						      + id             = (known after apply) 
						      + route_table_id = (known after apply) 
						      + subnet_id      = (known after apply) 
						    } 
						 
						  # aws_route_table_association.AWC-RT-SN-Association-Pub-2 will be created 
						  + resource "aws_route_table_association" "AWC-RT-SN-Association-Pub-2" { 
						      + id             = (known after apply) 
						      + route_table_id = (known after apply) 
						      + subnet_id      = (known after apply) 
						    } 
						 
						Plan: 4 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_route_table.AWC-RouteTable-Pub: Creating... 
						aws_route_table.AWC-RouteTable-Pub: Creation complete after 1s [id=rtb-03e180326b1e9e342] 
						aws_route_table_association.AWC-RT-SN-Association-Pub-1: Creating... 
						aws_route_table_association.AWC-RT-SN-Association-Pub-2: Creating... 
						aws_route.AWC-Route-Public: Creating... 
						aws_route_table_association.AWC-RT-SN-Association-Pub-1: Creation complete after 0s [id=rtbassoc-0fa2d0b5fe04866de] 
						aws_route_table_association.AWC-RT-SN-Association-Pub-2: Creation complete after 0s [id=rtbassoc-021578912696a90c3] 
						aws_route.AWC-Route-Public: Creation complete after 0s [id=r-rtb-03e180326b1e9e3421080289494] 
						 
						Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
					 
					PS C:\_TERRAFORM\_AWSCore&gt; 
				
			
		
	



	Terraform successfully created the new Route Table, Routes, and Associations for the Public Subnets and the Internet Gateway.
 


	
		Note: 
	 

	
		For more information about Gateways, please visit Amazon´s Product Documentation pages. 
		 
	 



	 
 


	Deploying a NAT Gateway



	After creating the Internet Gateway, you need to create a NAT Gateway in the Private Subnet(s) to enable the Private Subnet(s) to connect to the Internet. 
	A NAT Gateway is a Network Address Translation (NAT) device. It needs a mapped Elastic IP.
 


	
		Important: 
	 

	
		You can use a NAT Gateway to enable instances in a Private Subnet to connect to services outside your VPC. External services cannot initiate a connection with those instances. 
		A NAT Gateway is for use with IPv4 traffic only. 
		 
	 



	When you create a NAT Gateway, you specify one of the following connectivity types:
 


	
		Public – (Default) Instances in Private Subnets can connect to the Internet.
	
	
		Private – Instances in Private Subnets can connect to other VPCs or your on-premises network but not to the Internet. 
	



	Use Terraform to create the Elastic IPs and the NAT Gateways:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	



	
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #1e1e1e;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #1e1e1e;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}	
	
		
			
				
					
						
							### Create an Elastic IP for NATGW #1
						 

						
							resource "aws_eip" "AWC-EIP-NATGW-AZ1" {
						 

						
							  tags                  = {
						 

						
							                              Name = "AWC-TF-EIP1"
						 

						
							  }
						 

						
							}
						 

						
							 
						 

						
							### Create an Elastic IP for NATGW #2
						 

						
							resource "aws_eip" "AWC-EIP-NATGW-AZ2" {
						 

						
							  tags                  = {
						 

						
							                              Name = "AWC-TF-EIP2"
						 

						
							  }
						 

						
							}
						 

						
							 
						 

						
							### Create a NAT Gateway for Private Subnet #1
						 

						
							resource "aws_nat_gateway" "AWC-NATGW-SN1" {
						 

						
							  allocation_id         = aws_eip.AWC-EIP-NATGW-AZ1.id
						 

						
							  subnet_id             = aws_subnet.AWC-Subnet-Priv-1.id
						 

						
							  tags                  = {
						 

						
							                              Name = "AWC-TF-NATGW-SN1"
						 

						
							  }
						 

						
							}
						 

						
							 
						 

						
							### Create a NAT Gateway for Private Subnet #2
						 

						
							resource "aws_nat_gateway" "AWC-NATGW-SN2" {
						 

						
							  allocation_id         = aws_eip.AWC-EIP-NATGW-AZ2.id
						 

						
							  subnet_id             = aws_subnet.AWC-Subnet-Priv-2.id
						 

						
							  tags                  = {
						 

						
							                              Name = "AWC-TF-NATGW-SN2"
						 

						
							  }
						 
						}
					
				
			
		
	



	 
	Terraform will now create these entities:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\_TERRAFORM\_AWSCore&gt; terraform plan
					 

					
						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_eip.AWC-EIP-NATGW-AZ1 will be created 
						  + resource "aws_eip" "AWC-EIP-NATGW-AZ1" { 
						      + allocation_id        = (known after apply) 
						      + arn                  = (known after apply) 
						      + association_id       = (known after apply) 
						      + carrier_ip           = (known after apply) 
						      + customer_owned_ip    = (known after apply) 
						      + domain               = (known after apply) 
						      + id                   = (known after apply) 
						      + instance             = (known after apply) 
						      + ipam_pool_id         = (known after apply) 
						      + network_border_group = (known after apply) 
						      + network_interface    = (known after apply) 
						      + private_dns          = (known after apply) 
						      + private_ip           = (known after apply) 
						      + ptr_record           = (known after apply) 
						      + public_dns           = (known after apply) 
						      + public_ip            = (known after apply) 
						      + public_ipv4_pool     = (known after apply) 
						      + tags                 = { 
						          + "Name" = "AWC-TF-EIP1" 
						        } 
						      + tags_all             = { 
						          + "Name" = "AWC-TF-EIP1" 
						        } 
						      + vpc                  = (known after apply) 
						    } 
						 
						  # aws_eip.AWC-EIP-NATGW-AZ2 will be created 
						  + resource "aws_eip" "AWC-EIP-NATGW-AZ2" { 
						      + allocation_id        = (known after apply) 
						      + arn                  = (known after apply) 
						      + association_id       = (known after apply) 
						      + carrier_ip           = (known after apply) 
						      + customer_owned_ip    = (known after apply) 
						      + domain               = (known after apply) 
						      + id                   = (known after apply) 
						      + instance             = (known after apply) 
						      + ipam_pool_id         = (known after apply) 
						      + network_border_group = (known after apply) 
						      + network_interface    = (known after apply) 
						      + private_dns          = (known after apply) 
						      + private_ip           = (known after apply) 
						      + ptr_record           = (known after apply) 
						      + public_dns           = (known after apply) 
						      + public_ip            = (known after apply) 
						      + public_ipv4_pool     = (known after apply) 
						      + tags                 = { 
						          + "Name" = "AWC-TF-EIP2" 
						        } 
						      + tags_all             = { 
						          + "Name" = "AWC-TF-EIP2" 
						        } 
						      + vpc                  = (known after apply) 
						    }
					 

					
						# aws_nat_gateway.AWC-NATGW-SN1 will be created 
						  + resource "aws_nat_gateway" "AWC-NATGW-SN1" { 
						      + allocation_id                      = (known after apply) 
						      + association_id                     = (known after apply) 
						      + connectivity_type                  = "public" 
						      + id                                 = (known after apply) 
						      + network_interface_id               = (known after apply) 
						      + private_ip                         = (known after apply) 
						      + public_ip                          = (known after apply) 
						      + secondary_private_ip_address_count = (known after apply) 
						      + secondary_private_ip_addresses     = (known after apply) 
						      + subnet_id                          = (known after apply) 
						      + tags                               = { 
						          + "Name" = "AWC-TF-NATGW-SN1" 
						        } 
						      + tags_all                           = { 
						          + "Name" = "AWC-TF-NATGW-SN1" 
						        } 
						    } 
						 
						  # aws_nat_gateway.AWC-NATGW-SN2 will be created 
						  + resource "aws_nat_gateway" "AWC-NATGW-SN2" { 
						      + allocation_id                      = (known after apply) 
						      + association_id                     = (known after apply) 
						      + connectivity_type                  = "public" 
						      + id                                 = (known after apply) 
						      + network_interface_id               = (known after apply) 
						      + private_ip                         = (known after apply) 
						      + public_ip                          = (known after apply) 
						      + secondary_private_ip_address_count = (known after apply) 
						      + secondary_private_ip_addresses     = (known after apply) 
						      + subnet_id                          = (known after apply) 
						      + tags                               = { 
						          + "Name" = "AWC-TF-NATGW-SN2" 
						        } 
						      + tags_all                           = { 
						          + "Name" = "AWC-TF-NATGW-SN2" 
						        } 
						    } 
						 
						Plan: 4 to add, 0 to change, 0 to destroy. 
						 
						─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 
						 
						PS C:\_TERRAFORM\_AWSCore&gt; terraform apply 
						 
						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_eip.AWC-EIP-NATGW-AZ1 will be created 
						  + resource "aws_eip" "AWC-EIP-NATGW-AZ1" { 
						      + allocation_id        = (known after apply) 
						      + arn                  = (known after apply) 
						      + association_id       = (known after apply) 
						      + carrier_ip           = (known after apply) 
						      + customer_owned_ip    = (known after apply) 
						      + domain               = (known after apply) 
						      + id                   = (known after apply) 
						      + instance             = (known after apply) 
						      + ipam_pool_id         = (known after apply) 
						      + network_border_group = (known after apply) 
						      + network_interface    = (known after apply) 
						      + private_dns          = (known after apply) 
						      + private_ip           = (known after apply) 
						      + ptr_record           = (known after apply) 
						      + public_dns           = (known after apply) 
						      + public_ip            = (known after apply) 
						      + public_ipv4_pool     = (known after apply) 
						      + tags                 = { 
						          + "Name" = "AWC-TF-EIP1" 
						        } 
						      + tags_all             = { 
						          + "Name" = "AWC-TF-EIP1" 
						        } 
						      + vpc                  = (known after apply) 
						    } 
						 
						  # aws_eip.AWC-EIP-NATGW-AZ2 will be created 
						  + resource "aws_eip" "AWC-EIP-NATGW-AZ2" { 
						      + allocation_id        = (known after apply) 
						      + arn                  = (known after apply) 
						      + association_id       = (known after apply) 
						      + carrier_ip           = (known after apply) 
						      + customer_owned_ip    = (known after apply) 
						      + domain               = (known after apply) 
						      + id                   = (known after apply) 
						      + instance             = (known after apply) 
						      + ipam_pool_id         = (known after apply) 
						      + network_border_group = (known after apply) 
						      + network_interface    = (known after apply) 
						      + private_dns          = (known after apply) 
						      + private_ip           = (known after apply) 
						      + ptr_record           = (known after apply) 
						      + public_dns           = (known after apply) 
						      + public_ip            = (known after apply) 
						      + public_ipv4_pool     = (known after apply) 
						      + tags                 = { 
						          + "Name" = "AWC-TF-EIP2" 
						        } 
						      + tags_all             = { 
						          + "Name" = "AWC-TF-EIP2" 
						        } 
						      + vpc                  = (known after apply) 
						    }
					 

					
						# aws_nat_gateway.AWC-NATGW-SN1 will be created 
						  + resource "aws_nat_gateway" "AWC-NATGW-SN1" { 
						      + allocation_id                      = (known after apply) 
						      + association_id                     = (known after apply) 
						      + connectivity_type                  = "public" 
						      + id                                 = (known after apply) 
						      + network_interface_id               = (known after apply) 
						      + private_ip                         = (known after apply) 
						      + public_ip                          = (known after apply) 
						      + secondary_private_ip_address_count = (known after apply) 
						      + secondary_private_ip_addresses     = (known after apply) 
						      + subnet_id                          = (known after apply) 
						      + tags                               = { 
						          + "Name" = "AWC-TF-NATGW-SN1" 
						        } 
						      + tags_all                           = { 
						          + "Name" = "AWC-TF-NATGW-SN1" 
						        } 
						    } 
						 
						  # aws_nat_gateway.AWC-NATGW-SN2 will be created 
						  + resource "aws_nat_gateway" "AWC-NATGW-SN2" { 
						      + allocation_id                      = (known after apply) 
						      + association_id                     = (known after apply) 
						      + connectivity_type                  = "public" 
						      + id                                 = (known after apply) 
						      + network_interface_id               = (known after apply) 
						      + private_ip                         = (known after apply) 
						      + public_ip                          = (known after apply) 
						      + secondary_private_ip_address_count = (known after apply) 
						      + secondary_private_ip_addresses     = (known after apply) 
						      + subnet_id                          = (known after apply) 
						      + tags                               = { 
						          + "Name" = "AWC-TF-NATGW-SN2" 
						        } 
						      + tags_all                           = { 
						          + "Name" = "AWC-TF-NATGW-SN2" 
						        } 
						    } 
						 
						Plan: 4 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_eip.AWC-EIP-NATGW-AZ2: Creating... 
						aws_eip.AWC-EIP-NATGW-AZ1: Creating... 
						aws_eip.AWC-EIP-NATGW-AZ2: Creation complete after 1s [id=eipalloc-0d2c0d41ca785a357] 
						aws_eip.AWC-EIP-NATGW-AZ1: Creation complete after 1s [id=eipalloc-00184109cbc0ed1c3] 
						aws_nat_gateway.AWC-NATGW-SN1: Creating... 
						aws_nat_gateway.AWC-NATGW-SN2: Creating... 
						aws_nat_gateway.AWC-NATGW-SN1: Still creating... [10s elapsed] 
						aws_nat_gateway.AWC-NATGW-SN2: Still creating... [10s elapsed] 
						aws_nat_gateway.AWC-NATGW-SN1: Still creating... [20s elapsed] 
						aws_nat_gateway.AWC-NATGW-SN2: Still creating... [20s elapsed] 
						aws_nat_gateway.AWC-NATGW-SN1: Still creating... [30s elapsed] 
						aws_nat_gateway.AWC-NATGW-SN2: Still creating... [30s elapsed] 
						... 
						aws_nat_gateway.AWC-NATGW-SN1: Still creating... [1m40s elapsed] 
						aws_nat_gateway.AWC-NATGW-SN2: Still creating... [1m40s elapsed] 
						aws_nat_gateway.AWC-NATGW-SN2: Creation complete after 1m44s [id=nat-0b05e87d8b775ad17] 
						aws_nat_gateway.AWC-NATGW-SN1: Creation complete after 1m44s [id=nat-064b88196cc1199a4] 
						 
						 
						Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
					 
					PS C:\_TERRAFORM\_AWSCore&gt; 
				
			
		
	



	Terraform successfully created the new NAT Gateways and its needed Elastic IPs. 
	After creation, you need to add a Route Table and change it for the NAT Gateway as you did for the Internet Gateway.
 


	Use Terraform to create the Route Table and the needed Routes and Associations:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	



	
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #1e1e1e;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #1e1e1e;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}	
	
		
			
				
					
						
							### Create the Private Route Table for Subnet #1
						 

						
							resource "aws_route_table" "AWC-RouteTable-Priv-SN1" {
						 

						
							  vpc_id = aws_vpc.AWC-VPC.id
						 

						
							  tags                  = {
						 

						
							                              Name = "AWC-TF-RouteTable-Priv-SN1"
						 

						
							  }
						 

						
							}
						 

						
							 
						 

						
							### Create the Private Route Table for Subnet #2
						 

						
							resource "aws_route_table" "AWC-RouteTable-Priv-SN2" {
						 

						
							  vpc_id = aws_vpc.AWC-VPC.id
						 

						
							  tags                  = {
						 

						
							                              Name = "AWC-TF-RouteTable-Priv-SN2"
						 

						
							  }
						 

						
							}
						 

						
							 
						 

						
							#### Create Routes in the Private Route Table for Subnet #1
						 

						
							resource "aws_route" "AWC-Route-Private-SN1" {
						 

						
							  route_table_id         = aws_route_table.AWC-RouteTable-Priv-SN1.id
						 

						
							  destination_cidr_block = "0.0.0.0/0"
						 

						
							  gateway_id             = aws_nat_gateway.AWC-NATGW-SN1.id
						 

						
							}
						 

						
							 
						 

						
							#### Create Routes in the Private Route Table for Subnet #2
						 

						
							resource "aws_route" "AWC-Route-Private-SN2" {
						 

						
							  route_table_id         = aws_route_table.AWC-RouteTable-Priv-SN2.id
						 

						
							  destination_cidr_block = "0.0.0.0/0"
						 

						
							  gateway_id             = aws_nat_gateway.AWC-NATGW-SN2.id
						 

						
							}
						 

						
							 
						 

						
							#### Create Subnet Association for Private Subnet #1
						 

						
							resource "aws_route_table_association" "AWC-RT-SN-Association-Priv-1" {
						 

						
							  subnet_id      = aws_subnet.AWC-Subnet-Priv-1.id
						 

						
							  route_table_id = aws_route_table.AWC-RouteTable-Priv-SN1.id
						 

						
							}
						 

						
							 
						 

						
							#### Create Subnet Association for Private Subnet #2
						 

						
							resource "aws_route_table_association" "AWC-RT-SN-Association-Priv-2" {
						 

						
							  subnet_id      = aws_subnet.AWC-Subnet-Priv-2.id
						 

						
							  route_table_id = aws_route_table.AWC-RouteTable-Priv-SN2.id
						 

						
							}
						 
					
				
			
		
	



	 
	Terraform will now create these entities:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\_TERRAFORM\_AWSCore&gt; terraform plan
					 

					
						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_route_table.AWC-RouteTable-Priv-SN1 will be created 
						  + resource "aws_route_table" "AWC-RouteTable-Priv-SN1" { 
						      + arn              = (known after apply) 
						      + id               = (known after apply) 
						      + owner_id         = (known after apply) 
						      + propagating_vgws = (known after apply) 
						      + route            = (known after apply) 
						      + tags             = { 
						          + "Name" = "AWC-TF-RouteTable-Priv-SN1" 
						        } 
						      + tags_all         = { 
						          + "Name" = "AWC-TF-RouteTable-Priv-SN1" 
						        } 
						      + vpc_id           = (known after apply) 
						    } 
						 
						  # aws_route_table.AWC-RouteTable-Priv-SN2 will be created 
						  + resource "aws_route_table" "AWC-RouteTable-Priv-SN2" { 
						      + arn              = (known after apply) 
						      + id               = (known after apply) 
						      + owner_id         = (known after apply) 
						      + propagating_vgws = (known after apply) 
						      + route            = (known after apply) 
						      + tags             = { 
						          + "Name" = "AWC-TF-RouteTable-Priv-SN2" 
						        } 
						      + tags_all         = { 
						          + "Name" = "AWC-TF-RouteTable-Priv-SN2" 
						        } 
						      + vpc_id           = (known after apply) 
						    }
					 

					
						  # aws_route.AWC-Route-Private-SN1 will be created 
						  + resource "aws_route" "AWC-Route-Private-SN1" { 
						      + destination_cidr_block = "0.0.0.0/0" 
						      + gateway_id             = (known after apply) 
						      + id                     = (known after apply) 
						      + instance_id            = (known after apply) 
						      + instance_owner_id      = (known after apply) 
						      + network_interface_id   = (known after apply) 
						      + origin                 = (known after apply) 
						      + route_table_id         = (known after apply) 
						      + state                  = (known after apply) 
						    } 
						 
						  # aws_route.AWC-Route-Private-SN2 will be created 
						  + resource "aws_route" "AWC-Route-Private-SN2" { 
						      + destination_cidr_block = "0.0.0.0/0" 
						      + gateway_id             = (known after apply) 
						      + id                     = (known after apply) 
						      + instance_id            = (known after apply) 
						      + instance_owner_id      = (known after apply) 
						      + network_interface_id   = (known after apply) 
						      + origin                 = (known after apply) 
						      + route_table_id         = (known after apply) 
						      + state                  = (known after apply) 
						    } 
						  # aws_route_table_association.AWC-RT-SN-Association-Priv-1 will be created 
						  + resource "aws_route_table_association" "AWC-RT-SN-Association-Priv-1" { 
						      + id             = (known after apply) 
						      + route_table_id = (known after apply) 
						      + subnet_id      = (known after apply) 
						    } 
						 
						  # aws_route_table_association.AWC-RT-SN-Association-Priv-2 will be created 
						  + resource "aws_route_table_association" "AWC-RT-SN-Association-Priv-2" { 
						      + id             = (known after apply) 
						      + route_table_id = (known after apply) 
						      + subnet_id      = (known after apply) 
						    }
					 

					
						 
						Plan: 6 to add, 0 to change, 0 to destroy. 
						 
						─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 
						 
						PS C:\_TERRAFORM\_AWSCore&gt; terraform apply 
						 
						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_route_table.AWC-RouteTable-Pub will be created 
						  + resource "aws_route_table" "AWC-RouteTable-Pub" { 
						      + arn              = (known after apply) 
						      + id               = (known after apply) 
						      + owner_id         = (known after apply) 
						      + propagating_vgws = (known after apply) 
						      + route            = (known after apply) 
						      + tags             = { 
						          + "Name" = "AWC-TF-RouteTable-Pub" 
						        } 
						      + vpc_id           = (known after apply) 
						    }
					 

					
						  # aws_route.AWC-Route-Public will be created 
						  + resource "aws_route" "AWC-Route-Public" { 
						      + destination_cidr_block = "0.0.0.0/0" 
						      + gateway_id             = (known after apply) 
						      + id                     = (known after apply) 
						      + instance_id            = (known after apply) 
						      + instance_owner_id      = (known after apply) 
						      + network_interface_id   = (known after apply) 
						      + origin                 = (known after apply) 
						      + route_table_id         = (known after apply) 
						      + state                  = (known after apply) 
						    }
					 

					
						  # aws_route_table_association.AWC-RT-SN-Association-Pub-1 will be created 
						  + resource "aws_route_table_association" "AWC-RT-SN-Association-Pub-1" { 
						      + id             = (known after apply) 
						      + route_table_id = (known after apply) 
						      + subnet_id      = (known after apply) 
						    } 
						 
						  # aws_route_table_association.AWC-RT-SN-Association-Pub-2 will be created 
						  + resource "aws_route_table_association" "AWC-RT-SN-Association-Pub-2" { 
						      + id             = (known after apply) 
						      + route_table_id = (known after apply) 
						      + subnet_id      = (known after apply) 
						    } 
						 
						Terraform will perform the following actions:
					 

					
						  # aws_route_table.AWC-RouteTable-Priv-SN1 will be created 
						  + resource "aws_route_table" "AWC-RouteTable-Priv-SN1" { 
						      + arn              = (known after apply) 
						      + id               = (known after apply) 
						      + owner_id         = (known after apply) 
						      + propagating_vgws = (known after apply) 
						      + route            = (known after apply) 
						      + tags             = { 
						          + "Name" = "AWC-TF-RouteTable-Priv-SN1" 
						        } 
						      + tags_all         = { 
						          + "Name" = "AWC-TF-RouteTable-Priv-SN1" 
						        } 
						      + vpc_id           = (known after apply) 
						    } 
						 
						  # aws_route_table.AWC-RouteTable-Priv-SN2 will be created 
						  + resource "aws_route_table" "AWC-RouteTable-Priv-SN2" { 
						      + arn              = (known after apply) 
						      + id               = (known after apply) 
						      + owner_id         = (known after apply) 
						      + propagating_vgws = (known after apply) 
						      + route            = (known after apply) 
						      + tags             = { 
						          + "Name" = "AWC-TF-RouteTable-Priv-SN2" 
						        } 
						      + tags_all         = { 
						          + "Name" = "AWC-TF-RouteTable-Priv-SN2" 
						        } 
						      + vpc_id           = (known after apply) 
						    }
					 

					
						  # aws_route.AWC-Route-Private-SN1 will be created 
						  + resource "aws_route" "AWC-Route-Private-SN1" { 
						      + destination_cidr_block = "0.0.0.0/0" 
						      + gateway_id             = (known after apply) 
						      + id                     = (known after apply) 
						      + instance_id            = (known after apply) 
						      + instance_owner_id      = (known after apply) 
						      + network_interface_id   = (known after apply) 
						      + origin                 = (known after apply) 
						      + route_table_id         = (known after apply) 
						      + state                  = (known after apply) 
						    } 
						 
						  # aws_route.AWC-Route-Private-SN2 will be created 
						  + resource "aws_route" "AWC-Route-Private-SN2" { 
						      + destination_cidr_block = "0.0.0.0/0" 
						      + gateway_id             = (known after apply) 
						      + id                     = (known after apply) 
						      + instance_id            = (known after apply) 
						      + instance_owner_id      = (known after apply) 
						      + network_interface_id   = (known after apply) 
						      + origin                 = (known after apply) 
						      + route_table_id         = (known after apply) 
						      + state                  = (known after apply) 
						    } 
						  # aws_route_table_association.AWC-RT-SN-Association-Priv-1 will be created 
						  + resource "aws_route_table_association" "AWC-RT-SN-Association-Priv-1" { 
						      + id             = (known after apply) 
						      + route_table_id = (known after apply) 
						      + subnet_id      = (known after apply) 
						    } 
						 
						  # aws_route_table_association.AWC-RT-SN-Association-Priv-2 will be created 
						  + resource "aws_route_table_association" "AWC-RT-SN-Association-Priv-2" { 
						      + id             = (known after apply) 
						      + route_table_id = (known after apply) 
						      + subnet_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 
						 
						aws_route_table.AWC-RouteTable-Priv-SN2: Creating... 
						aws_route_table.AWC-RouteTable-Priv-SN1: Creating... 
						aws_route.AWC-Route-Private-SN2: Creating... 
						aws_route.AWC-Route-Private-SN1: Creating... 
						aws_route_table.AWC-RouteTable-Priv-SN2: Creation complete after 1s [id=rtb-04d3927d25865adcf] 
						aws_route_table.AWC-RouteTable-Priv-SN1: Creation complete after 1s [id=rtb-0c9cb8b3a967e3481] 
						aws_route.AWC-Route-Private-SN2: Creation complete after 0s [id=r-rtb-04d3927d25865adcf1080289494] 
						aws_route.AWC-Route-Private-SN1: Creation complete after 0s [id=r-rtb-0c9cb8b3a967e34811080289494] 
						aws_route_table_association.AWC-RT-SN-Association-Priv-1: Creating... 
						aws_route_table_association.AWC-RT-SN-Association-Priv-2: Creating... 
						aws_route_table_association.AWC-RT-SN-Association-Priv-1: Creation complete after 0s [id=rtbassoc-090c9d66718c2a158] 
						aws_route_table_association.AWC-RT-SN-Association-Priv-2: Creation complete after 0s [id=rtbassoc-038405332aa4c4384] 
						 
						 
						Apply complete! Resources: 6 added, 0 changed, 0 destroyed.
					 
					PS C:\_TERRAFORM\_AWSCore&gt; 
				
			
		
	



	Terraform successfully created the new Route Table, Routes, and Associations for the Private Subnets.
 


	
		Note: 
	 

	
		For more information about Gateways, please visit Amazon´s Product Documentation pages. 
		 
	 



	Now, all instances in the Private Subnets should be able to connect to the Internet. 
	 
 


	Set the IAM permissions



	After creating all needed Network components, we must set all required IAM permissions. 
	Identity and Access Management (IAM) allows control of access to AWS resources. 
	IAM controls who can be authenticated (signed in) and authorized (have permissions) to use Amazon VPC resources.
 


	Deploying an IAM Policy



	You can control access by attaching policies and attaching them to identities or resources. 
	A Policy defines the permissions associated with it. Policies are evaluated when a user, for example, or a root user, wants to access a resource. 
	Permissions in the Policies determine whether the request is allowed or denied. 
	Most Policies are stored in AWS as JSON documents.
 


	As an example, we deploy an IAM policy and an IAM role that sets the needed permissions for deploying DaaS and its entities:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	



	
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #1e1e1e;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #1e1e1e;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}	
	
		
			
				
					
						
							### Create the needed IAM Roles and Policies
						 

						
							resource "aws_iam_role" "AWC-IAM-Main-Role" {
						 

						
							  name = "AWC-IAM-Main-Role"
						 

						
							  
						 

						
							  assume_role_policy = jsonencode({
						 

						
							    Version = "2012-10-17"
						 

						
							    Statement = [{
						 

						
							          Action    = "sts:AssumeRole"
						 

						
							          Effect    = "Allow"
						 

						
							          Principal = {
						 

						
							                  Service = "ec2.amazonaws.com"
						 

						
							      }
						 

						
							    }]
						 

						
							  })
						 

						
							}
						 

						
							 
						 

						
							resource "aws_iam_role_policy" "AWC-IAM-Main"  {
						 

						
							  name    = "AWC-IAM-Role-Main"
						 

						
							  role    = aws_iam_role.AWC-IAM-Main-Role.id
						 

						
							  policy  = &lt;&lt;EOF
						 

						
							{
						 

						
							  "Version": "2012-10-17",
						 

						
							  "Statement": [
						 

						
							    {
						 

						
							      "Sid": "VisualEditor0",
						 

						
							      "Effect": "Allow",
						 

						
							      "Action": [
						 

						
							        "workdocs:DeregisterDirectory",
						 

						
							        "workdocs:RegisterDirectory",
						 

						
							        "workdocs:AddUserToGroup",
						 

						
							        "ec2:ImportInstance",
						 

						
							        "ec2:DescribeImages",
						 

						
							        "ec2:DescribeImageAttribute",
						 

						
							        "ec2:CreateKeyPair",
						 

						
							        "ec2:DescribeKeyPairs",
						 

						
							        "ec2:ModifyImageAttribute",
						 

						
							        "ec2:DescribeVpcs",
						 

						
							        "ec2:DescribeSubnets",
						 

						
							        "ec2:RunInstances",
						 

						
							        "ec2:DescribeSecurityGroups",
						 

						
							        "ec2:CreateTags",
						 

						
							        "ec2:DescribeRouteTables",
						 

						
							        "ec2:DescribeInternetGateways",
						 

						
							        "ec2:CreateSecurityGroup",
						 

						
							        "ec2:DescribeInstanceTypes",
						 

						
							        "servicequotas:ListServices",
						 

						
							        "servicequotas:GetRequestedServiceQuotaChange",
						 

						
							        "servicequotas:ListTagsForResource",
						 

						
							        "servicequotas:GetServiceQuota",
						 

						
							        "servicequotas:GetAssociationForServiceQuotaTemplate",
						 

						
							        "servicequotas:ListAWSDefaultServiceQuotas",
						 

						
							        "servicequotas:ListServiceQuotas",
						 

						
							        "servicequotas:GetAWSDefaultServiceQuota",
						 

						
							        "servicequotas:GetServiceQuotaIncreaseRequestFromTemplate",
						 

						
							        "servicequotas:ListServiceQuotaIncreaseRequestsInTemplate",
						 

						
							        "servicequotas:ListRequestedServiceQuotaChangeHistory",
						 

						
							        "servicequotas:ListRequestedServiceQuotaChangeHistoryByQuota",
						 

						
							        "sts:DecodeAuthorizationMessage",
						 

						
							        "ds:*",
						 

						
							        "workspaces:*",
						 

						
							        "iam:GetRole",
						 

						
							        "iam:GetContextKeysForPrincipalPolicy",
						 

						
							        "iam:SimulatePrincipalPolicy"
						 

						
							      ],
						 

						
							      "Resource": "*"
						 

						
							    }
						 

						
							  ]
						 

						
							}
						 

						
							 EOF
						 

						
							 
						 

						
							}
						 
					
				
			
		
	



	 
	Terraform will now create these entities:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\_TERRAFORM\_AWSCore&gt; terraform plan
					 
					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_iam_role.AWC-IAM-Main-Role will be created 
					  + resource "aws_iam_role" "AWC-IAM-Main-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                  = "AWC-IAM-Main-Role" 
					      + name_prefix           = (known after apply) 
					      + path                  = "/" 
					      + tags_all              = (known after apply) 
					      + unique_id             = (known after apply) 
					 
					      + inline_policy (known after apply) 
					    } 
					 
					  # aws_iam_role_policy.AWC-IAM-Main will be created 
					  + resource "aws_iam_role_policy" "AWC-IAM-Main" { 
					      + id          = (known after apply) 
					      + name        = "AWC-IAM-Role-Main" 
					      + name_prefix = (known after apply) 
					      + policy      = jsonencode( 
					            { 
					              + Statement = [ 
					                  + { 
					                      + Action   = [ 
					                          + "workdocs:DeregisterDirectory", 
					                          + "workdocs:RegisterDirectory", 
					                          + "workdocs:AddUserToGroup", 
					                          + "ec2:ImportInstance", 
					                          + "ec2:DescribeImages", 
					                          + "ec2:DescribeImageAttribute", 
					                          + "ec2:CreateKeyPair", 
					                          + "ec2:DescribeKeyPairs", 
					                          + "ec2:ModifyImageAttribute", 
					                          + "ec2:DescribeVpcs", 
					                          + "ec2:DescribeSubnets", 
					                          + "ec2:RunInstances", 
					                          + "ec2:DescribeSecurityGroups", 
					                          + "ec2:CreateTags", 
					                          + "ec2:DescribeRouteTables", 
					                          + "ec2:DescribeInternetGateways", 
					                          + "ec2:CreateSecurityGroup", 
					                          + "ec2:DescribeInstanceTypes", 
					                          + "servicequotas:ListServices", 
					                          + "servicequotas:GetRequestedServiceQuotaChange", 
					                          + "servicequotas:ListTagsForResource", 
					                          + "servicequotas:GetServiceQuota", 
					                          + "servicequotas:GetAssociationForServiceQuotaTemplate", 
					                          + "servicequotas:ListAWSDefaultServiceQuotas", 
					                          + "servicequotas:ListServiceQuotas", 
					                          + "servicequotas:GetAWSDefaultServiceQuota", 
					                          + "servicequotas:GetServiceQuotaIncreaseRequestFromTemplate", 
					                          + "servicequotas:ListServiceQuotaIncreaseRequestsInTemplate", 
					                          + "servicequotas:ListRequestedServiceQuotaChangeHistory", 
					                          + "servicequotas:ListRequestedServiceQuotaChangeHistoryByQuota", 
					                          + "sts:DecodeAuthorizationMessage", 
					                          + "ds:*", 
					                          + "workspaces:*", 
					                          + "iam:GetRole", 
					                          + "iam:GetContextKeysForPrincipalPolicy", 
					                          + "iam:SimulatePrincipalPolicy", 
					                        ] 
					                      + Effect   = "Allow" 
					                      + Resource = "*" 
					                      + Sid      = "VisualEditor0" 
					                    }, 
					                ] 
					              + Version   = "2012-10-17" 
					            } 
					        ) 
					      + role        = (known after apply) 
					    } 
					 
					Plan: 2 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:\_TERRAFORM\_AWSCore&gt; terraform apply 
					 
					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_iam_role.AWC-IAM-Main-Role will be created 
					  + resource "aws_iam_role" "AWC-IAM-Main-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                  = "AWC-IAM-Main-Role" 
					      + name_prefix           = (known after apply) 
					      + path                  = "/" 
					      + tags_all              = (known after apply) 
					      + unique_id             = (known after apply) 
					 
					      + inline_policy (known after apply) 
					    } 
					 
					  # aws_iam_role_policy.AWC-IAM-Main will be created 
					  + resource "aws_iam_role_policy" "AWC-IAM-Main" { 
					      + id          = (known after apply) 
					      + name        = "AWC-IAM-Role-Main" 
					      + name_prefix = (known after apply) 
					      + policy      = jsonencode( 
					            { 
					              + Statement = [ 
					                  + { 
					                      + Action   = [ 
					                          + "workdocs:DeregisterDirectory", 
					                          + "workdocs:RegisterDirectory", 
					                          + "workdocs:AddUserToGroup", 
					                          + "ec2:ImportInstance", 
					                          + "ec2:DescribeImages", 
					                          + "ec2:DescribeImageAttribute", 
					                          + "ec2:CreateKeyPair", 
					                          + "ec2:DescribeKeyPairs", 
					                          + "ec2:ModifyImageAttribute", 
					                          + "ec2:DescribeVpcs", 
					                          + "ec2:DescribeSubnets", 
					                          + "ec2:RunInstances", 
					                          + "ec2:DescribeSecurityGroups", 
					                          + "ec2:CreateTags", 
					                          + "ec2:DescribeRouteTables", 
					                          + "ec2:DescribeInternetGateways", 
					                          + "ec2:CreateSecurityGroup", 
					                          + "ec2:DescribeInstanceTypes", 
					                          + "servicequotas:ListServices", 
					                          + "servicequotas:GetRequestedServiceQuotaChange", 
					                          + "servicequotas:ListTagsForResource", 
					                          + "servicequotas:GetServiceQuota", 
					                          + "servicequotas:GetAssociationForServiceQuotaTemplate", 
					                          + "servicequotas:ListAWSDefaultServiceQuotas", 
					                          + "servicequotas:ListServiceQuotas", 
					                          + "servicequotas:GetAWSDefaultServiceQuota", 
					                          + "servicequotas:GetServiceQuotaIncreaseRequestFromTemplate", 
					                          + "servicequotas:ListServiceQuotaIncreaseRequestsInTemplate", 
					                          + "servicequotas:ListRequestedServiceQuotaChangeHistory", 
					                          + "servicequotas:ListRequestedServiceQuotaChangeHistoryByQuota", 
					                          + "sts:DecodeAuthorizationMessage", 
					                          + "ds:*", 
					                          + "workspaces:*", 
					                          + "iam:GetRole", 
					                          + "iam:GetContextKeysForPrincipalPolicy", 
					                          + "iam:SimulatePrincipalPolicy", 
					                        ] 
					                      + Effect   = "Allow" 
					                      + Resource = "*" 
					                      + Sid      = "VisualEditor0" 
					                    }, 
					                ] 
					              + Version   = "2012-10-17" 
					            } 
					        ) 
					      + role        = (known after apply) 
					    } 
					 
					Plan: 2 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_iam_role.AWC-IAM-Main-Role: Creating... 
					aws_iam_role.AWC-IAM-Main-Role: Creation complete after 1s [id=AWC-IAM-Main-Role] 
					aws_iam_role_policy.AWC-IAM-Main: Creating... 
					aws_iam_role_policy.AWC-IAM-Main: Creation complete after 0s [id=AWC-IAM-Main-Role:AWC-IAM-Role-Main] 
					 
					Apply complete! Resources: 2 added, 0 changed, 0 destroyed. 
					 
					PS C:\_TERRAFORM\_AWSCore&gt; 
				
			
		
	



	Terraform successfully created the initial IAM policy.
 


	As the various needs for IAM Policies are too divergent, the detailed description of the deployment of further IAM policies is out-of-scope of this guide.
 


	
		Note: 
	 

	
		For more information about Gateways, please visit Amazon´s Product Documentation pages. 
		 
	 



	 
 


	Deploying Security Groups on the Network Entities 



	If you associate a Security Group with an EC2 instance, it controls the instance's inbound and outbound traffic.
 


	
		Note: 
	 

	
		Security Groups enable you to increase security for the resources in your VPC as they act like virtual firewalls. 
		 
	 



	When you create a VPC, it comes with a default Security Group. You can create additional Security Groups for a VPC, each with their own inbound and outbound rules:
 


	You can specify each inbound rule's source, port range, and protocol. 
	You can specify each outbound rule's destination, port range, and protocol.
 


	The following diagram shows a VPC with a Subnet, an Internet Gateway, and a Security Group. 
	The Subnet contains an EC2 instance. The Security Group is assigned to the instance and acts as a virtual firewall. The only traffic that reaches the instance is the traffic allowed by the Security Group rules. 
 


	
 


	For the Security Group bound to the Instances in the Public Subnets, use the following rules:
 


	Type: HTTP 
	Protocol: TCP 
	Port range: 80 
	Source: Custom 0.0.0.0/0
 


	Type: HTTPS 
	Protocol: TCP 
	Port range: 443 
	Source: Custom 0.0.0.0/0
 


	Type: RDP 
	Protocol: TCP 
	Port range: 3389 
	Source: MyIP – your current external IP should be entered automatically
 


	Create more Inbound Rules according to your needs. 
	 
 


	For the Security Group bound to the Instances in the Private Subnets, use the following rules:
 


	Type: HTTP 
	Protocol: TCP 
	Port range: 80 
	Source: Custom 0.0.0.0/0
 


	Type: HTTPS 
	Protocol: TCP 
	Port range: 443 
	Source: Custom 0.0.0.0/0
 


	Type: RDP 
	Protocol: TCP 
	Port range: 3389 
	Source: Custom 0.0.0.0/0
 


	Type: Custom TCP 
	Protocol: TCP 
	Port range: 1494 
	Source: Custom 0.0.0.0/0
 


	Type: Custom UDP 
	Protocol: UDP 
	Port range: 1494 
	Source: Custom 0.0.0.0/0
 


	Type: Custom TCP 
	Protocol: TCP 
	Port range: 2598 
	Source: Custom 0.0.0.0/0
 


	Type: Custom UDP 
	Protocol: UDP 
	Port range: 2598 
	Source: Custom 0.0.0.0/0
 


	Create more Inbound Rules according to your needs. 
 


	Use Terraform to create the Security Groups and their Rules:
 


	
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #1e1e1e;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #1e1e1e;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}	
	
		
			
				
					
						
							### Create Internal-bound Security Group
						 

						
							resource "aws_security_group" "AWC-SG-Internal" {
						 

						
							  name        = "SG-Internal"
						 

						
							  vpc_id      = aws_vpc.AWC-VPC.id
						 

						
							  tags                  = {
						 

						
							                              Name = "AWC-TF-SecurityGroup-Internal" 
						 

						
							  }
						 

						
							}
						 

						
							 
						 

						
							### Create Public-Security Group
						 

						
							resource "aws_security_group" "AWC-SG-Public" {
						 

						
							  name        = "SG-External"
						 

						
							  vpc_id      = aws_vpc.AWC-VPC.id
						 

						
							  tags                  = {
						 

						
							                              Name = "AWC-TF-SecurityGroup-Public" 
						 

						
							  }
						 

						
							}
						 

						
							 
						 

						
							#### Create the Security Group Rules
						 

						
							resource "aws_vpc_security_group_ingress_rule" "AWC-SG-Int-IR-HTTPS" {
						 

						
							  security_group_id = aws_security_group.AWC-SG-Internal.id
						 

						
							  cidr_ipv4         = "0.0.0.0/0"
						 

						
							  from_port         = 0
						 

						
							  ip_protocol       = "tcp"
						 

						
							  to_port           = 443
						 

						
							}
						 

						
							 
						 

						
							resource "aws_vpc_security_group_ingress_rule" "AWC-SG-Int-IR-HTTP" {
						 

						
							  security_group_id = aws_security_group.AWC-SG-Internal.id
						 

						
							  cidr_ipv4         = "0.0.0.0/0"
						 

						
							  from_port         = 0
						 

						
							  ip_protocol       = "tcp"
						 

						
							  to_port           = 80
						 

						
							}
						 

						
							 
						 

						
							resource "aws_vpc_security_group_ingress_rule" "AWC-SG-Public-IR-HTTPS" {
						 

						
							  security_group_id = aws_security_group.AWC-SG-Public.id
						 

						
							  cidr_ipv4         = "0.0.0.0/0"
						 

						
							  from_port         = 0
						 

						
							  ip_protocol       = "tcp"
						 

						
							  to_port           = 443
						 

						
							}
						 

						
							 
						 

						
							resource "aws_vpc_security_group_ingress_rule" "AWC-SG-Public-IR-HTTP" {
						 

						
							  security_group_id = aws_security_group.AWC-SG-Public.id
						 

						
							  cidr_ipv4         = "0.0.0.0/0"
						 

						
							  from_port         = 0
						 

						
							  ip_protocol       = "tcp"
						 

						
							  to_port           = 80
						 

						
							}
						 

						
							 
						 

						
							resource "aws_vpc_security_group_ingress_rule" "AWC-SG-Pub-IR-RDP" {
						 

						
							  security_group_id = aws_security_group.AWC-SG-Public.id
						 

						
							  cidr_ipv4         = XXXXXXXXXX
						 

						
							  from_port         = 0
						 

						
							  ip_protocol       = "tcp"
						 

						
							  to_port           = 3389
						 

						
							}
						 

						
							 
						 

						
							 
						 

						
							resource "aws_vpc_security_group_ingress_rule" "AWC-SG-Int-IR-RDP" {
						 

						
							  security_group_id = aws_security_group.AWC-SG-Internal.id
						 

						
							  cidr_ipv4         = "0.0.0.0/0"
						 

						
							  from_port         = 0
						 

						
							  ip_protocol       = "tcp"
						 

						
							  to_port           = 3389
						 

						
							}
						 

						
							 
						 

						
							resource "aws_vpc_security_group_ingress_rule" "AWC-SG-Int-IR-ICATCP" {
						 

						
							  security_group_id = aws_security_group.AWC-SG-Internal.id
						 

						
							  cidr_ipv4         = "0.0.0.0/0"
						 

						
							  from_port         = 0
						 

						
							  ip_protocol       = "tcp"
						 

						
							  to_port           = 1494
						 

						
							}
						 

						
							 
						 

						
							resource "aws_vpc_security_group_ingress_rule" "AWC-SG-Int-IR-ICAUDP" {
						 

						
							  security_group_id = aws_security_group.AWC-SG-Internal.id
						 

						
							  cidr_ipv4         = "0.0.0.0/0"
						 

						
							  from_port         = 0
						 

						
							  ip_protocol       = "udp"
						 

						
							  to_port           = 1494
						 

						
							}
						 

						
							 
						 

						
							resource "aws_vpc_security_group_ingress_rule" "AWC-SG-Int-IR-CGPTCP" {
						 

						
							  security_group_id = aws_security_group.AWC-SG-Internal.id
						 

						
							  cidr_ipv4         = "0.0.0.0/0"
						 

						
							  from_port         = 0
						 

						
							  ip_protocol       = "tcp"
						 

						
							  to_port           = 2598
						 

						
							}
						 

						
							 
						 

						
							resource "aws_vpc_security_group_ingress_rule" "AWC-SG-Int-IR-CGPUDP" {
						 

						
							  security_group_id = aws_security_group.AWC-SG-Internal.id
						 

						
							  cidr_ipv4         = "0.0.0.0/0"
						 

						
							  from_port         = 0
						 

						
							  ip_protocol       = "udp"
						 

						
							  to_port           = 2598
						 

						
							}
						 

						
							 
						 

						
							resource "aws_vpc_security_group_ingress_rule" "AWC-SG-Int-IR-ICMP" {
						 

						
							  security_group_id = aws_security_group.AWC-SG-Internal.id
						 

						
							  cidr_ipv4         = "0.0.0.0/0"
						 

						
							  from_port         = 0
						 

						
							  ip_protocol       = "icmp"
						 

						
							  to_port           = 0
						 

						
							}
						 

						
							 
						 

						
							resource "aws_vpc_security_group_egress_rule" "AWC-SG-Int-ER-All" {
						 

						
							  security_group_id = aws_security_group.AWC-SG-Internal.id
						 

						
							  cidr_ipv4         = "0.0.0.0/0"
						 

						
							  from_port         = 0
						 

						
							  ip_protocol       = "-1"
						 

						
							  to_port           = 0
						 

						
							}
						 

						
							 
						 

						
							resource "aws_vpc_security_group_egress_rule" "AWC-SG-Ext-ER-All" {
						 

						
							  security_group_id = aws_security_group.AWC-SG-External.id
						 

						
							  cidr_ipv4         = "0.0.0.0/0"
						 

						
							  from_port         = 0
						 

						
							  ip_protocol       = "-1"
						 

						
							  to_port           = 0
						 

						
							}
						 

						
							 
						 
					
				
			
		
	



	 
	Terraform will now create these entities:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\_TERRAFORM\_AWSCore&gt; terraform plan
					 

					
						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_security_group.AWS-SG-Internal will be created 
						  + resource "aws_security_group" "AWS-SG-Internal" { 
						      + arn                    = (known after apply) 
						      + description            = "Managed by Terraform" 
						      + egress                 = (known after apply) 
						      + id                     = (known after apply) 
						      + ingress                = (known after apply) 
						      + name                   = "SG-Internal" 
						      + name_prefix            = (known after apply) 
						      + owner_id               = (known after apply) 
						      + revoke_rules_on_delete = false 
						      + tags                   = { 
						          + "Name" = "AWC-TF-SecurityGroup-Internal" 
						        } 
						      + tags_all               = { 
						          + "Name" = "AWC-TF-SecurityGroup-Internal" 
						        } 
						      + vpc_id                 = "vpc-0a0c4e01213dca45a" 
						    }
					 

					
						  # aws_security_group.AWS-SG-Public will be created 
						  + resource "aws_security_group" "AWS-SG-Public" { 
						      + arn                    = (known after apply) 
						      + description            = "Managed by Terraform" 
						      + egress                 = (known after apply) 
						      + id                     = (known after apply) 
						      + ingress                = (known after apply) 
						      + name                   = "SG-Public" 
						      + name_prefix            = (known after apply) 
						      + owner_id               = (known after apply) 
						      + revoke_rules_on_delete = false 
						      + tags                   = { 
						          + "Name" = "AWC-TF-SecurityGroup-Public" 
						        } 
						      + tags_all               = { 
						          + "Name" = "AWC-TF-SecurityGroup-Public" 
						        } 
						      + vpc_id                 = "vpc-0a0c4e01213dca45a" 
						    }
					 

					
						# aws_vpc_security_group_egress_rule.AWC-SG-Int-ER-All will be created 
						  + resource "aws_vpc_security_group_egress_rule" "AWC-SG-Int-ER-All" { 
						      + arn                    = (known after apply) 
						      + cidr_ipv4              = "0.0.0.0/0" 
						      + from_port              = 0 
						      + id                     = (known after apply) 
						      + ip_protocol            = "-1" 
						      + security_group_id      = (known after apply) 
						      + security_group_rule_id = (known after apply) 
						      + tags_all               = {} 
						      + to_port                = 0 
						    }
					 

					
						# aws_vpc_security_group_egress_rule.AWC-SG-Pub-ER-All will be created 
						  + resource "aws_vpc_security_group_egress_rule" "AWC-SG-Pub-ER-All" { 
						      + arn                    = (known after apply) 
						      + cidr_ipv4              = "0.0.0.0/0" 
						      + from_port              = 0 
						      + id                     = (known after apply) 
						      + ip_protocol            = "-1" 
						      + security_group_id      = (known after apply) 
						      + security_group_rule_id = (known after apply) 
						      + tags_all               = {} 
						      + to_port                = 0 
						    } 
						 
						  # aws_vpc_security_group_ingress_rule.AWC-SG-Int-IR-CGPTCP will be created 
						  + resource "aws_vpc_security_group_ingress_rule" "AWC-SG-Int-IR-CGPTCP" { 
						      + arn                    = (known after apply) 
						      + cidr_ipv4              = "0.0.0.0/0" 
						      + from_port              = 0 
						      + id                     = (known after apply) 
						      + ip_protocol            = "tcp" 
						      + security_group_id      = (known after apply) 
						      + security_group_rule_id = (known after apply) 
						      + tags_all               = {} 
						      + to_port                = 2598 
						    } 
						 
						  # aws_vpc_security_group_ingress_rule.AWC-SG—Int-IR-CGPUDP will be created 
						  + resource "aws_vpc_security_group_ingress_rule" "AWC-SG-Int-IR-CGPUDP" { 
						      + arn                    = (known after apply) 
						      + cidr_ipv4              = "0.0.0.0/0" 
						      + from_port              = 0 
						      + id                     = (known after apply) 
						      + ip_protocol            = "udp" 
						      + security_group_id      = (known after apply) 
						      + security_group_rule_id = (known after apply) 
						      + tags_all               = {} 
						      + to_port                = 2598 
						    } 
						 
						  # aws_vpc_security_group_ingress_rule.AWC-SG—Int-IR-HTTP will be created 
						  + resource "aws_vpc_security_group_ingress_rule" "AWC-SG-Int-IR-HTTP" { 
						      + arn                    = (known after apply) 
						      + cidr_ipv4              = "0.0.0.0/0" 
						      + from_port              = 0 
						      + id                     = (known after apply) 
						      + ip_protocol            = "tcp" 
						      + security_group_id      = (known after apply) 
						      + security_group_rule_id = (known after apply) 
						      + tags_all               = {} 
						      + to_port                = 80 
						    } 
						 
						  # aws_vpc_security_group_ingress_rule.AWC-SG-Int-IR-HTTPS will be created 
						  + resource "aws_vpc_security_group_ingress_rule" "AWC-SG-Int-IR-HTTPS" { 
						      + arn                    = (known after apply) 
						      + cidr_ipv4              = "0.0.0.0/0" 
						      + from_port              = 0 
						      + id                     = (known after apply) 
						      + ip_protocol            = "tcp" 
						      + security_group_id      = (known after apply) 
						      + security_group_rule_id = (known after apply) 
						      + tags_all               = {} 
						      + to_port                = 443 
						    }
					 

					
						# aws_vpc_security_group_ingress_rule.AWC-SG—Pub-IR-HTTP will be created 
						  + resource "aws_vpc_security_group_ingress_rule" "AWC-SG-Pub-IR-HTTP" { 
						      + arn                    = (known after apply) 
						      + cidr_ipv4              = "0.0.0.0/0" 
						      + from_port              = 0 
						      + id                     = (known after apply) 
						      + ip_protocol            = "tcp" 
						      + security_group_id      = (known after apply) 
						      + security_group_rule_id = (known after apply) 
						      + tags_all               = {} 
						      + to_port                = 80 
						    } 
						 
						  # aws_vpc_security_group_ingress_rule.AWC-SG-Pub-IR-HTTPS will be created 
						  + resource "aws_vpc_security_group_ingress_rule" "AWC-SG-Pub-IR-HTTPS" { 
						      + arn                    = (known after apply) 
						      + cidr_ipv4              = "0.0.0.0/0" 
						      + from_port              = 0 
						      + id                     = (known after apply) 
						      + ip_protocol            = "tcp" 
						      + security_group_id      = (known after apply) 
						      + security_group_rule_id = (known after apply) 
						      + tags_all               = {} 
						      + to_port                = 443 
						    } 
						 
						  # aws_vpc_security_group_ingress_rule.AWC-SG-Int-IR-ICATCP will be created 
						  + resource "aws_vpc_security_group_ingress_rule" "AWC-SG-Int-IR-ICATCP" { 
						      + arn                    = (known after apply) 
						      + cidr_ipv4              = "0.0.0.0/0" 
						      + from_port              = 0 
						      + id                     = (known after apply) 
						      + ip_protocol            = "tcp" 
						      + security_group_id      = (known after apply) 
						      + security_group_rule_id = (known after apply) 
						      + tags_all               = {} 
						      + to_port                = 1494 
						    } 
						 
						  # aws_vpc_security_group_ingress_rule.AWC-SG-Int-IR-ICAUDP will be created 
						  + resource "aws_vpc_security_group_ingress_rule" "AWC-SG-Int-IR-ICAUDP" { 
						      + arn                    = (known after apply) 
						      + cidr_ipv4              = "0.0.0.0/0" 
						      + from_port              = 0 
						      + id                     = (known after apply) 
						      + ip_protocol            = "udp" 
						      + security_group_id      = (known after apply) 
						      + security_group_rule_id = (known after apply) 
						      + tags_all               = {} 
						      + to_port                = 1494 
						    } 
						 
						  # aws_vpc_security_group_ingress_rule.AWC-SG-Int-IR-ICMP will be created 
						  + resource "aws_vpc_security_group_ingress_rule" "AWC-SG-Int-IR-ICMP" { 
						      + arn                    = (known after apply) 
						      + cidr_ipv4              = "0.0.0.0/0" 
						      + from_port              = 0 
						      + id                     = (known after apply) 
						      + ip_protocol            = "icmp" 
						      + security_group_id      = (known after apply) 
						      + security_group_rule_id = (known after apply) 
						      + tags_all               = {} 
						      + to_port                = 0 
						    } 
						 
						  # aws_vpc_security_group_ingress_rule.AWC-SG-Int-IR-RDP will be created 
						  + resource "aws_vpc_security_group_ingress_rule" "AWC-SG-Int-IR-RDP" { 
						      + arn                    = (known after apply) 
						      + cidr_ipv4              = "0.0.0.0/0" 
						      + from_port              = 0 
						      + id                     = (known after apply) 
						      + ip_protocol            = "tcp" 
						      + security_group_id      = (known after apply) 
						      + security_group_rule_id = (known after apply) 
						      + tags_all               = {} 
						      + to_port                = 3389 
						    } 
						 
					 

					
						# aws_vpc_security_group_ingress_rule.AWC-SG-Pub-IR-RDP will be created 
						  + resource "aws_vpc_security_group_ingress_rule" "AWC-SG-Pub-IR-RDP" { 
						      + arn                    = (known after apply) 
						      + cidr_ipv4              = "XXXXXXXXXXXXX" 
						      + from_port              = 0 
						      + id                     = (known after apply) 
						      + ip_protocol            = "tcp" 
						      + security_group_id      = (known after apply) 
						      + security_group_rule_id = (known after apply) 
						      + tags_all               = {} 
						      + to_port                = 3389 
						    }
					 

					
						 
						Plan: 15 to add, 0 to change, 0 to destroy. 
						 
						─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 
						 
						PS C:\_TERRAFORM\_AWSCore&gt; terraform apply 
						 
						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_security_group.AWS-SG-Internal will be created 
						  + resource "aws_security_group" "AWS-SG-Internal" { 
						      + arn                    = (known after apply) 
						      + description            = "Managed by Terraform" 
						      + egress                 = (known after apply) 
						      + id                     = (known after apply) 
						      + ingress                = (known after apply) 
						      + name                   = "SG-Internal" 
						      + name_prefix            = (known after apply) 
						      + owner_id               = (known after apply) 
						      + revoke_rules_on_delete = false 
						      + tags                   = { 
						          + "Name" = "AWC-TF-SecurityGroup-Internal" 
						        } 
						      + tags_all               = { 
						          + "Name" = "AWC-TF-SecurityGroup-Internal" 
						        } 
						      + vpc_id                 = "vpc-0a0c4e01213dca45a" 
						    }
					 

					
						  # aws_security_group.AWS-SG-Public will be created 
						  + resource "aws_security_group" "AWS-SG-Public" { 
						      + arn                    = (known after apply) 
						      + description            = "Managed by Terraform" 
						      + egress                 = (known after apply) 
						      + id                     = (known after apply) 
						      + ingress                = (known after apply) 
						      + name                   = "SG-Public" 
						      + name_prefix            = (known after apply) 
						      + owner_id               = (known after apply) 
						      + revoke_rules_on_delete = false 
						      + tags                   = { 
						          + "Name" = "AWC-TF-SecurityGroup-Public" 
						        } 
						      + tags_all               = { 
						          + "Name" = "AWC-TF-SecurityGroup-Public" 
						        } 
						      + vpc_id                 = "vpc-0a0c4e01213dca45a" 
						    }
					 

					
						# aws_vpc_security_group_egress_rule.AWC-SG-Int-ER-All will be created 
						  + resource "aws_vpc_security_group_egress_rule" "AWC-SG-Int-ER-All" { 
						      + arn                    = (known after apply) 
						      + cidr_ipv4              = "0.0.0.0/0" 
						      + from_port              = 0 
						      + id                     = (known after apply) 
						      + ip_protocol            = "-1" 
						      + security_group_id      = (known after apply) 
						      + security_group_rule_id = (known after apply) 
						      + tags_all               = {} 
						      + to_port                = 0 
						    }
					 

					
						# aws_vpc_security_group_egress_rule.AWC-SG-Pub-ER-All will be created 
						  + resource "aws_vpc_security_group_egress_rule" "AWC-SG-Pub-ER-All" { 
						      + arn                    = (known after apply) 
						      + cidr_ipv4              = "0.0.0.0/0" 
						      + from_port              = 0 
						      + id                     = (known after apply) 
						      + ip_protocol            = "-1" 
						      + security_group_id      = (known after apply) 
						      + security_group_rule_id = (known after apply) 
						      + tags_all               = {} 
						      + to_port                = 0 
						    } 
						 
						  # aws_vpc_security_group_ingress_rule.AWC-SG-Int-IR-CGPTCP will be created 
						  + resource "aws_vpc_security_group_ingress_rule" "AWC-SG-Int-IR-CGPTCP" { 
						      + arn                    = (known after apply) 
						      + cidr_ipv4              = "0.0.0.0/0" 
						      + from_port              = 0 
						      + id                     = (known after apply) 
						      + ip_protocol            = "tcp" 
						      + security_group_id      = (known after apply) 
						      + security_group_rule_id = (known after apply) 
						      + tags_all               = {} 
						      + to_port                = 2598 
						    } 
						 
						  # aws_vpc_security_group_ingress_rule.AWC-SG—Int-IR-CGPUDP will be created 
						  + resource "aws_vpc_security_group_ingress_rule" "AWC-SG-Int-IR-CGPUDP" { 
						      + arn                    = (known after apply) 
						      + cidr_ipv4              = "0.0.0.0/0" 
						      + from_port              = 0 
						      + id                     = (known after apply) 
						      + ip_protocol            = "udp" 
						      + security_group_id      = (known after apply) 
						      + security_group_rule_id = (known after apply) 
						      + tags_all               = {} 
						      + to_port                = 2598 
						    } 
						 
						  # aws_vpc_security_group_ingress_rule.AWC-SG—Int-IR-HTTP will be created 
						  + resource "aws_vpc_security_group_ingress_rule" "AWC-SG-Int-IR-HTTP" { 
						      + arn                    = (known after apply) 
						      + cidr_ipv4              = "0.0.0.0/0" 
						      + from_port              = 0 
						      + id                     = (known after apply) 
						      + ip_protocol            = "tcp" 
						      + security_group_id      = (known after apply) 
						      + security_group_rule_id = (known after apply) 
						      + tags_all               = {} 
						      + to_port                = 80 
						    } 
						 
						  # aws_vpc_security_group_ingress_rule.AWC-SG-Int-IR-HTTPS will be created 
						  + resource "aws_vpc_security_group_ingress_rule" "AWC-SG-Int-IR-HTTPS" { 
						      + arn                    = (known after apply) 
						      + cidr_ipv4              = "0.0.0.0/0" 
						      + from_port              = 0 
						      + id                     = (known after apply) 
						      + ip_protocol            = "tcp" 
						      + security_group_id      = (known after apply) 
						      + security_group_rule_id = (known after apply) 
						      + tags_all               = {} 
						      + to_port                = 443 
						    }
					 

					
						# aws_vpc_security_group_ingress_rule.AWC-SG—Pub-IR-HTTP will be created 
						  + resource "aws_vpc_security_group_ingress_rule" "AWC-SG-Pub-IR-HTTP" { 
						      + arn                    = (known after apply) 
						      + cidr_ipv4              = "0.0.0.0/0" 
						      + from_port              = 0 
						      + id                     = (known after apply) 
						      + ip_protocol            = "tcp" 
						      + security_group_id      = (known after apply) 
						      + security_group_rule_id = (known after apply) 
						      + tags_all               = {} 
						      + to_port                = 80 
						    } 
						 
						  # aws_vpc_security_group_ingress_rule.AWC-SG-Pub-IR-HTTPS will be created 
						  + resource "aws_vpc_security_group_ingress_rule" "AWC-SG-Pub-IR-HTTPS" { 
						      + arn                    = (known after apply) 
						      + cidr_ipv4              = "0.0.0.0/0" 
						      + from_port              = 0 
						      + id                     = (known after apply) 
						      + ip_protocol            = "tcp" 
						      + security_group_id      = (known after apply) 
						      + security_group_rule_id = (known after apply) 
						      + tags_all               = {} 
						      + to_port                = 443 
						    } 
						 
						 
						  # aws_vpc_security_group_ingress_rule.AWC-SG-Int-IR-ICATCP will be created 
						  + resource "aws_vpc_security_group_ingress_rule" "AWC-SG-Int-IR-ICATCP" { 
						      + arn                    = (known after apply) 
						      + cidr_ipv4              = "0.0.0.0/0" 
						      + from_port              = 0 
						      + id                     = (known after apply) 
						      + ip_protocol            = "tcp" 
						      + security_group_id      = (known after apply) 
						      + security_group_rule_id = (known after apply) 
						      + tags_all               = {} 
						      + to_port                = 1494 
						    } 
						 
						  # aws_vpc_security_group_ingress_rule.AWC-SG-Int-IR-ICAUDP will be created 
						  + resource "aws_vpc_security_group_ingress_rule" "AWC-SG-Int-IR-ICAUDP" { 
						      + arn                    = (known after apply) 
						      + cidr_ipv4              = "0.0.0.0/0" 
						      + from_port              = 0 
						      + id                     = (known after apply) 
						      + ip_protocol            = "udp" 
						      + security_group_id      = (known after apply) 
						      + security_group_rule_id = (known after apply) 
						      + tags_all               = {} 
						      + to_port                = 1494 
						    } 
						 
						  # aws_vpc_security_group_ingress_rule.AWC-SG-Int-IR-ICMP will be created 
						  + resource "aws_vpc_security_group_ingress_rule" "AWC-SG-Int-IR-ICMP" { 
						      + arn                    = (known after apply) 
						      + cidr_ipv4              = "0.0.0.0/0" 
						      + from_port              = 0 
						      + id                     = (known after apply) 
						      + ip_protocol            = "icmp" 
						      + security_group_id      = (known after apply) 
						      + security_group_rule_id = (known after apply) 
						      + tags_all               = {} 
						      + to_port                = 0 
						    } 
						 
						  # aws_vpc_security_group_ingress_rule.AWC-SG-Int-IR-RDP will be created 
						  + resource "aws_vpc_security_group_ingress_rule" "AWC-SG-Int-IR-RDP" { 
						      + arn                    = (known after apply) 
						      + cidr_ipv4              = "0.0.0.0/0" 
						      + from_port              = 0 
						      + id                     = (known after apply) 
						      + ip_protocol            = "tcp" 
						      + security_group_id      = (known after apply) 
						      + security_group_rule_id = (known after apply) 
						      + tags_all               = {} 
						      + to_port                = 3389 
						    } 
						 
					 

					
						# aws_vpc_security_group_ingress_rule.AWC-SG-Pub-IR-RDP will be created 
						  + resource "aws_vpc_security_group_ingress_rule" "AWC-SG-Pub-IR-RDP" { 
						      + arn                    = (known after apply) 
						      + cidr_ipv4              = "XXXXXXXXXXXXX" 
						      + from_port              = 0 
						      + id                     = (known after apply) 
						      + ip_protocol            = "tcp" 
						      + security_group_id      = (known after apply) 
						      + security_group_rule_id = (known after apply) 
						      + tags_all               = {} 
						      + to_port                = 3389 
						    }
					 

					
						 
						Plan: 15 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_security_group.AWS-SG-Internal: Creation complete after 1s [id=sg-0633dbccc6f7debdc] 
						aws_security_group.AWS-SG-Public: Creation complete after 1s [id=sg-06a8ab84b0a692170] 
						aws_vpc_security_group_ingress_rule.AWC-SG-Int-IR-ICATCP: Creating... 
						aws_vpc_security_group_ingress_rule.AWC-SG-Int-IR-HTTP: Creating... 
						aws_vpc_security_group_ingress_rule.AWC-SG-Int-IR-CGPUDP: Creating... 
						aws_vpc_security_group_ingress_rule.AWC-SG-Int-IR-ICMP: Creating... 
						aws_vpc_security_group_ingress_rule.AWC-SG-Int-IR-ICAUDP: Creating... 
						... 
						aws_vpc_security_group_ingress_rule.AWC-SG-IR-ICATCP: Creation complete after 0s [id=sgr-01067ddbb01b277fa] 
						aws_vpc_security_group_ingress_rule.AWC-SG-Int-IR-CGPUDP: Creation complete after 0s [id=sgr-0c276bd82c1893367] 
						aws_vpc_security_group_ingress_rule.AWC-SG—Int-IR-RDP: Creation complete after 0s [id=sgr-036882befc2d63329] 
						aws_vpc_security_group_ingress_rule.AWC-SG-Int-IR-HTTPS: Creation complete after 0s [id=sgr-0f83b2b1e904eaf12] 
						aws_vpc_security_group_egress_rule.AWC-SG—Int-ER-All: Creation complete after 0s [id=sgr-0742921e3eee9d06b] 
						 
						Apply complete! Resources: 15 added, 0 changed, 0 destroyed.
					 
					PS C:\_TERRAFORM\_AWSCore&gt;
				
			
		
	



	Terraform successfully created the Security Groups and its needed Rules.
 


	
		Note: 
	 

	
		Please visit Amazon´s Product Documentation pages for more information about Security Groups. 
		 
	 



	 
 


	Deploy all needed Instances in your VPC



	An Amazon EC2 Instance is a Virtual Machine in the AWS Cloud environment. 
	Amazon EC2 provides a wide range of Instance types. You can choose an Instance type that provides the compute resources, memory, storage, and network configurations according to your needs. 
 


	In this guide, we will initially deploy the following Instances:
 


	
		1 Jump Host for accessing the environment over the Internet using RDP
	
	
		1 Domain Controller for providing all needed Active Directory functionalities
	
	
		2 Cloud Connectors
	



	Further Instances will be created automatically during the Machine Catalog creation.
 


	
		Important: 
	 

	
		You need an EC2 Key Pair to decrypt the administrator password to access a non-domain-joined Windows-based Instance. A Key Pair, consisting of a public key and a private key, is a set of security credentials you use to prove your identity. 
		After joining the Instance to a Domain, you can log on using the Domain credentials. 
		The Key Pair is used only for local Admin access! 
		 
	 



	Creating the EC2 Key Pair using the GUI



	Before deploying Windows Instances, you must create a Key Pair to gain access to the Instance after it is created.
 


	
		Log on to your AWS console, enter EC2 in the Search bar at the top of the page, and choose the Network &amp; Security option. Click on Key Pairs on the Dashboard. 
		If no Key Pair was already created, click Create key pair. 
		
	
	
		Choose the needed settings and click Create key pair. 
		
	



	
		Important: 
	 

	
		After successful creation, download and store the Key Pair in a safe place. 
		You will need it each time you access the Windows Instance with local administrator credentials. 
		 
	 



	 
 


	Creating the EC2 Key Pair using PowerShell



	The AWS EC2-Terraform provider is currently not supporting the creation of a Key Pair. 
	To automate this process, you must use PowerShell:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					PS C:\_TERRAFORM\_AWSCore&gt; $GetKeyPair = New-EC2KeyPair -KeyName AWC-KeyPair 
					PS C:\_TERRAFORM\_AWSCore&gt; $GetKeyPair 
					 
					 
					KeyFingerprint : c1:2b:01:8d:ba:4a:50:ed:41:d1:94:f7:d6:39:df:04:b3:f9:cb:dd 
					KeyMaterial    : -----BEGIN RSA PRIVATE KEY----- 
					                 MIIEpQIBAAKCAQEAskyHNFpSh+k+K+7ccyjEguX/VEcQKiYkf3ebAJ1VZSZJ0FnX 
					...                 +7IB7HL42zIt7oG29n35XycvXI6tg/mT7pEtnAehuolwtapdCFKUGjo= 
					                 -----END RSA PRIVATE KEY----- 
					KeyName        : AWC-KeyPair 
					KeyPairId      : key-00cb83cebc992e403 
					Tags           : {} 
					 
					 
					PS C:\_TERRAFORM\_AWSCore&gt; $GetKeyPair.KeyMaterial | Out-File -Encoding ascii “C:\_TERRAFORM\_AWSCore\WC-KeyPair.pem” 
					PS C:\_TERRAFORM\_AWSCore&gt;
				
			
		
	



	 
 


	Deploying the Jump Host Instance



	For security reasons, our environment will be completely shielded and deployed in the already-created Private Subnet. 
	Administrative access to the Instances is only possible using a dedicated Jump Host Instance in the Public Subnet. 
	 
 


	
		Important: 
	 

	
		Choose the right instance type that fits your needs. Each type offers different compute, memory, and storage capabilities and, therefore, different costs. 
		You can find an overview of all available Instance types at https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-types.html 
		 
		In this guide, we chose a t2.medium type for the Jump Host. 
		 
	 



	Adjust all relevant settings for the new Instance, such as Name and Tags, AMI Type and AMI Image, Instance Type, Key Pair to log in, Security Group to which this Instance will be bound, Storage configuration, and all advanced details, according to your needs.
 


	Use Terraform to create the Jumphost instance:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	



	
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #1e1e1e;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #1e1e1e;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}	
	
		
			
				
					
						
							resource "aws_instance" "AWC-Instance-Jumphost" {
						 

						
							  disable_api_termination   = "${var.AWC-Instance-Jumphost-API}"
						 

						
							  instance_type             = "${var.AWC-Instance-Jumphost-Instance}"
						 

						
							  ami                       = "${var.AWC-Instance-Jumphost-AMI}"
						 

						
							  iam_instance_profile      = "${aws_iam_instance_profile.AWC-Instance-Jumphost.name}"
						 

						
							  subnet_id                 = "${aws_subnet.AWC-Subnet-Pub-1.id}"
						 

						
							  vpc_security_group_ids    = [
						 

						
							                                  "${aws_security_group.AWS-SG-Public.id}"
						 

						
							              ]
						 

						
							 
						 

						
							  root_block_device {
						 

						
							    volume_type             = "gp2"
						 

						
							    volume_size             = "${var.AWC-Instance-Jumphost-Disk}"
						 

						
							    delete_on_termination   = true
						 

						
							              }
						 

						
							 
						 

						
							  tags                      = {
						 

						
							                                  Name = "AWC-Instance-Jumphost" 
						 

						
							  }
						 
						}
					
				
			
		
	



	 
	Terraform will now create these entities:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\_TERRAFORM\_AWSCore&gt; terraform plan
					 

					
						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_instance.AWC-Instance-Jumphost will be created 
						  + resource "aws_instance" "AWC-Instance-Jumphost" { 
						      + ami                                  = "ami-9e3bbefd" 
						      + 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              = false 
						      + 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                             = (known after apply) 
						      + 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                           = (known after apply) 
						      + 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-0c66b831b007a95dc" 
						      + tags                                 = { 
						          + "Name" = "AWC-Instance-Jumphost" 
						        } 
						      + tags_all                             = { 
						          + "Name" = "AWC-Instance-Jumphost" 
						        } 
						      + tenancy                              = (known after apply) 
						      + user_data                            = (known after apply) 
						      + user_data_base64                     = (known after apply) 
						      + user_data_replace_on_change          = false 
						      + vpc_security_group_ids               = [ 
						          + "sg-0633dbccc6f7debdc", 
						        ] 
						 
						      + capacity_reservation_specification (known after apply) 
						 
						      + cpu_options (known after apply) 
						 
						      + ebs_block_device (known after apply) 
						 
						      + enclave_options (known after apply) 
						 
						      + ephemeral_block_device (known after apply) 
						 
						      + instance_market_options (known after apply) 
						 
						      + maintenance_options (known after apply) 
						 
						      + metadata_options (known after apply) 
						 
						      + network_interface (known after apply) 
						 
						      + private_dns_name_options (known after apply) 
						 
						      + root_block_device { 
						          + delete_on_termination = true 
						          + device_name           = (known after apply) 
						          + encrypted             = (known after apply) 
						          + iops                  = (known after apply) 
						          + kms_key_id            = (known after apply) 
						          + tags_all              = (known after apply) 
						          + throughput            = (known after apply) 
						          + volume_id             = (known after apply) 
						          + volume_size           = 40 
						          + volume_type           = "gp2" 
						        } 
						    } 
						 
						Plan: 1 to add, 0 to change, 0 to destroy. 
						 
						─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 
						 
						PS C:\_TERRAFORM\_AWSCore&gt; terraform apply 
						 
						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_instance.AWC-Instance-Jumphost will be created 
						  + resource "aws_instance" "AWC-Instance-Jumphost" { 
						      + ami                                  = "ami-9e3bbefd" 
						      + 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              = false 
						      + 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                             = (known after apply) 
						      + 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                           = (known after apply) 
						      + 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-0c66b831b007a95dc" 
						      + tags                                 = { 
						          + "Name" = "AWC-Instance-Jumphost" 
						        } 
						      + tags_all                             = { 
						          + "Name" = "AWC-Instance-Jumphost" 
						        } 
						      + tenancy                              = (known after apply) 
						      + user_data                            = (known after apply) 
						      + user_data_base64                     = (known after apply) 
						      + user_data_replace_on_change          = false 
						      + vpc_security_group_ids               = [ 
						          + "sg-0633dbccc6f7debdc", 
						        ] 
						 
						      + capacity_reservation_specification (known after apply) 
						 
						      + cpu_options (known after apply) 
						 
						      + ebs_block_device (known after apply) 
						 
						      + enclave_options (known after apply) 
						 
						      + ephemeral_block_device (known after apply) 
						 
						      + instance_market_options (known after apply) 
						 
						      + maintenance_options (known after apply) 
						 
						      + metadata_options (known after apply) 
						 
						      + network_interface (known after apply) 
						 
						      + private_dns_name_options (known after apply) 
						 
						      + root_block_device { 
						          + delete_on_termination = true 
						          + device_name           = (known after apply) 
						          + encrypted             = (known after apply) 
						          + iops                  = (known after apply) 
						          + kms_key_id            = (known after apply) 
						          + tags_all              = (known after apply) 
						          + throughput            = (known after apply) 
						          + volume_id             = (known after apply) 
						          + volume_size           = 40 
						          + volume_type           = "gp2" 
						        } 
						    } 
						 
						Plan: 1 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.AWC-Instance-Jumphost: Creating... 
						... 
						aws_instance.AWC-Instance-Jumphost: Creation complete after 2m49s [id=i-983998321f2] 
						 
						Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
					 
					PS C:\_TERRAFORM\_AWSCore&gt; 
				
			
		
	



	Terraform successfully created the Jumphost Instance. 
	 
 


	Connecting to the Jump Host Instance Instance



	The Jump Host Instance is not Domain-joined for security reasons. 
	You need to log on to it using local administrator credentials.
 


	As mentioned in the Key Pair section, you need an EC2 Key Pair to decrypt the administrator password.
 


	
		To log on to the Instance, open the AWS console, enter EC2 in the Search bar at the top of the page, and choose the Instances option.
	
	
		Mark the Instance you want to connect to and click on Connect: 
		
	
	
		Choose RDP client as the connection method and click Get password: 
		
	
	
		Now upload the mentioned Key Pair using Upload private key file. Click Decrypt password after uploading to get the administrator credentials. 
		
	
	
		Now you have the decrypted login information and can log on to the Jump Host Instance using RDP. 
		
	



	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	



	 
 


	Deploying the Domain Controller Instance



	For this environment, we decided to follow our standard deployment type for Domain Controllers: 
	The AD deployment consists of a Hub-and-Spoke model. Each Resource Location running on a Hypervisor/Hyperscaler is connected to the primary Domain Controller using IPSec-based Site-to-Site VPNs. Each Resource Location can have its subdomain if needed.
 


	
		Note: 
	 

	
		You can follow the steps mentioned above to deploy the Jump Host Instance. 
		 
	 



	After the successful creation of the Domain Controller Instance, you can access it by:
 


	 
 


	
		Connect to the Jump Host Instance using RDP and log on using the Key Pair
	
	
		Connect to the Domain Controller Instance using RDP from the Jump Host Instance
	



	The configuration of the Domain Controller and Replication from the primary Domain Controller is out-of-scope of this guide.
 


	Assuming the Domain Controller Instance is completely configured, you can proceed to the next step – deploying and configuring the Cloud Connector Instances. 
	 
 


	Deploying and Configuring the Resource Location and its Cloud Connector Instances



	Connecting your resources to Citrix Cloud involves deploying Cloud Connectors in your environment and creating Resource Locations. 
	Resource Locations contain the resources required to deliver Cloud Services to your subscribers. Depending on which Citrix Cloud services you use and the services you want to provide your subscribers, they can contain different resources.
 


	To create a Resource Location, install at least two Cloud Connectors in your domain.
 


	
		Note: 
	 

	
		You can follow the steps mentioned above for deploying the Jump Host Instance, but we recommend choosing another Instance type, like a t3.large or similar. 
		 
	 



	After the successful creation of the Cloud Connector Instances, you can access them by:
 


	
		Connect to the Jump Host Instance using RDP and log on using the Key Pair
	
	
		Connect to the Cloud Connector Instances using RDP from the Jump Host Instance
	



	Before deploying the Cloud Connector software, you must add the Cloud Connector Instances to the Domain. This step is outside the scope of this guide.
 


	For more detailed step-by-step instructions, please look at the corresponding part in this guide or in this guide. 
 


	As all Cloud Connector Instances were successfully registered, you can proceed to the next step – deploying and configuring Amazon WorkSpaces Core. 
	 
 


	Deploying Amazon WorkSpaces Core



	This is a multi-step process: Create the Image, a Directory Connector, and the AWC Deployment. 
	 
 


	Deploying a WorkSpaces Account



	Use the following Terraform code to create the account:
 


	
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #1e1e1e;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #1e1e1e;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}	
	
		
			
				
					
						
							### Get AWC IAM-related Role
						 

						
							 data "aws_iam_role" "AWC-IAM-Main-Role" {
						 

						
							    name       = "${var.AWC-GetAWCMainRole-Name}"
						 

						
							}
						 

						
							 
						 

						
							### Create Quick Deploy AWS WorkSpaces Account with AWS Access Key and Secret Access Key
						 

						
							resource "citrix_quickcreate_aws_workspaces_account" "CreateAWCAccount" {
						 

						
							  depends_on = [ data.aws_iam_role.AWC-IAM-Main-Role]
						 

						
							    name                  = "${var.AWC-Account-Name}"
						 

						
							    aws_region            = "${var.AWC-Account-Region}"
						 

						
							    aws_role_arn          = data.aws_iam_role.AWC-IAM-Main-Role.arn
						 

						
							}
						 
					
				
			
		
	



	 
	Terraform will now create the account:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\_TERRAFORM\_AWSCore&gt; terraform plan
					 

					
						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:
					 

					
						 
						# data.aws_iam_role.AWC-IAM-Main-Role will be read during apply 
						data "aws_iam_role" "AWC-IAM-Main-Role" { 
						      + arn                  = (known after apply) 
						      + assume_role_policy   = (known after apply) 
						      + create_date          = (known after apply) 
						      + description          = (known after apply) 
						      + id                   = (known after apply) 
						      + max_session_duration = (known after apply) 
						      + name                 = "AWC-IAM-Main-Role" 
						      + path                 = (known after apply) 
						      + permissions_boundary = (known after apply) 
						      + role_last_used       = (known after apply) 
						      + tags                 = (known after apply) 
						      + unique_id            = (known after apply) 
						    }
					 

					
						 
					 

					
						# citrix_quickcreate_aws_workspaces_account.CreateAWCAccount will be created 
						  + resource "citrix_quickcreate_aws_workspaces_account" "CreateAWCAccount" { 
						      + aws_account              = (known after apply) 
						      + aws_byol_feature_enabled = true 
						      + aws_region               = "eu-central-1" 
						      + aws_role_arn             = (sensitive value) 
						      + id                       = (known after apply) 
						      + name                     = "TACG-AWC-TF-Account" 
						    } 
						 
						Plan: 1 to add, 0 to change, 0 to destroy. 
						 
						─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 
						 
						PS C:\_TERRAFORM\_AWSCore&gt; terraform apply 
						 
						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:
					 

					
						# data.aws_iam_role.AWC-IAM-Main-Role will be read during apply 
						data "aws_iam_role" "AWC-IAM-Main-Role" { 
						      + arn                  = (known after apply) 
						      + assume_role_policy   = (known after apply) 
						      + create_date          = (known after apply) 
						      + description          = (known after apply) 
						      + id                   = (known after apply) 
						      + max_session_duration = (known after apply) 
						      + name                 = "AWC-IAM-Main-Role" 
						      + path                 = (known after apply) 
						      + permissions_boundary = (known after apply) 
						      + role_last_used       = (known after apply) 
						      + tags                 = (known after apply) 
						      + unique_id            = (known after apply) 
						    }
					 

					
						 
					 

					
						# citrix_quickcreate_aws_workspaces_account.CreateAWCAccount will be created 
						  + resource "citrix_quickcreate_aws_workspaces_account" "CreateAWCAccount" { 
						      + aws_account              = (known after apply) 
						      + aws_byol_feature_enabled = true 
						      + aws_region               = "eu-central-1" 
						      + aws_role_arn             = (sensitive value) 
						      + id                       = (known after apply) 
						      + name                     = "TACG-AWC-TF-Account" 
						    } 
						 
						Plan: 1 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_quickcreate_aws_workspaces_account.CreateAWCAccount: Creating... 
						citrix_quickcreate_aws_workspaces_account.CreateAWCAccount: Creation complete after 4s [id=2514cb6f-61af-431b-8f9c-7691c9e4939e] 
						 
						Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
					 
					PS C:\_TERRAFORM\_AWSCore&gt; 
				
			
		
	



	Terraform successfully created the new WorkSpaces account.
 


	If your licensing agreement with Microsoft allows it, you can bring and deploy your Windows 10 or 11 VDI machines into your WorkSpaces Core environment. Before enabling your account for Bring your Own Licenses (BYOL), you must go through a verification process to confirm your eligibility for BYOL. You can find more information here. You then need to enable BYOL for your account by following the instructions in Enable BYOL for your eligible WorkSpaces account using the Amazon WorkSpaces console. 
	 
 


	Checking BYOL enablement using Terraform



	You can use Terraform to check if your account is enabled for BYOL:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	



	
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #1e1e1e;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #1e1e1e;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}	
	
		
			
				
					
						
							# Get details of a Citrix QuickCreate AWS WorkSpaces Account using the account name
						 

						
							data "citrix_quickcreate_aws_workspaces_account" "GetAWCAccount" {
						 

						
							  name = "TACG-AWC-TF-Account"
						 

						
							}
						 

						
							 
						 

						
							output "GetAWCAccount" {
						 

						
							  value = data.citrix_quickcreate_aws_workspaces_account.GetAWCAccount
						 

						
							}
						 

						
							 
						 
					
				
			
		
	



	 
	Terraform will now check the account:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\_TERRAFORM\_AWSCore&gt; terraform plan 
						data.citrix_quickcreate_aws_workspaces_account.GetAWCAccount: Reading... 
						data.citrix_quickcreate_aws_workspaces_account.GetAWCAccount: Read complete after 4s [id=2514cb6f-61af-431b-8f9c-7691c9e4939e] 
						 
						Changes to Outputs: 
						  + GetAWCAccount = { 
						      + aws_account              = "35XXXXXXXXXXXX" 
						      + aws_byol_feature_enabled = true 
						      + aws_region               = "eu-central-1" 
						      + id                       = "2514xxxx-xxxx-xxxx-xxxx-xxxxxxxx939e" 
						      + name                     = "TACG-AWC-TF-Account" 
						    } 
						 
						You can apply this plan to save these new output values to the Terraform state, without changing any real 
						infrastructure. 
						 
					 
				
			
		
	



	Terraform returns aws_byol_feature_enabled = true.
 


	
		Note: 
	 

	
		If Terraform returns that BYOL is not enabled, you can open a Support Case to enable BYOL. 
		You can find instructions later in the guide. 
		 
	 



	Checking BYOL enablement using PowerShell



	You also can use PowerShell to check I your account is enabled for BYOL:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	



	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	



	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\_TERRAFORM\_AWSCore&gt; Get-WKSAccount 
						 
						DedicatedTenancyAccountType DedicatedTenancyManagementCidrRange DedicatedTenancySupport 
						--------------------------- ----------------------------------- ----------------------- 
						SOURCE_ACCOUNT              100.64.0.0/16                       ENABLED
					 
				
			
		
	



	If PowerShell returns DedicatedTenancySupport as True, then BYOL is enabled for your account.
 


	Please also note the DedicatedTenancyManagementCidrRange value for later use (in this case it is 100.64.0.0/16).
 


	
		Note: 
	 

	
		If PowerShell returns that BYOL is not enabled, you can open a Support Case to enable BYOL. 
		You can find instructions in the next chapter. 
		 
	 



	 
 


	Checking BYOL enablement using the GUI and eventually open a Support Case to enable BYOL



	
		Log on to your AWS console and enter WorkSpaces in the Search bar at the top of the page. 
		
	
	
		Choose Account Settings from the WorkSpaces Console. 
		
	
	
		If you don't see the Enable BYOL option, your account is not eligible for BYOL. You must create a Technical Support Case to enable your account to use BYOL. 
		 
		
	
	
		After your account is enabled for BYOL, you can set it up by clicking on Set up BYOL. 
		Under Bring Your Own License (BYOL), in the Management network interface IP address range area, choose an IP address range, then choose Display available CIDR blocks. 
		Select the CIDR block you want to use and then choose Enable BYOL.
	
	
		After successful enrollment (enabling BYOL can last several hours), the WorkSpaces Console shows the successful BYOL enablement. 
		
	



	 
 


	Deploying and Configuring the Image 



	After you confirm that your VM meets the Microsoft BYOL requirements by following the instructions in Confirm that the Windows VM in Amazon WorkSpaces meets the requirements for Microsoft BYOL, you must export the VM from your virtualization environment. You will need this to create an image for BYOL that you can use in WorkSpaces.
 


	The VM you export must be on a single volume with a maximum size of 70 GB and at least 10 GB of free space. 
 


	
		Note: 
	 

	
		Describing the whole process of creating a BYOL Image is out-of-scope of this guide. 
		For more information, see the documentation for Exporting your VM from its virtualization environment, Bring Your Own Windows desktop licenses in WorkSpaces - Amazon WorkSpaces, and Bringing your Windows 11 image to AWS with VM Import/Export. 
		 
	 



	 
 


	
		Important: 
	 

	
		To stay compliant with Microsoft licensing terms, AWS runs your BYOL WorkSpaces on hardware dedicated to you in the AWS Cloud. 
		 
	 



	Your Image must run one of the following Windows versions:
 


	
		Windows 10 Version 22H2 (November 2022 Update)
	
	
		Windows 10 Enterprise LTSC 2019 (1809)
	
	
		Windows 10 Enterprise LTSC 2021 (21H2)
	
	
		Windows 11 Enterprise 23H2 (October 2023 release)
	
	
		Windows 11 Enterprise 22H2 (October 2022 release)
	



	
		Important: 
	 

	
		While AWC creates the VDI VMs, the VDAs are not configured to get the list of Cloud Connectors. 
		You must manually configure the VDAs by setting the adjacent Registry key or deploying a GPO containing the addresses of the Cloud Connectors. For more information, see VDA registration. 
		 
	 



	 
	In this example, we set the Registry key HKEY_LOCAL_MACHINE\Software\Citrix\VirtualDesktopAgent\ListOfDDCs (REG_SZ) and fill in the DNS name of the Cloud Connector: 
	
 


	Make sure to check the successful registration to the Cloud Connectors: 
	Restart the Citrix Desktop Service and open the Event Viewer. 
	Look for the adjacent events in the Application log: 
	
 


	Let’s assume that you have successfully created your BYOL-based Image. 
	To proceed further, you need the AMI ID of your Image.
 


	Get the needed Image-ID using Terraform



	
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #1e1e1e;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #1e1e1e;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}	
	
		
			
				
					
						
							# Get details of the BYOL-Image using the Image name
						 

						
							data "aws_ami" "GetAWCImage" {
						 

						
							  owners           = ["self"]
						 

						
							}
						 

						
							 
						 

						
							output "GetAWCImage" {
						 

						
							  value = data.aws_ami.GetAWCImage.image_id
						 

						
							}
						 
					
				
			
		
	



	 
	Terraform will now retrieve the AMI-ID:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\_TERRAFORM\_AWSCore&gt; terraform plan 
						data.aws_ami.GetAWCImage: Reading... 
						data.aws_ami.GetAWCImage: Read complete after 1s [id=ami-0709cbe857677f074] 
						 
						Changes to Outputs: 
						  + GetAWCImage = "ami-0709cbe857677f074" 
						 
						You can apply this plan to save these new output values to the Terraform state, without changing any real 
						infrastructure. 
						 
					 
				
			
		
	



	Terraform returns the AMI-ID. You need it for the creation of the WorkSpaces Image. 
	 
 


	Get the needed Image-ID using PowerShell



	You can use the Get-EC2Image snippet to retrieve your self-created images:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	



	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\_TERRAFORM\_AWSCore&gt; Get-EC2Image -Owner self | FT Name, Description, ImageID, PlatformDetails 
						 
						Name                         Description                          ImageId               PlatformDetails 
						----                         -----------                          -------               --------------- 
						import-ami-06d69e4e65f241d3d AWS-VMImport service: Windows 10 Pro ami-0709cbe857677f074 Windows BYOL 
						 
						 
						PS C:\_TERRAFORM\_AWSCore&gt; 
					 
				
			
		
	



	In this case, the AMI-ID is ami-0709cbe857677f074. You need it for the creation of the WorkSpaces Image.
 


	You can now deploy the WorkSpaces Image. 
	 
 


	
		Note: 
	 

	
		Creating the Workspaces Image is quite a lengthy process. In out example, it took almost 90 minutes. 
		 
	 



	The parameter aws_image_id is the just retrieved AMI-ID of your BYOL Image.
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	



	
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #1e1e1e;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #1e1e1e;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}	
	
		
			
				
					
						
							# Quick Deploy AWS WorkSpaces Image with AMI image
						 

						
							resource "citrix_quickcreate_aws_workspaces_image" "CreateAWCImage" {
						 

						
							  depends_on = [ citrix_quickcreate_aws_workspaces_account.CreateAWCAccount ]
						 

						
							  name              = "${var.AWC-Image-Name}"
						 

						
							  account_id        = citrix_quickcreate_aws_workspaces_account.CreateAWCAccount.id
						 

						
							  aws_image_id      = "${var.AWC-Image-ID}"
						 

						
							  description       = "${var.AWC-Image-Description}"
						 

						
							  session_support   = "${var.AWC-Image-SessionSupport}"
						 

						
							  operating_system  = "${var.AWC-Image-OSType}"
						 

						
							  ingestion_process = "${var.AWC-Image-IngestionType}"
						 
						}
					
				
			
		
	



	 
	Terraform will now create the WorkSpaces Image:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\_TERRAFORM\_AWSCore&gt; terraform plan
					 

					
						Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: 
						  + create
					 

					
						  # citrix_quickcreate_aws_workspaces_image.CreateAWCImage will be created 
						  + resource "citrix_quickcreate_aws_workspaces_image" "CreateAWCImage" { 
						      + account_id        = (known after apply) 
						      + aws_image_id      = "ami-0709cbe857677f074" 
						      + description       = "Imported AMI-Image for AWC" 
						      + id                = (known after apply) 
						      + ingestion_process = "BYOL_REGULAR_BYOP" 
						      + name              = "TACG-AWC-TF-Image" 
						      + operating_system  = "WINDOWS" 
						      + session_support   = "SingleSession" 
						      + state             = (known after apply) 
						      + tenancy           = (known after apply) 
						    }
					 

					
						Plan: 1 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:\_TERRAFORM\_AWSCore&gt; terraform apply
					 

					
						Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: 
						  + create
					 

					
						  # citrix_quickcreate_aws_workspaces_image.CreateAWCImage will be created 
						  + resource "citrix_quickcreate_aws_workspaces_image" "CreateAWCImage" { 
						      + account_id        = (known after apply) 
						      + aws_image_id      = "ami-0709cbe857677f074" 
						      + description       = "Imported AMI-Image for AWC" 
						      + id                = (known after apply) 
						      + ingestion_process = "BYOL_REGULAR_BYOP" 
						      + name              = "TACG-AWC-TF-Image" 
						      + operating_system  = "WINDOWS" 
						      + session_support   = "SingleSession" 
						      + state             = (known after apply) 
						      + tenancy           = (known after apply) 
						    }
					 

					
						Plan: 1 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_quickcreate_aws_workspaces_image.CreateAWCImage: Creating... 
						citrix_quickcreate_aws_workspaces_image.CreateAWCImage: Still creating... [10s elapsed] 
						citrix_quickcreate_aws_workspaces_image.CreateAWCImage: Still creating... [20s elapsed] 
						citrix_quickcreate_aws_workspaces_image.CreateAWCImage: Still creating... [30s elapsed] 
						citrix_quickcreate_aws_workspaces_image.CreateAWCImage: Still creating... [40s elapsed] 
						citrix_quickcreate_aws_workspaces_image.CreateAWCImage: Still creating... [50s elapsed] 
						citrix_quickcreate_aws_workspaces_image.CreateAWCImage: Still creating... [1m0s elapsed] 
						... 
						citrix_quickcreate_aws_workspaces_image.CreateAWCImage: Still creating... [1h24m44s elapsed] 
						citrix_quickcreate_aws_workspaces_image.CreateAWCImage: Still creating... [1h24m54s elapsed] 
						citrix_quickcreate_aws_workspaces_image.CreateAWCImage: Still creating... [1h25m4s elapsed] 
						citrix_quickcreate_aws_workspaces_image.CreateAWCImage: Still creating... [1h25m14s elapsed] 
						citrix_quickcreate_aws_workspaces_image.CreateAWCImage: Still creating... [1h25m24s elapsed] 
						citrix_quickcreate_aws_workspaces_image.CreateAWCImage: Creation complete after 1h25m29s elapsed [id=5e4c32b7-64b2-4b5f-8144-4382cec08fee]
					 

					
						Apply complete! Resources: 1 added, 0 changed, 0 destroyed. 
						 
					 
					PS C:\_TERRAFORM\_AWSCore&gt; 
				
			
		
	



	Terraform successfully created the WorkSpaces Image. 
	 
 


	Deploying and Configuring the Directory 



	WorkSpaces uses a directory to store and manage information for your WorkSpaces and users. The most used options are:
 


	
		AD Connector — Use your existing on-premises Microsoft Active Directory. 
		Users can sign into their WorkSpaces using their on-premises credentials and access on-premises resources from their WorkSpaces. 
		 
	
	
		AWS Managed Microsoft AD — Create a Microsoft Active Directory hosted on AWS.
	



	In this example, we use our own Domain Controller – an Instance running on Amazon EC2, which runs all relevant AD roles. 
	We deploy a Directory based on an AD Connector.
 


	We need to determine a few IDs before proceeding with the deployment:
 


	
		The VPC-ID
	
	
		The Subnet-IDs
	
	
		The DNS Servers containing the address(es) of the Domain Controller
	
	
		Domain credentials
	



	Use Terraform to retrieve the needed IDs:
 


	
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #1e1e1e;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #1e1e1e;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}	
	
		
			
				
					
						
							# Get needed entities for creating the AWS Directory
						 

						
							## Get VPC
						 

						
							 data "aws_vpc" "GetAWCVPC" {
						 

						
							}
						 

						
							 
						 

						
							output "GetAWCVPC" {
						 

						
							  value = data.aws_vpc.GetAWCVPC.id
						 

						
							}
						 

						
							 
						 

						
							## Get Subnets
						 

						
							data "aws_subnets" "GetAWCSubnets" {
						 

						
							  filter {
						 

						
							    name   = "vpc-id"
						 

						
							    values = [data.aws_vpc.GetAWCVPC.id]
						 

						
							  }
						 

						
							}
						 

						
							 
						 

						
							output "GetAWCSubnets" {
						 

						
							  value = data.aws_subnets.GetAWCSubnets.ids
						 

						
							}
						 

						
							 
						 

						
							## Get DNS
						 

						
							data "aws_vpc_dhcp_options" "GetAWCDNS" {
						 

						
							  filter {
						 

						
							    name   = "dhcp-options-id"
						 

						
							    values = [data.aws_vpc.GetAWCVPC.dhcp_options_id]
						 

						
							  }
						 

						
							}
						 

						
							 
						 

						
							output "GetAWCDNS" {
						 

						
							  value = data.aws_vpc_dhcp_options.GetAWCDNS.domain_name_servers
						 

						
							}
						 

						
							 
						 
					
				
			
		
	



	 
	Terraform retrieves the IDs – write them down as you need them in the next step:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					PS C:\_TERRAFORM\_AWSCore&gt; terraform plan 
					data.aws_vpc.GetAWCVPC: Reading... 
					data.aws_vpc.GetAWCVPC: Read complete after 0s [id=vpc-7f3c5a15] 
					data.aws_subnets.GetAWCSubnets: Reading... 
					data.aws_vpc_dhcp_options.GetAWCDNS: Reading... 
					data.aws_vpc_dhcp_options.GetAWCDNS: Read complete after 0s [id=dopt-01f3f9f880f1698ad] 
					data.aws_subnets.GetAWCSubnets: Read complete after 0s [id=eu-central-1] 
					 
					Changes to Outputs: 
					  + GetAWCDNS     = [ 
					      + "172.31.20.19", 
					    ] 
					  + GetAWCSubnets = [ 
					      + "subnet-70b1ff1a", 
					      + "subnet-650d8819", 
					      + "subnet-5935bd15", 
					    ] 
					  + GetAWCVPC     = "vpc-7f3c5a15" 
					 
					You can apply this plan to save these new output values to the Terraform state, without changing any real 
					infrastructure.
				
			
		
	



	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	 
	You can now use Terraform to create a Directory :
 


	
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #1e1e1e;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #1e1e1e;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}	
	
		
			
				
					
						
							## Create the Directory based on an AD Connector
						 

						
							resource "aws_directory_service_directory" "GetAWCDirectory" {
						 

						
							  name     = "${var.AWC-DirSvc-DomainName}"
						 

						
							  password = "${var.AWC-DirSvc-AdminPassword}"
						 

						
							  size     = "Small"
						 

						
							  type     = "ADConnector"
						 

						
							 
						 

						
							  connect_settings {
						 

						
							    customer_dns_ips  = [data.aws_vpc_dhcp_options.GetAWCDNS.domain_name_servers[0]]
						 

						
							    customer_username = "${var.AWC-DirSvc-AdminName}"
						 

						
							    subnet_ids        = [data.aws_subnets.GetAWCSubnets.ids[0], data.aws_subnets.GetAWCSubnets.ids[1], ]
						 

						
							    vpc_id            = data.aws_vpc.GetAWCVPC.id
						 

						
							  }
						 
						}
					
				
			
		
	



	 
	Terraform creates the Directory:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\_TERRAFORM\_AWSCore&gt; terraform plan 
						 
						data.aws_vpc.GetAWCVPC: Reading... 
						data.aws_vpc.GetAWCVPC: Read complete after 1s [id=vpc-7f3c5a15] 
						data.aws_subnets.GetAWCSubnets: Reading... 
						data.aws_vpc_dhcp_options.GetAWCDNS: Reading... 
						data.aws_subnets.GetAWCSubnets: Read complete after 0s [id=eu-central-1] 
						data.aws_vpc_dhcp_options.GetAWCDNS: Read complete after 0s [id=dopt-01f3f9f880f1698ad] 
						 
						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_directory_service_directory.GetAWCDirectory will be created 
						  + resource "aws_directory_service_directory" "GetAWCDirectory" { 
						      + access_url                           = (known after apply) 
						      + alias                                = (known after apply) 
						      + desired_number_of_domain_controllers = (known after apply) 
						      + dns_ip_addresses                     = (known after apply) 
						      + edition                              = (known after apply) 
						      + enable_sso                           = false 
						      + id                                   = (known after apply) 
						      + name                                 = "XXXXXXXXX" 
						      + password                             = (sensitive value) 
						      + security_group_id                    = (known after apply) 
						      + short_name                           = (known after apply) 
						      + size                                 = "Small" 
						      + tags_all                             = (known after apply) 
						      + type                                 = "ADConnector" 
						 
						      + connect_settings { 
						          + availability_zones = (known after apply) 
						          + connect_ips        = (known after apply) 
						          + customer_dns_ips   = [ 
						              + "172.31.20.19", 
						            ] 
						          + customer_username  = (sensitive value) 
						          + subnet_ids         = [ 
						              + "subnet-650d8819", 
						              + "subnet-70b1ff1a", 
						            ] 
						          + vpc_id             = "vpc-7f3c5a15" 
						        } 
						    } 
						 
						Plan: 1 to add, 0 to change, 0 to destroy. 
						 
						Changes to Outputs: 
						  + GetAWCDNS     = [ 
						      + "172.31.20.19", 
						    ] 
						  + GetAWCDNS1    = "172.31.20.19" 
						  + GetAWCSubnets = [ 
						      + "subnet-70b1ff1a", 
						      + "subnet-650d8819", 
						      + "subnet-5935bd15", 
						    ] 
						  + GetAWCVPC     = "vpc-7f3c5a15" 
						 
						────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 
						 
						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:\_TERRAFORM\_AWSCore&gt; terraform apply 
						data.aws_vpc.GetAWCVPC: Reading... 
						data.aws_vpc.GetAWCVPC: Read complete after 0s [id=vpc-7f3c5a15] 
						data.aws_vpc_dhcp_options.GetAWCDNS: Reading... 
						data.aws_subnets.GetAWCSubnets: Reading... 
						data.aws_subnets.GetAWCSubnets: Read complete after 1s [id=eu-central-1] 
						data.aws_vpc_dhcp_options.GetAWCDNS: Read complete after 1s [id=dopt-01f3f9f880f1698ad] 
						 
						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_directory_service_directory.GetAWCDirectory will be created 
						  + resource "aws_directory_service_directory" "GetAWCDirectory" { 
						      + access_url                           = (known after apply) 
						      + alias                                = (known after apply) 
						      + desired_number_of_domain_controllers = (known after apply) 
						      + dns_ip_addresses                     = (known after apply) 
						      + edition                              = (known after apply) 
						      + enable_sso                           = false 
						      + id                                   = (known after apply) 
						      + name                                 = "XXXXXXXXXX" 
						      + password                             = (sensitive value) 
						      + security_group_id                    = (known after apply) 
						      + short_name                           = (known after apply) 
						      + size                                 = "Small" 
						      + tags_all                             = (known after apply) 
						      + type                                 = "ADConnector" 
						 
						      + connect_settings { 
						          + availability_zones = (known after apply) 
						          + connect_ips        = (known after apply) 
						          + customer_dns_ips   = [ 
						              + "172.31.20.19", 
						            ] 
						          + customer_username  = (sensitive value) 
						          + subnet_ids         = [ 
						              + "subnet-650d8819", 
						              + "subnet-70b1ff1a", 
						            ] 
						          + vpc_id             = "vpc-7f3c5a15" 
						        } 
						    } 
						 
						Plan: 1 to add, 0 to change, 0 to destroy. 
						 
						Changes to Outputs: 
						  + GetAWCDNS     = [ 
						      + "172.31.20.19", 
						    ] 
						  + GetAWCDNS1    = "172.31.20.19" 
						  + GetAWCSubnets = [ 
						      + "subnet-70b1ff1a", 
						      + "subnet-650d8819", 
						      + "subnet-5935bd15", 
						    ] 
						  + GetAWCVPC     = "vpc-7f3c5a15" 
						 
						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_directory_service_directory.GetAWCDirectory: Creating... 
						aws_directory_service_directory.GetAWCDirectory: Still creating... [10s elapsed] 
						aws_directory_service_directory.GetAWCDirectory: Still creating... [20s elapsed]
					 

					
						...
					 

					
						aws_directory_service_directory.GetAWCDirectory: Creation complete after 3h18m49s [id=ade47627-990a-3421-dde2-38f8ed20c11f] 
						 
						Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
					 

					
						PS C:\_TERRAFORM\_AWSCore&gt; 
					 
				
			
		
	



	 
	You now can use Terraform to create a Directory Connection which connects to the just created AWS Directory Service:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	



	
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #1e1e1e;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #1e1e1e;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}	
	
		
			
				
					
						
							### Deploy AWC Directory Connection
						 

						
							resource "citrix_quickcreate_aws_workspaces_directory_connection" "CreateAWCDirectoryConnection" {
						 

						
							    name                                = "${var.AWC-DirectoryConnection-Name}"
						 

						
							    account                             = citrix_quickcreate_aws_workspaces_account.CreateAWCAccount.id 
							    resource_location                   = data.citrix_cloud_resource_location.GetResourceLocation.id 
							    directory                           = aws_directory_service_directory.GetAWCDirectory.id 
							    subnets                             = [data.aws_subnets.GetAWCSubnets.ids[0], data.aws_subnets.GetAWCSubnets.ids[1], ]
						 

						
							    tenancy                             = "DEDICATED"
						 

						
							    security_group                      = "${var.AWC-DirectoryConnection-SecurityGroup}"
						 

						
							    default_ou                          = "${var.AWC-DirectoryConnection-OU}"
						 

						
							    user_enabled_as_local_administrator = false
						 

						
							}
						 
					
				
			
		
	



	 
	Terraform creates the Directory Connection:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\_TERRAFORM\_AWSCore&gt; terraform plan 
						 
						data.aws_vpc.GetAWCVPC: Reading... 
						data.aws_vpc.GetAWCVPC: Read complete after 1s [id=vpc-7f3c5a15] 
						data.aws_subnets.GetAWCSubnets: Reading... 
						data.aws_vpc_dhcp_options.GetAWCDNS: Reading... 
						data.aws_subnets.GetAWCSubnets: Read complete after 0s [id=eu-central-1] 
						data.aws_vpc_dhcp_options.GetAWCDNS: Read complete after 0s [id=dopt-01f3f9f880f1698ad] 
						 
						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_quickcreate_aws_workspaces_directory_connection.CreateAWCDirectoryConnection will be created 
						  + resource "citrix_quickcreate_aws_workspaces_directory_connection" "CreateAWCDirectoryConnection" { 
						      + account                             = (sensitive value) 
						      + default_ou                          = (sensitive value) 
						      + directory                           = (sensitive value) 
						      + id                                  = (known after apply) 
						      + name                                = "TACG-AWC-TF-DirectoryConnection" 
						      + resource_location                   = (sensitive value) 
						      + security_group                      = "sg-b5806ec1" 
						      + subnets                             = [ 
						          + "subnet-650d8819", 
						          + "subnet-70b1ff1a", 
						        ] 
						      + tenancy                             = "DEDICATED" 
						      + user_enabled_as_local_administrator = false 
						    } 
						 
						Plan: 1 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:\_TERRAFORM\_AWSCore&gt; terraform apply 
						data.aws_vpc.GetAWCVPC: Reading... 
						data.aws_vpc.GetAWCVPC: Read complete after 0s [id=vpc-7f3c5a15] 
						data.aws_vpc_dhcp_options.GetAWCDNS: Reading... 
						data.aws_subnets.GetAWCSubnets: Reading... 
						data.aws_subnets.GetAWCSubnets: Read complete after 1s [id=eu-central-1] 
						data.aws_vpc_dhcp_options.GetAWCDNS: Read complete after 1s [id=dopt-01f3f9f880f1698ad] 
						 
						Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: 
						  + create 
						 
						# citrix_quickcreate_aws_workspaces_directory_connection.CreateAWCDirectoryConnection will be created 
						  + resource "citrix_quickcreate_aws_workspaces_directory_connection" "CreateAWCDirectoryConnection" { 
						      + account                             = (sensitive value) 
						      + default_ou                          = (sensitive value) 
						      + directory                           = (sensitive value) 
						      + id                                  = (known after apply) 
						      + name                                = "TACG-AWC-TF-DirectoryConnection" 
						      + resource_location                   = (sensitive value) 
						      + security_group                      = "sg-b5806ec1" 
						      + subnets                             = [ 
						          + "subnet-650d8819", 
						          + "subnet-70b1ff1a", 
						        ] 
						      + tenancy                             = "DEDICATED" 
						      + user_enabled_as_local_administrator = false 
						    } 
						 
						Plan: 1 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_quickcreate_aws_workspaces_directory_connection.CreateAWCDirectoryConnection: Creating... 
						citrix_quickcreate_aws_workspaces_directory_connection.CreateAWCDirectoryConnection: Still creating... [11s elapsed] 
						citrix_quickcreate_aws_workspaces_directory_connection.CreateAWCDirectoryConnection: Creation complete after 16s [0682bc9b-0493-4e59-ac2c-9cb23d66e243] 
						 
						Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
					 

					
						PS C:\_TERRAFORM\_AWSCore&gt; 
					 
				
			
		
	



	The creation of the Directory Connection is completed. 
	 
 


	Creating the Deployment using Terraform



	A Deployment is a group of VDI Desktops users can access from their Citrix Workspace. 
	This procedure specifies the characteristics of the Virtual Machines to be deployed as desktops and which AD users can use them. 
	 
 


	
		Note: 
	 

	
		Amazon Workspaces Core sets the computer names when the Workspace is created. They are used as an identifier to ensure accurate Workspace data. You cannot modify the computer name using Citrix DaaS for Amazon Workspaces Core. 
		 
	 



	You have created all the entities needed in this final step.
 


	Use Terraform to create the deployment. This could take some time to complete.
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	



	
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #1e1e1e;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #1e1e1e;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}	
	
		
			
				
					
						
							### Deploy AWC Deployment
						 

						
							resource "citrix_quickcreate_aws_workspaces_deployment" "CreateAWCDeployment" {
						 

						
							  depends_on = [ citrix_quickcreate_aws_workspaces_directory_connection.CreateAWCDirectoryConnection ]
						 

						
							    name                    = "${var.AWC-Deployment-Name}"
						 

						
							    account_id              = citrix_quickcreate_aws_workspaces_account.CreateAWCAccount.id 
							    directory_connection_id = citrix_quickcreate_aws_workspaces_directory_connection.CreateAWCDirectoryConnection.id 
							    image_id                = citrix_quickcreate_aws_workspaces_image.CreateAWCImage.id 
							    performance             = "${var.AWC-Deployment-Performance}" 
							    root_volume_size        = "${var.AWC-Deployment-RootSize}" 
							    user_volume_size        = "${var.AWC-Deployment-UserSize}" 
							    volumes_encrypted       = true 
							    volumes_encryption_key  = "${var.AWC-Deployment-EncryptionKeyPath}"
						 

						
							    running_mode = "${var.AWC-Deployment-RunningMode}"
						 

						
							    user_decoupled_workspaces = false
						 

						
							    workspaces = [
						 

						
							        {
						 

						
							            username = "${var.AWC-Deployment-UserName}"
						 

						
							            root_volume_size = 80
						 

						
							            user_volume_size = 50
						 

						
							            maintenance_mode = false
						 

						
							        },
						 

						
							    ]
						 

						
							} 
						 

						
							 
						 
					
				
			
		
	



	 
	Terraform creates the WorkSpaces Core Deployment:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\_TERRAFORM\_AWSCore&gt; terraform plan 
						 
						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_quickcreate_aws_workspaces_deployment.CreateAWCDeployment will be created 
						  + resource "citrix_quickcreate_aws_workspaces_deployment" "CreateAWCDeployment" { 
						      + account_id                = (sensitive value) 
						      + directory_connection_id   = (sensitive value) 
						      + id                        = (known after apply) 
						      + image_id                  = (sensitive value) 
						      + name                      = "TACG-AWC-TF-Deployment" 
						      + performance               = "STANDARD" 
						      + root_volume_size          = 80 
						      + running_mode              = "ALWAYS_ON" 
						      + user_decoupled_workspaces = false 
						      + user_volume_size          = 50 
						      + volumes_encrypted         = true 
						      + volumes_encryption_key    = (sensitive value) 
						      + workspaces                = [ 
						          + { 
						              + broker_machine_id = (known after apply) 
						              + machine_id        = (known after apply) 
						              + machine_name      = (known after apply) 
						              + maintenance_mode  = false 
						              + root_volume_size  = 80 
						              + user_volume_size  = 50 
						              + username          = "gerhard" 
						              + workspace_id      = (known after apply) 
						            }, 
						        ] 
						    } 
						 
						Plan: 1 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:\_TERRAFORM\_AWSCore&gt; terraform apply 
						 
						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_quickcreate_aws_workspaces_deployment.CreateAWCDeployment will be created 
						  + resource "citrix_quickcreate_aws_workspaces_deployment" "CreateAWCDeployment" { 
						      + account_id                = (sensitive value) 
						      + directory_connection_id   = (sensitive value) 
						      + id                        = (known after apply) 
						      + image_id                  = (sensitive value) 
						      + name                      = "TACG-AWC-TF-Deployment" 
						      + performance               = "STANDARD" 
						      + root_volume_size          = 80 
						      + running_mode              = "ALWAYS_ON" 
						      + user_decoupled_workspaces = false 
						      + user_volume_size          = 50 
						      + volumes_encrypted         = true 
						      + volumes_encryption_key    = (sensitive value) 
						      + workspaces                = [ 
						          + { 
						              + broker_machine_id = (known after apply) 
						              + machine_id        = (known after apply) 
						              + machine_name      = (known after apply) 
						              + maintenance_mode  = false 
						              + root_volume_size  = 80 
						              + user_volume_size  = 50 
						              + username          = "gerhard" 
						              + workspace_id      = (known after apply) 
						            }, 
						        ] 
						    } 
						 
						Plan: 1 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_quickcreate_aws_workspaces_deployment.CreateAWCDeployment: Creating... 
						citrix_quickcreate_aws_workspaces_deployment.CreateAWCDeployment: Still creating... [10s elapsed] 
						citrix_quickcreate_aws_workspaces_deployment.CreateAWCDeployment: Still creating... [20s elapsed] 
						citrix_quickcreate_aws_workspaces_deployment.CreateAWCDeployment: Still creating... [30s elapsed] 
						citrix_quickcreate_aws_workspaces_deployment.CreateAWCDeployment: Still creating... [40s elapsed] 
						... 
						citrix_quickcreate_aws_workspaces_deployment.CreateAWCDeployment: Still creating... [19m31s elapsed] 
						citrix_quickcreate_aws_workspaces_deployment.CreateAWCDeployment: Still creating... [19m41s elapsed] 
						citrix_quickcreate_aws_workspaces_deployment.CreateAWCDeployment: Still creating... [19m51s elapsed] 
						citrix_quickcreate_aws_workspaces_deployment.CreateAWCDeployment: Still creating... [20m1s elapsed] 
						citrix_quickcreate_aws_workspaces_deployment.CreateAWCDeployment: Creation complete after 20m10s [id=30786e23-67dd-4602-97db-3acb71df0d3f] 
						 
						Apply complete! Resources: 1 added, 0 changed, 0 destroyed. 
						PS C:\_TERRAFORM\_AWSCore&gt; 
					 
				
			
		
	



	The creation of the AWC Deployment is completed. 
	 
	You can use Terraform to check the completion:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	



	
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #1e1e1e;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #1e1e1e;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}	
	
		
			
				
					
						
							# Get details of a Citrix QuickCreate AWS WorkSpaces Deployment 
						 

						
							data "citrix_quickcreate_aws_workspaces_deployment" "GetAWCDeployment" {
						 

						
							id = citrix_quickcreate_aws_workspaces_deployment.CreateAWCDeployment.id
						 

						
							}
						 

						
							 
						 

						
							output "GetAWCDeployment" {
						 

						
							  value = data.citrix_quickcreate_aws_workspaces_deployment.GetAWCDeployment
						 

						
							}
						 

						
							 
						 
					
				
			
		
	



	 
	Terraform retrieves the details of the WorkSpaces Core Deployment:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\_TERRAFORM\_AWSCore&gt; terraform plan 
						data.citrix_quickcreate_aws_workspaces_deployment.GetAWCDeployment: Reading... 
						data.citrix_quickcreate_aws_workspaces_deployment.GetAWCDeployment: Read complete after 5s [id=30786e23-67dd-4602-97db-3acb71df0d3f] 
						 
						Changes to Outputs: 
						  + GetAWCDeployment = { 
						      + account_id              = "2514xxxx-xxxx-xxxx-xxxx-xxxxxxxx939e" 
						      + directory_connection_id = "2514xxxx-xxxx-xxxx-xxxx-xxxxxxxx8fee" 
						      + id                      = "2514xxxx-xxxx-xxxx-xxxx-xxxxxxxx0d3f" 
						      + image_id                = "2514xxxx-xxxx-xxxx-xxxx-xxxxxxxx" 
						      + name                    = "TACG-AWC-TF-Deployment" 
						    } 
						 
						You can apply this plan to save these new output values to the Terraform state, without changing any real infrastructure.
					 
				
			
		
	



	 
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	



	Test User Access to this Environment



	Log on to your environment using Citrix WorkSpace App using User credentials assigned to this environment.
 


	Check if all assigned Applications and Desktops are available: 
	
 


	The assigned AWC Desktop is available. The environment is ready to use.
 


	
 


	 
 


	Summary



	Terraform is straightforward for fully automatic deployment of Citrix DaaS on Amazon WorkSpaces Core. You can use Terraform to create all needed AWS and Citrix entities—you only need to follow the correct steps and look at the dependencies. 
	Bringing your own Windows licenses (BYOL) into WorkSpaces Core enables you to use Windows Desktop-based VDI deployments on Amazon´s EC2.
 


	We have shown examples of using Terraform to create all the needed entities and configurations to deploy a working Citrix DaaS environment running on Amazon WorkSpaces Core.
 


	 
 

.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#012456;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_11/prereq-awscli1.png.d909c442e34e9f25b2393ea14647fe0c.png" length="146797" type="image/png"/><pubDate>Tue, 12 Nov 2024 15:20:30 +0000</pubDate></item><item><title>Citrix VDA Upgrade Service PowerShell Commands</title><link>https://community.stage.citrix.com/tech-zone/learn/tech-briefs/citrix-vda-upgrade-service-posh/</link><description><![CDATA[Overview



	The Citrix VDA Upgrade Service is part of Citrix DaaS (Desktop as a Service). It supports the streamlined upgrade process of Virtual Delivery Agents (VDAs) within the Citrix environment. The service is designed for organizations using Citrix DaaS to manage and maintain virtual desktop environments, ensuring these environments stay up to date with the latest VDA versions. This update process is crucial because it improves performance, enhances security, and is compatible with new Citrix DaaS features. Administrators can use VUS to check for the latest updates, schedule upgrades, and monitor progress. They can initiate an upgrade immediately or set it for a specified date and time. This flexibility helps reduce downtime by allowing upgrades during off-peak hours. Upgrades can be scheduled through Citrix Web Studio or automated via PowerShell commands. The service lets admins view VDAs' current upgrade status within their catalog, identifying issues if upgrades don't proceed as expected. 
 


	The Citrix VDA Update Service PowerShell snap-in provides administrative functions for the VDA Update Service. In this article, we will focus on the available PowerShell commands.
 


	Prerequisites



	To use the Citrix VDA Upgrade Service PowerShell commands, you need the Citrix Remote PowerShell SDK version 7.43 or later. Go to Citrix Downloads to download the latest version.
 


	Available PowerShell Commands



	Below is the full list of PowerShell Cmdlets available with Citrix Remote PowerShell SDK for the Citrix VDA Upgrade Service. In total, there are 31 Cmdlets, 18 of which are for internal purposes only (noted with (1) above). In the ensuing section, the purpose of each Cmdlet is described in detail.
 


	
		Confirm-VusSelection 
	
	
		Get-VusAvailableVdaVersion 
	
	
		Get-VusCatalog 
	
	
		Get-VusComponents 
	
	
		Get-VusComponentVersion 
	
	
		Get-VusDBConnection (1)
	
	
		Get-VusDBSchema (1)
	
	
		Get-VusDBVersionChangeScript (1)
	
	
		Get-VusEntityUnit (1)
	
	
		Get-VusInstalledDBVersion (1)
	
	
		Get-VusMachine 
	
	
		Get-VusMachineSchedule (1)
	
	
		Get-VusRevision (1)
	
	
		Get-VusSchedule (1)
	
	
		Get-VusService (1)
	
	
		Get-VusServiceAddedCapability (1)
	
	
		Get-VusServiceInstance (1)
	
	
		Get-VusServiceStatus (1)
	
	
		New-VusCatalogSchedule
	
	
		New-VusMachineUpgrade
	
	
		Remove-VusCatalogUpgrade 
	
	
		Remove-VusMachineUpgrade 
	
	
		Remove-VusServiceMetadata (1)
	
	
		Reset-VusEnabledFeatureList (1)
	
	
		Reset-VusServiceGroupMembership (1)
	
	
		Set-VusCatalogUpgrade
	
	
		Set-VusCatalogUpgradeType 
	
	
		Set-VusDBConnection (1)
	
	
		Set-VusMachineUpgrade 
	
	
		Set-VusServiceMetadata (1)
	
	
		Test-VusDBConnection (1)
	



	Confirm-VusSelection  



	Validate the component selection choices at the catalog level that can be Included/Excluded during an upgrade using VDA Upgrade Service.
 


	The Confirm-VusSelection cmdlet is used to verify and validate the component Selections (New Components to be included in the Upgrade), Exclusions (Installed Components that should not be upgraded), and features to be used in a Catalog Upgrade using the VDA Upgrade Service.
 


	Confirm-VusSelection returns true if the Selection is Valid. If it is invalid, it returns false information as to why the selection is invalid.
 


	Requires: -CatalogName, -CatalogUID, -CatalogUUID, -SelectedComponents, -ExcludedComponents and -Features parameters.
 


	You will have to run the following command to retrieve the FeatureId:
 


	$(Get-VusComponents -CatalogName catalog-name).AvailableFeatures | Format-List -Property *
 


	
 


	The following commands must be run to configure the Components.
 


	$EmptyParamList = @()
 


	$ComponentA = [PSCustomObject]@{ 
 


	    ComponentId = [System.guid]::new('396394b3-53f0-48e0-8d8c-61dad39e3f17') 
 


	    Parameters = $EmptyParamList 
 


	}
 


	
 


	After, you can run the command by retrieving details for components and features.
 


	Confirm-VusSelection -CatalogName “Windows 10 Static MC” -Features “44ff6e61-e3cc-45d0-be9b-f52b5ab72a08” -SelectedComponents $ComponentA
 


	
 


	Get-VusAvailableVdaVersion 



	The Get-VusAvailableVdaVersion cmdlet in Citrix’s VDA Update Service allows administrators to check the latest available VDA versions compatible with their current Citrix setup, specifically for VDA updates via Citrix Virtual Apps and Desktops service. 
 


	When this cmdlet is executed, it returns details about the compatible VDA versions to whichh the current catalog or machine can be upgraded based on the catalog’s lowest VDA version. 
 


	This cmdlet is useful for determining the upgrade paths for the current (CR) and long-term service releases (LTSR).
 


	Using Get-VusAvailableVdaVersion helps ensure that administrators can select the best version for their needs, be it a more recent CR version for frequent updates or an LTSR version for stability. 
 


	This cmdlet is often combined with scheduling cmdlets such as New-VusCatalogSchedule and New-VusMachineUpgrade for the streamlined deployment of upgrades.
 


	Example of a command:
 


	Get-VusAvailableVdaVersion
 


	
 


	Get-VusCatalog 



	The Get-VusCatalog cmdlet in Citrix’s VDA Upgrade Service is a PowerShell command used to retrieve and display information about a specific machine catalog in Citrix DaaS environments. 
 


	This cmdlet helps check the upgrade state and configuration of Virtual Delivery Agents (VDAs) in a catalog. Administrators can view details like the catalog's current VDA version, upgrade eligibility, and any scheduled upgrades. 
 


	Additionally, it provides insights into whether the catalog meets the requirements for specific upgrade paths, making it a valuable tool for managing and automating VDA upgrades across large deployments.
 


	The Get-VusCatalog cmdlet also allows administrators to set the upgrade type at the catalog level (e.g., selecting LTSR or Current Release tracks), ensuring consistency and stability for VDAs in the catalog. 
 


	This flexibility enhances operational efficiency by enabling automated and scheduled upgrades, minimizing disruption for end users.
 


	It’s often used alongside other cmdlets, like New-VusCatalogSchedule for scheduling upgrades or Set-VusCatalogUpgradeType to define upgrade preferences, supporting streamlined and effective VDA version management and catalog compatibility requirements. 
 


	Example of a command:
 


	Get-VusCatalog
 


	
 


	Get-VusComponents 



	The Get-VusComponents cmdlet in Citrix DaaS SDK retrieves information on all available upgradeable components for Virtual Delivery Agents (VDAs). 
 


	This command is essential in identifying the specific VDA components that can be targeted in a catalog upgrade. 
 


	When used, Get-VusComponents returns details such as component IDs and parameters required to specify which VDA components to include or exclude in an upgrade. 
 


	These identifiers are often used with other cmdlets, such as Confirm-VusSelection and New-VusCatalogSchedule, to customize the upgrade configuration for different VDAs in the environment. 
 


	For example, administrators can extract component details using Get-VusComponents and then pass those details to New-VusCatalogSchedule to schedule upgrades that include specific components or exclude others based on catalog requirements.
 


	This modular setup streamlines the upgrade process, enabling granular control over component selection and version management. 
 


	This cmdlet is crucial for refining upgrade tasks as part of the Citrix Daas SDK, especially in large-scale environments where certain components may require different update timelines or configurations.
 


	Requires: -CatalogName, -CatalogUID or -CatalogUUID parameter.
 


	Example of a command: 
 


	Get-VusComponents -CatalogName “Windows 10 Static MC”
 


	
 


	Get-VusComponentVersion 



	The Get-VusComponentVersion cmdlet is part of Citrix's VDA Upgrade Service management and retrieves the specific version information for components on Virtual Delivery Agents (VDAs) within a machine catalog. 
 


	This cmdlet verifies that a VDA has successfully reported its component versions to the VDA Update Service.
 


	You typically pass the MachineId (or UUID) to this cmdlet to filter results for individual VDAs. 
 


	This can be critical in troubleshooting scenarios, especially when confirming that the VDA Upgrade Agent has accurately recorded and reported its version information. 
 


	If no version information is reported, it might indicate issues with the agent's connectivity or configuration, which may require restarting the VDA Upgrade Agent or checking its log files for errors. 
 


	For example, you can use Get-VusComponentVersion -MachineId &lt;UUID&gt; to retrieve the current version data for a specific VDA, which helps in planning or confirming upgrades across environments
 


	Example of a command: 
 


	Get-VusComponentVersion
 


	
 


	Get-VusMachine



	The Get-VusMachine cmdlet in Citrix Virtual Apps and Desktops (VAD) is a PowerShell command designed to retrieve details about individual virtual desktop machines within a machine catalog. 
 


	This cmdlet is primarily used to track machines' upgrade readiness and version alignment within the catalog.
 


	Specifically, Get-VusMachine outputs data such as:
 


	
		MachineName: The name of the virtual desktop machine.
	
	
		Uid: The unique identifier for the machine.
	
	
		UpgradeState: The current state of the machine regarding updates (e.g., UpToDate, Available, Scheduled or Unknown).
	
	
		UpgradeType: The update track, such as Current Release (CR) or Long Term Service Release (LTSR), applied to that machine.
	



	This cmdlet is often combined with Set-VusMachineUpgrade to schedule upgrades based on machine-specific requirements. It enables administrators to automate, track, and manage upgrades across multiple machines in the Citrix environment.
 


	Example of a command: 
 


	Get-VusMachine
 


	
 


	New-VusCatalogSchedule 



	The New-VusCatalogSchedule cmdlet in Citrix's VDA Upgrade Service (VUS) sets up scheduled upgrades for Virtual Delivery Agent (VDA) versions within a specific catalog. 
 


	This scheduling process helps automate upgrades to maintain consistent software versions across a deployment.
 


	To use this cmdlet, specify a scheduled time (ScheduledTimeInUtc) or set the upgrade to begin immediately (UpgradeNow). 
 


	Additionally, you can define DurationInHours, which sets the length of the upgrade process, allowing VDAs to be placed into maintenance mode as needed.
 


	The upgrade schedule is configured for each catalog to help ensure compatibility with the chosen VDA version.
 


	Other parameters include options for defining the VDA version from a local network share if you prefer this over direct cloud-based upgrades. 
 


	This allows more flexibility in managing the installation location, especially if certain network policies restrict cloud access for updates.
 


	Requires at least parameters -CatalogName(UID,UUID) -UpgradeNow or -ScheduledTimeInUtc and DurationInHours.s
 


	Example of a command:                                                                                                          
 


	New-VusCatalogSchedule -CatalogName "Windows 10 Static MC" -ScheduledTimeInUtc ([System.DateTime]::ParseExact("11/01/2024 01:00 PM", 'MM/dd/yyyy hh:mm tt', $null)) -DurationInHours 4
 


	
 


	New-VusMachineUpgrade 



	The New-VusMachineUpgrade cmdlet in Citrix configures Virtual Delivery Agents (VDAs) upgrades at the machine level. 
 


	This allows administrators to schedule or immediately initiate the upgrade process for a specific VDA, ensuring that the machines run the latest software.
 


	Key Features:
 


	
		Machine Level Configuration: This cmdlet targets individual machines rather than the entire catalog.
	
	
		Scheduling: The ScheduledTimeInUtc parameter allows you to set upgrades to occur immediately or at a specified time.
	
	
		Logging: When the upgrade process is executed, it can log actions to help troubleshoot any issues that arise during the upgrade.
	
	
		Compatibility: The cmdlet also supports upgrades to compatible versions of the VDA based on the existing version running on the machine.
	



	Requires at least parameters -MachineName(UID,UUID) -UpgradeNow or -ScheduledTimeInUtc, DurationInHours and UpgradeVersion.
 


	Example of a command: 
 


	New-VusMachineUpgrade -MachineName LAB\LAB-CTX-W10S-06 -UpgradeNow -DurationInHours 2 -UpgradeVersion 2402.0.1100.1256
 


	
 


	Remove-VusCatalogUpgrade 



	The Remove-VusCatalogUpgrade cmdlet in Citrix is used to cancel or remove an ongoing upgrade process for a machine catalog. 
 


	This is particularly useful when you want to halt an upgrade that is currently in progress or scheduled due to a change in strategy or issues arising during the upgrade process.
 


	Invoking this cmdlet typically requires parameters that specify which catalog's upgrade you wish to remove. 
 


	This can include the catalog's unique identifier (UUID) or other identifiers associated with the upgrade process. 
 


	By executing this command, you ensure that the specified upgrade does not proceed, allowing for better control over your environment, especially in larger setups where managing multiple upgrades can become complex.
 


	Requires at least parameters -CatalogName(UID,UUID) 
 


	Example of a command: 
 


	Remove-VusCatalogUpgrade -CatalogName “Windows 10 Static MC”
 


	
 


	 
 


	Remove-VusMachineUpgrade 



	The Remove-VusMachineUpgrade cmdlet in Citrix cancels or removes a scheduled upgrade for a specific Virtual Delivery Agent (VDA) machine. 
 


	This command can be beneficial if you need to halt an upgrade process previously scheduled due to changes in your deployment plans or if you've identified issues requiring the upgrade to be postponed.
 


	Key Features of Remove-VusMachineUpgrade:
 


	
		Targeted Removal: This option allows you to specify the machine by its UUID (Universally Unique Identifier) to target which upgrade should be canceled precisely.
	
	
		Usage Context: This cmdlet is typically used in environments where upgrades to VDAs are managed through PowerShell, particularly in automated or scripted scenarios.
	



	It requires at least the following parameters: MachineName(UID,UUID).
 


	Example of a command: 
 


	Remove-VusMachineUpgrade -MachineName LAB\LAB-CTX-W10S-06
 


	
 


	 
 


	Set-VusCatalogUpgrade 



	The Set-VusCatalogUpgrade cmdlet in Citrix's Virtual Apps and Desktops (formerly Citrix DaaS) environment modifies an already scheduled catalog upgrade's date, time, and duration. 
 


	This cmdlet allows administrators to specify the upgrade type (such as Current Release (CR) or Long Term Service Release (LTSR)) for the catalog. The change is made at the catalog level and influences how the Virtual Delivery Agents (VDAs) within that catalog are upgraded.
 


	Here's a brief overview of its functionality:
 


	
		Upgrade Type: You can set the upgrade type to dictate whether VDAs in the catalog receive upgrades as part of the CR or LTSR tracks. This helps manage the updated strategy based on organizational needs.
	
	
		Catalog Level: The command operates at the machine catalog level, affecting all machines within the specified catalog rather than individual machines.
	
	
		Pre-requisites: Before using this cmdlet, you must ensure that the catalog is set up correctly and that you have the necessary permissions to make changes.
	



	Using Set-VusCatalogUpgrade is part of the broader strategy for managing VDA upgrades effectively. This strategy ensures that the virtual environment remains up to date while adhering to organizational policies regarding software updates.
 


	
		Note: 
	 

	
		The initial schedule needs to have been configured with the parameters ScheduledTimeInUtc.
	 



	
 


	Example of a command: 
 


	Set-VusCatalogUpgrade -CatalogName “Windows 10 Static MC” -UpgradeNow -DuractionInHours 8
 


	
 


	Set-VusCatalogUpgradeType



	The Set-VusCatalogUpgradeType cmdlet in Citrix's VDA Upgrade Service specifies the upgrade type for a machine catalog. 
 


	The upgrade types can typically be set to Continuous Release (CR) or Long-Term Service Release (LTSR). This cmdlet can only be applied at the machine catalog level.
 


	Key Features:
 


	
		Upgrade Type Selection: You can select between CR and LTSR, which determines how upgrades will be handled for the virtual desktops in that catalog.
	
	
		Scheduled Upgrades: Once the upgrade type is set, it can help schedule upgrades according to the chosen release track, whichh is crucial for managing the lifecycle of virtual desktop environments.
	



	
		Important:
	 

	
		Changing the upgrade type might affect the available upgrade paths for the catalog's VDA (Virtual Delivery Agent) versions.
	 

	
		It is essential to understand the implications of selecting CR or LTSR based on your organization's upgrade strategy and support requirements.
	 



	It requires at least the following parameters: CatalogName(UID,UUID) and UpgradeType.
 


	Example of a command: 
 


	Set-VusCatalogUpgradeType -CatalogName “Windows 10 Static MC” -UpgradeType CR
 


	
 


	Set-VusMachineUpgrade 



	The Set-VusMachineUpgrade cmdlet in Citrix manages the upgrade process for Virtual Delivery Agents (VDAs) at the machine level. 
 


	This cmdlet lets administrators specify upgrade options for individual machines within a catalog.
 


	Key Features of Set-VusMachineUpgrade:
 


	
		
			
				Parameters:

				
					
						MachineUuid: Specifies the UUID of the machine you want to upgrade.
					
					
						ScheduledTimeInUtc: Sets a specific time in UTC for the upgrade.
					
					
						UpgradeNow: A switch parameter that, when used, triggers the upgrade immediately rather than at a scheduled time.
					
					
						DurationInHours: This option allows the administrator to specify how long the upgrade should take, which can help manage resources and downtime effectively.
					
					
						LoggingId*: This optional parameter tracks and logs the operation for future reference.
					
				
			
			
				Behavior:
				
					
						If the upgrade is already in progress, the new settings will affect only the machines that have yet to begin the upgrade.
					
					
						This cmdlet is helpful for quickly scheduling or rescheduling upgrades to ensure that VDAs are updated to the desired version while managing potential disruptions.
					
				
			
		
	



	Requires at least parameters -MachineName(UID,UUID), UpgradeNow and DurationInHours.
 


	Example of a command: 
 


	Set-VusMachineUpgrade -MachineName “LAB\LAB-CTX-W10S-06” -UpgradeNow -DurationInHours 4]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_11/image.png.92a337b2dafcbb763fade44fdc9eb31f.png" length="322248" type="image/png"/><pubDate>Wed, 06 Nov 2024 19:01:00 +0000</pubDate></item><item><title>PoC Guide: Deploying Citrix DaaS on Amazon WorkSpaces Core</title><link>https://community.stage.citrix.com/tech-zone/learn/poc-guides/daas-on-awc/</link><description><![CDATA[PoC Guide: Deploying Citrix DaaS on Amazon WorkSpaces Core 
	 



	Overview



	AWS WorkSpaces Core is a managed virtual desktop infrastructure designed to work with third-party VDI solutions such as Citrix DaaS. It is the compute layer of AWS workloads that the Citrix DaaS control plane can help orchestrate and manage to deliver HDX-optimized apps anywhere. AWS WorkSpaces Core and Citrix improve cost savings, simplify cloud management, and provide a superior user experience.
 


	Citrix DaaS treats AWS WorkSpaces Core as another Resource Location option to leverage within your deployment. It helps your IT teams manage and provision WorkSpace Core desktops directly from the Citrix platform, reducing application delivery complexity and simplifying operations. With security features like Zero Trust Network Access (ZTNA), contextual access policies, and secure browser redirection, Citrix protects your business from cyber threats and data leaks.
 


	Citrix improves the WorkSpaces Core experience with advanced HDX graphics, Unified Communication optimizations, USB redirection, and support for 3D workloads, ensuring smooth performance on any device or network. With centralized management, automated patching, and streamlined updates, Citrix optimizes your budget by reducing IT costs and complexity.
 


	This Proof of Concept guide will help you start deploying Citrix DaaS on Amazon WorkSpaces Core. 
	 
 


	The following is covered in this guide:
 


	
		Deploying all needed entities for Citrix DaaS:

		
			
				Deploying a Resource Location and its Cloud Connectors
			
		
	
	
		Deploying all needed entities for Amazon WorkSpaces Core:
		
			
				Deploying a VPC
			
			
				Deploying the Subnets and Gateways
			
			
				Setting the IAM permissions
			
			
				Deploying all needed instances like the Jumphost and a Domain Controller
			
		
	
	
		Creating the Deployment of Citrix DaaS on Amazon WorkSpaces Core 
		 
	



	
		 Note: 
	 

	
		To automatically deploy Citrix DaaS and Amazon WorkSpaces Core using Terraform, please review our PoC Guide: Deploying Citrix DaaS and Amazon Workspaces Core using Terraform.
	 



	 
 


	Deploying Citrix DaaS on Amazon WorkSpaces Core



	Before deploying Citrix DaaS, we must create and configure all necessary prerequisites. 
	We assume you already have a Citrix Cloud tenant with an active trial or a paid subscription to the Citrix DaaS service and an active AWS account. 
	 
 


	Deploy an Amazon VPC



	An Amazon Virtual Private Cloud (VPC) enables you to launch AWS resources into a virtual network that you have defined.
 


	
		Important: 
	 

	
		It is possible to have multiple VPCs in your AWS tenant. 
		Create a dedicated VPC for your Citrix DaaS deployment. 
		Tag all resources for easier management. 
		The default VPC holds these IP address ranges: 172.31.0.0/16 
		 
	 



	
		Log on to your AWS console and open the VPC service dashboard (enter VPC in the Search bar at the top of the page):
	
	
		No VPCs are defined, so we need to create one. 
		
	
	
		Choose the AWS region where you want to create your VPC (example screenshot): 
		
	
	
		Fill out the VPC settings according to your needs (example screenshot): 
		 
		 
		
	
	
		After completing all settings, click Create VPC to start creation. It will take some moments before the VPC is ready. 
		
	



	
		Note: 
	 

	
		For more information about VPCs, please visit Amazon´s Product Documentation pages. 
		 
	 



	 
 


	Deploy Subnets, Internet Gateways, NAT Gateways, Route Tables and Security Groups



	After creating the VPC, we deploy all needed network components, such as Subnets, Internet Gateways, NAT Gateways, and Routing Tables. 
	 
 


	Deploying the Subnets



	A Subnet is a range of IP addresses in your VPC. 
	You can create AWS resources in specific subnets, such as EC2 instances.
 


	The Subnet type is determined by how you configure routing for your Subnets:
 


	
		Public Subnet: The Subnet has a direct route to an Internet Gateway. Resources in a Public Subnet can directly access the Internet.
	
	
		Private Subnet: The Subnet does not have a direct route to an Internet Gateway. Resources in a Private Subnet require a NAT device to access the Internet. 
		 
	



	
		Important: 
	 

	
		Each Subnet must be associated with a Route Table, specifying all allowed outbound traffic routes. Every Subnet is automatically associated with the main Route Table for the VPC. 
		Disable the Auto-assign IP settings, as otherwise, a public IPv4 address is automatically requested for each new Network Interface in this Subnet. 
		 
	 



	 
 


	
		Note: 
	 

	
		Security Groups enable you to increase security for the resources in your VPC as they act like virtual firewalls. 
		 
	 



	
		Log on to your AWS console, open the VPC service dashboard (enter VPC in the Search bar on top of the page), and choose the Subnets option:
	
	
		In this guide, we create four different Subnets:
		
			
				One Public Subnet containing the Jump Host VM
			
			
				One Private Subnet for the Cloud Connector VMs and other Infrastructure-related VMs
			
			
				Two Private Subnets for all Worker VMs/VDAs 
				Example screenshots: 
				 
				 
				 
				 
				
			
		
	
	
		Remember to disable the Auto-assign setting. 
		 
		 
	



	
		Note: 
	 

	
		For more information about Subnets, please visit Amazon´s Product Documentation pages. 
		 
	 



	After creating the Subnets, you must create an Internet Gateway to enable the Public Subnet and the Jump Host-VM to connect to the Internet. 
	 
 


	Deploying an Internet Gateway



	An Internet Gateway allows communication between your VPC and the Internet. It supports IPv4 and IPv6 traffic and provides a target in your VPC Route Tables. 
	Resources in your Public Subnets can connect to the Internet if they have a public IPv4 address or an IPv6 address. The communication flow is two-way—the resources can also be contacted from the Internet.
 


	For communication using IPv4, the Internet Gateway also performs Network Address Translation (NAT). 
	 
 


	
		Important: 
	 

	
		Make sure to secure the communication flow of your resources by using Network ACLs or Security Groups. 
		 
	 



	
		Log on to your AWS console, open the VPC service dashboard (enter VPC in the Search bar on top of the page), and choose the Internet Gateways option (example screenshot): 
		
	
	
		After creating the Internet Gateway, you must attach it to the VPC by clicking the Attach to a VPC button. Choose the correct VPC. 
		
	



	To connect the Public Subnet to the Internet, you need to adjust the adjacent Route Table. A Route Table contains a set of routes directing network traffic from your Subnet or Gateway.
 


	
		 Log on to your AWS console, open the VPC service dashboard (enter VPC in the Search bar at the top of the page), and choose the Route Tables option.
	
	
		Choose Create Route Table and set the values according to your needs. 
		
	
	
		Create the Route Table by clicking the adjacent button.
	
	
		After creation, choose the just created Route Table and edit the routes: 
		
	
	
		Enter 0.0.0.0/0 into the Destination field and choose the Internet Gateway you created as the Target. Click Add route to save the route to the Route Table
	



	The Public Subnet now has a connection to the Internet. 
	 
 


	Deploying a NAT Gateway



	After creating the Internet Gateway, you need to create a NAT Gateway in the Private Subnet(s) to enable the Private Subnet(s) to connect to the Internet.
 


	A NAT Gateway is a Network Address Translation (NAT) device. 
	 
 


	
		Important: 
	 

	
		You can use a NAT Gateway to enable instances in a Private Subnet to connect to services outside your VPC. External services cannot initiate a connection with those instances. 
		A NAT Gateway is for use with IPv4 traffic only. 
		 
	 



	When you create a NAT Gateway, you specify one of the following connectivity types:
 


	
		Public – (Default) Instances in Private Subnets can connect to the Internet.
	
	
		Private – Instances in Private Subnets can connect to other VPCs or your on-premises network but not to the Internet.
	



	
		Log on to your AWS console, open the VPC service dashboard (enter VPC in the Search bar at the top of the page), and choose the NAT gateways option. 
		
	
	
		Choose the settings according to your needs and click on Create NAT gateway to create it. 
	
	
		After creation, you need to add a Route Table and change the Route Table for the NAT Gateway as you did before for the Internet Gateway. 
		
	
	
		Add the correct settings and click on Add route.
	
	
		After changing the Route Table, you must edit the Subnet settings by choosing Subnet associations. Select the Internal Subnet and click on Save associations. 
		
	



	All instances in the Internal Subnet should be able to connect to the Internet.
 


	
		Note: 
	 

	
		For more information about Gateways, please visit Amazon´s Product Documentation pages. 
		 
	 



	Set the IAM Permissions



	After creating all needed Network components, we must set all required IAM permissions.
 


	Identity and Access Management (IAM) allows control of access to AWS resources. 
	IAM controls who can be authenticated (signed in) and authorized (have permissions) to use Amazon VPC resources. 
	 
 


	Deploying the initial IAM Policy



	You can control access by attaching policies and attaching them to identities or resources.  
	A Policy defines the permissions associated with it. Policies are evaluated when a user or a root user wants to access a resource.  
	Permissions in the Policies determine whether the request is allowed or denied.  
	Most Policies are stored in AWS as JSON documents.
 


	More information about IAM JSON-based policies can be found in the IAM JSON policy reference. 
	 
 


	Deploying Security Groups on the Network Entities 



	A Security Group acts like a virtual firewall. 
	If you associate a Security Group with an EC2 instance, it controls the instance's inbound and outbound traffic.
 


	When you create a VPC, it comes with a default Security Group. 
	You can create additional Security Groups for a VPC, each with their own inbound and outbound rules:
 


	
		You can specify each inbound rule's source, port range, and protocol. 
	
	
		You can specify each outbound rule's destination, port range, and protocol.
	



	The following diagram shows a VPC with a Subnet, an Internet Gateway, and a Security Group. 
	The Subnet contains an EC2 instance. The Security Group is assigned to the instance and acts as a virtual firewall. The only traffic that reaches the instance is the traffic allowed by the Security Group rules. 
	
 


	
		Log on to your AWS console, enter VPC in the Search bar at the top of the page, and choose the Security option. Click on Security Groups on the Dashboard. 
		
	
	
		Create a new Security Group by clicking on Create security group. 
		
	
	
		Fill out all relevant fields and add the Inbound Rules by clicking Add rule on the Inbound Rules part. 
		 
		For the Security Group bound to the Instances in the Public Subnet, use the following rules:
	



	Type: HTTP 
	Protocol: TCP 
	Port range: 80 
	Source: Custom 0.0.0.0/0
 


	Type: HTTPS 
	Protocol: TCP 
	Port range: 443 
	Source: Custom 0.0.0.0/0
 


	Type: RDP 
	Protocol: TCP 
	Port range: 3389 
	Source: MyIP – your current external IP should be entered automatically
 


	Create more Inbound Rules according to your needs. 
	 
 


	For the Security Group bound to the Instances in the Private Subnet, use the following rules:
 


	Type: HTTP 
	Protocol: TCP 
	Port range: 80 
	Source: Custom 0.0.0.0/0
 


	Type: HTTPS 
	Protocol: TCP 
	Port range: 443 
	Source: Custom 0.0.0.0/0
 


	Type: RDP 
	Protocol: TCP 
	Port range: 3389 
	Source: Custom 0.0.0.0/0
 


	Type: Custom TCP 
	Protocol: TCP 
	Port range: 1494 
	Source: Custom 0.0.0.0/0
 


	Type: Custom UDP 
	Protocol: UDP 
	Port range: 1494 
	Source: Custom 0.0.0.0/0
 


	Type: Custom TCP 
	Protocol: TCP 
	Port range: 2598 
	Source: Custom 0.0.0.0/0
 


	Type: Custom UDP 
	Protocol: UDP 
	Port range: 2598 
	Source: Custom 0.0.0.0/0
 


	Create more Inbound Rules according to your needs.
 


	After creating all the needed rules, click on Create security group to create it. 
	 
	
 


	You have successfully created all needed Security Groups. All necessary Network-related prerequisites have been created. 
	 
 


	
		Note: 
	 

	
		For more information about Security Groups, please visit Amazon´s Product Documentation pages. 
		 
	 



	 
 


	Deploy all needed Instances in your VPC



	An Amazon EC2 Instance is a Virtual Machine in the AWS Cloud environment. Amazon EC2 provides a wide range of Instance types. You can choose an Instance type that provides the compute resources, memory, storage, and network configurations that meet your needs. 
 


	In this section, we will initially deploy the following Instances:
 


	
		(1) Jump Host for accessing the environment over the Internet using RDP
	
	
		(1) Domain Controller for providing all needed Active Directory functionalities
	
	
		(2) Cloud Connectors
	
	
		Further Instances will be created automatically during the Machine Catalog creation.
	



	
		Important: 
	 

	
		To access a non-domain-joined Windows-based Instance, you need an EC2 Key Pair to decrypt the administrator password. A Key Pair, consisting of a public key and a private key, is a set of security credentials you use to prove your identity. 
		 
		After joining the Instance to a Domain, you can log on using the Domain credentials. 
		The Key Pair is used only for local Admin access. 
		 
	 



	 
 


	Creating the EC2 Key Pair



	Before deploying Windows Instances, you must create a Key Pair to gain access to the Instance after it is created.
 


	
		Log on to your AWS console, enter EC2 in the Search bar at the top of the page, and choose the Network &amp; Security option. Click on Key Pairs on the Dashboard. If no Key Pair was already created, click Create key pair.. 
		
	
	
		Choose the needed settings and click Create key pair. 
		
	



	 
 


	
		Important: 
	 

	
		After successful creation, download and store the Key Pair in a safe place. 
		You will need it each time you access the Windows Instance with local administrator credentials. 
		 
	 



	 
 


	Deploying the Jump Host Instance



	Our environment will be completely shielded and deployed in the already-created Private Subnet for security reasons. 
	Administrative access to the Instances is only possible using a dedicated Jump Host Instance in the Public Subnet.
 


	
		Important: 
	 

	
		Choose the right Instance type that fits your needs. 
		Each type offers different compute, memory, and storage capabilities, which reflect different costs. An overview of all available Instance types can be found here. 
		 
		In this guide, we chose a t2.medium type for the Jump Host. 
		 
	 



	
		Log on to your AWS console, enter EC2 in the Search bar at the top of the page, and choose the Instances option. Click on Launch instance on the Dashboard. 
		
	
	
		Choose all relevant settings for the new Instance, such as Name and Tags, AMI Type and AMI Image, Instance Type, Key Pair to log in, Security Group to which this Instance will be bound, and Storage configuration. 
		Set advanced details according to your needs. 
		
	
	
		After choosing and setting all the needed information, click Launch instance to create the Instance.
	
	
		After successful creation, the Jump Host Instance is listed on the Instances tab. 
		
	



	 
 


	Connecting to the Jump Host Instance Instance



	For security reasons, the Jump Host Instance is not Domain-joined. You need to log on to it using local administrator credentials.
 


	As mentioned in the Key Pair section, you need an EC2 Key Pair to decrypt the administrator password.
 


	
		To log on to the Instance, open the AWS console, enter EC2 in the Search bar at the top of the page, and choose the Instances option.
	
	
		Mark the Instance you want to connect to and click on Connect. 
		
	
	
		Choose RDP client as the connection method and click Get password. 
		
	
	
		Now upload the mentioned Key Pair using Upload private key file. Click Decrypt password after uploading to get the administrator credentials. 
		
	
	
		Now you have the decrypted login information and can log on to the Jump Host Instance using RDP. 
		
	



	 
 


	Deploying the Domain Controller Instance



	For this environment, we decided to follow our standard deployment type for Domain Controllers: 
	 
	The AD deployment consists of a Hub-and-Spoke model. Each Resource Location running on a Hypervisor/Hyperscaler is connected to the primary Domain Controller using IPSec-based Site-to-Site VPNs. Each Resource Location can have its subdomain if needed.
 


	
		Note: 
	 

	
		You can follow the steps mentioned above to deploy the Jump Host Instance. 
		 
	 



	 
 


	After the successful creation of the Domain Controller Instance, you can access it by:
 


	
		Connect to the Jump Host Instance using RDP and log on using the Key Pair.
	
	
		Connect to the Domain Controller Instance using RDP from the Jump Host Instance.
	
	
		The Domain Controller and Replication configurations are out of this guide's scope. 
		 
	



	Assuming the Domain Controller Instance is completely configured, you can proceed to the next step – deploying and configuring the Cloud Connector Instances.  
	 
 


	Deploying and Configuring the Resource Location and its Cloud Connector Instances



	Connecting your resources to Citrix Cloud involves deploying Cloud Connectors in your environment and creating Resource Locations.
 


	Resource Locations contain the resources required to deliver Cloud Services to your subscribers. Depending on which Citrix Cloud services you use and the services you want to provide your subscribers, they can contain different resources.
 


	To create a Resource Location, install at least two Cloud Connectors in your domain. 
 


	
		Note: 
	 

	
		You can follow the steps mentioned above for deploying the Jump Host Instance, but we recommend choosing another Instance type, such as a t3.large. 
		 
	 



	After the successful creation of the Cloud Connector Instances, you can access them by:
 


	
		Connect to the Jump Host Instance using RDP and log on using the Key Pair
	
	
		Connect to the Cloud Connector Instances using RDP from the Jump Host Instance
	



	Before deploying the Cloud Connector software, you must add the Cloud Connector Instances to the Domain. This step is outside the scope of this guide.
 


	For more detailed step-by-step instructions, please look at the corresponding part in this guide.
 


	As all Cloud Connector Instances were successfully registered, you can proceed to the next step – deploying and configuring Amazon WorkSpaces Core.
 


	 
 


	Deploying Amazon WorkSpaces Core



	This is a multi-step process: Create the Image, a Directory Connector, and the AWC Deployment.
 


	If your licensing agreement with Microsoft allows it, you can bring and deploy your Windows 10 or 11 VDI machines into your WorkSpaces Core environment. Before enabling your account for Bring your Own Licenses (BYOL), you must go through a verification process to confirm your eligibility for BYOL. You can find more information here. You then need to enable BYOL for your account by following the instructions in Enable BYOL for your eligible WorkSpaces account using the Amazon WorkSpaces console.
 


	
		Log on to your AWS console and enter WorkSpaces in the Search bar on top of the page. 
		
	
	
		Choose Account Settings from the WorkSpaces Console. 
		
	
	
		If you don't see the Enable BYOL option, your account is not eligible for BYOL. You must create a Technical Support Case to enable your account to use BYOL. 
		 
		
	
	
		After your account is enabled for BYOL, you can set it up by clicking on Set up BYOL. 
		Under Bring Your Own License (BYOL), in the Management network interface IP address range area, choose an IP address range, then choose Display available CIDR blocks.
	
	
		Select the CIDR block you want to use and then choose Enable BYOL.
	
	
		After successful enrollment (enabling BYOL can last several hours), the WorkSpaces Console shows the successful BYOL enablement. 
		
	



	 
 


	Deploying and Configuring the Image 



	After you confirm that your VM meets the Microsoft BYOL requirements by following the instructions in Confirm that the Windows VM in Amazon WorkSpaces meets the requirements for Microsoft BYOL, you must export the VM from your virtualization environment. You will need this to create an image for BYOL that you can use in WorkSpaces.
 


	The VM you export must be on a single volume with a maximum size of 70 GB and at least 10 GB of free space. 
 


	
		Note: 
	 

	
		Describing the whole process of creating a BYOL Image is out-of-scope of this guide. 
		 
		For more information, see the documentation for Exporting your VM from its virtualization environment, Bring Your Own Windows desktop licenses in WorkSpaces - Amazon WorkSpaces, and Bringing your Windows 11 image to AWS with VM Import/Export.
	 

	
		 
	 



	 
 


	
		Important: 
	 

	
		To stay compliant with Microsoft licensing terms, AWS runs your BYOL WorkSpaces on hardware dedicated to you in the AWS Cloud. 
		 
	 



	 
 


	Your Image must run one of the following Windows versions:
 


	
		Windows 10 Version 22H2 (November 2022 Update)
	
	
		Windows 10 Enterprise LTSC 2019 (1809)
	
	
		Windows 10 Enterprise LTSC 2021 (21H2)
	
	
		Windows 11 Enterprise 23H2 (October 2023 release)
	
	
		Windows 11 Enterprise 22H2 (October 2022 release)
	



	 
 


	
		Important: 
	 

	
		While AWC creates the VDI VMs, the VDAs are not configured to get the list of Cloud Connectors. 
		You must manually configure the VDAs by setting the adjacent Registry key or deploying a GPO containing the addresses of the Cloud Connectors. For more information, see VDA registration. 
		 
	 



	
		In this example, we set the Registry key HKEY_LOCAL_MACHINE\Software\Citrix\VirtualDesktopAgent\ListOfDDCs (REG_SZ) and fill in the DNS name of the Cloud Connector. 
		
	
	
		Make sure to check the successful registration to the Cloud Connectors: 
		Restart the Citrix Desktop Service and open the Event Viewer.
	
	
		Look for the adjacent events in the Application log: 
		
	



	 
 


	Deploying and Configuring the Directory 



	WorkSpaces uses a directory to store and manage information for your WorkSpaces and users. The most used options are:
 


	
		AD Connector — Use your existing on-premises Microsoft Active Directory. 
		Users can sign into their WorkSpaces using their on-premises credentials and access on-premises resources from their WorkSpaces. 
		 
	
	
		AWS Managed Microsoft AD — Create a Microsoft Active Directory hosted on AWS.
	



	In this example, we use our own Domain Controller – an Instance running on Amazon EC2, which runs all relevant AD roles.
 


	
		Log on to your AWS console and enter Directory Service in the Search bar at the top of the page. 
		
	
	
		Click on Set up directory. 
		
	
	
		Select AD Connector from the list of Directory types according to your needs. 
		
	
	
		Select the Directory Size according to your needs. 
		
	
	
		Choose the VPC and Subnet settings according to your needs. 
		
	
	
		Enter all Connect to AD-related settings according to your needs. 
		
	
	
		Review all settings and click Create directory to start the process. 
		
	
	
		The Directory will now be created. This can be a lengthy process. Please check the Directory dashboard to see if the Directory was successfully created and is available before proceeding. 
		
	



	 
 


	Deploying and Configuring the Amazon Web Services Account



	This procedure enables all needed permissions for Citrix DaaS to connect to AWS.
 


	
		Log into your Citrix Cloud Console, open the DaaS service, and choose Quick Deploy. 
		
	
	
		Choose Amazon WorkSpaces Core. 
		
	
	
		Under Step 2, Click on Start. 
		
	
	
		On the shown page, click on Download AWS CloudFormation Template. 
		
	
	
		Log on to your AWS console and enter CloudFormation in the Search bar on top of the page. 
		
	
	
		Under Create Stack, click on With new resources (standard).
	
	
		Select Choose an existing template and Upload a template file on the following page. Click Choose file and upload the Cloud Formation template you downloaded from Citrix Quick Deploy. Click Next. 
		
	
	
		Provide a stack name and a Role name for the role that will be created. Click Next. 
		
	
	
		Leave all the default parameters on the next screen, Acknowledge the warning message, and click Next. 
		
	
	
		Click Submit.
	
	
		Once the Cloud Formation deployment is done, go to IAM in AWS and search for the created role. Copy the ARN. It will be used later in the configuration. 
		
	



	 
 


	Creating the Deployment using the Quick Deploy wizard of Citrix DaaS



	A Deployment is a group of VDI Desktops users can access from their Citrix Workspace. 
	This procedure specifies the characteristics of the Virtual Machines to be deployed as desktops and which AD users can use them.
 


	
		Note: 
	 

	
		Amazon Workspaces Core sets the computer names when the Workspace is created. They are used as an identifier to ensure accurate Workspace data. You cannot modify the computer name using Citrix DaaS for Amazon Workspaces Core. 
		 
	 



	Citrix DaaS WebStudio enables the creation of the AWC Deployment using the QuickDeploy wizard.
 


	
		Log into your Citrix Cloud Console, open the DaaS service, and choose Quick Deploy. 
		
	
	
		Choose Amazon WorkSpaces Core. 
		
	
	
		The Quick Deploy wizard shows all the needed steps. As the Resource Location was already created, the wizard marks this first step as Completed. 
		 
		 
		 
		
	
	
		You can review each completed step by clicking Review. 
		
	
	
		Next, connect the Amazon Web Services account. Click Start to begin.
	
	
		Review the prerequisites and create the needed roles. The process was described earlier in this guide. Click Next
	
	
		Enter the arn: noticed above into the Role ID field and enter an appropriate name according to your needs. Click Next. 
		
	
	
		Choose the AWS region according to your needs. Click Next. 
		
	
	
		The Quick Deploy wizard will show that BYOL support has been successfully enabled. If not, you must stop and follow the instructions above to enable BYOL. If BYOL is already enabled, click Next. 
		
	
	
		Review the Summary and click Finish. 
		
	
	
		Assigning the Amazon Web Services account is complete. 
		
	
	
		The next step is to create the Directory Connection. Click Start to begin. 
		
	
	
		Review the prerequisites and click Next. 
		
	
	
		Choose the AWC-adjacent Citrix Cloud Resource Location. Click Next. 
		
	
	
		Choose the correct Account representing the needed arn:. Click Next. 
		
	
	
		The wizard chooses the Directory and the Subnets assigned to the chosen Account. Click Next.
	
	
		Enter the Organizational unit, select the Security Group and the administrator privileges according to your needs. Click Next. 
		
	
	
		Review the Summary and enter an appropriate Name. Click Add directory connection to assign the Directory. 
		
	
	
		Assigning the Directory Connection is complete. 
		
	
	
		The next step is to assign the Image. Click Start to begin. 
		
	
	
		Look at the prerequisites corresponding to your Image type and needs. Click Next: Choose image to proceed. 
		
	
	
		Choose the Image. Enter an appropriate Name and select the correct Account. After selecting the Account, the wizard lists all the images assigned. Select the Image according to your needs. 
		
	
	
		Choose the Ingestion of dedicated Graphic processors. Select Applications according to your needs, which will be included in the Image during the Image Creation process. Click Next: Summary. 
		
	
	
		Review the Summary and click on Import Image. This process might take several hours. 
		 
		
	
	
		Assigning the Image is complete. 
		
	
	
		The next step is Deliver the Deployment to the Users. Click Start to begin. 
		
	
	
		Review the Create deployment settings. The Directory Connection and Image chosen earlier should be set. Choose the Performance settings according to your needs. Click Next: Encryption. 
		
	
	
		Choose the Encryption settings according to your needs. 
		
	
	
		Select the Desktops settings according to your needs. Beware of choosing the Number of VDAs and the Volume sizes within your needs to limit costs. 
		
	
	
		If you want to assign the AD-User(s) to the VDIs during the creation, choose the AD-User(s) here. 
		
	
	
		Review the Summary and assign an appropriate name to the Deployment. 
		
	
	
		The Quick Deploy wizard should show all steps as complete. 
		
	
	
		Review the settings. 
		 
		 
		 
		 
		
	
	
		The creation of the AWC Deployment is completed. This could take some time to complete.
	



	 
 


	Test User Access to this Environment



	Log on to your environment using Citrix WorkSpace App using User credentials assigned to this environment. 
	Check if all assigned Applications and Desktops are available: 
	 
	 
	The assigned AWC Desktop is available. The environment is ready to use: 
	
 


	 
 


	Summary



	Deploying Citrix DaaS on Amazon WorkSpaces Core is straightforward. 
	However, all prerequisites must be fulfilled on the Amazon and Citrix DaaS sides. 
	Bringing your own Windows licenses (BYOL) into WorkSpaces Core enables you to use Windows Desktop-based VDI deployments on Amazon´s EC2.
 


	We have shown examples of creating all the needed entities and configurations to deploy a working Citrix DaaS environment running on Amazon WorkSpaces Core.]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_10/vpc2.png.512d515f96fe713ffd690492e0383ec6.png" length="145810" type="image/png"/><pubDate>Sat, 02 Nov 2024 11:28:00 +0000</pubDate></item><item><title>Unified Communications SDK</title><link>https://community.stage.citrix.com/tech-zone/learn/tech-briefs/ucssdk/</link><description><![CDATA[Overview



	Over the past few years, the need for real-time communications has sparked numerous vendors to adopt and build WebRTC-based applications. With WebRTC, vendors can build real-time communications within browsers that are accessible from anywhere.  This anywhere accessibility reduces costs by replacing hardware endpoints with softphones and increases scalability and flexibility for enterprises. Many enterprises have adopted these WebRTC-based applications within their call centers, where Citrix virtual desktops provide access to the enterprise environment and applications, including these WebRTC-based applications. User experience is critical in real-time communications, and the Citrix Unified Communications SDK is a private SDK that allows vendors to integrate the SDK into their WebRTC applications to offload media rendering to the endpoint, simulating local endpoint calls within the virtual desktop. For vendors interested, please sign up here.
 


	For Citrix customers using WebRTC-based applications, the SDK is not required as, in most cases, application configuration is all that is required.
 


	Unified Communications SDK Explained



	The Citrix Unified Communications SDK allows Unified Communications vendors, Cloud contact center providers, Communications Platform as a Service (CPaaS) providers, or any WebRTC-based communications vendors to integrate seamlessly with electron-based desktop or browser-based applications.
 


	
 


	The Unified Communications SDK is a JavaScript-based software development kit (SDK) built following WebRTC specifications. Integration with the SDK Applications to interface with the Citrix virtualization stack, allowing for remote call handling by our RTC media engine with the Citrix Workspace app. When integrated, resource-intensive media rendering can be offloaded to the endpoint, saving Citrix Virtual Desktop resources and Citrix ICA channel bandwidth while enhancing the performance and quality of the end-user session. 
 


	Unified Communications SDK Architecture



	
 


	Vendor electron or browser-based applications that intend to support WebRTC redirection on a Citrix virtual environment must utilize the Citrix Webrtc.js SDK. Once the SDK is loaded and used, the HdxRtcEngine.exe process is launched on the client endpoint if redirection succeeds. Once HdxRtcEngine.exe is launched on the client endpoint, any signaling and payload data flows from Citrix VDA to the client endpoint, reaches the cloud, bounces back to the client endpoint, and then is forwarded to VDA. For example, a complete round trip of data flow could be Electron App -&gt; CitrxWebrtc.js SDK -&gt; Citrix VDA components -&gt; Citrix Client Endpoint components -&gt; Cloud -&gt; Citrix Client Endpoint components -&gt; Citrix VDA components -&gt; CitrxWebrtc.js SDK -&gt; Electron App.
 


	
		Note:
	 

	
		WebRTC SDK and Unified Communication SDK are used interchangeably as they are the same SDK.
	 



	Unified Communications SDK Benefits



	Using the Unified Communications SDK within your WebRTC-based application provides several benefits.  These benefits include:
 


	
		Enhanced media processing performance by offloading processor-intensive media encoding/decoding from the Citrix Virtual Delivery Agent (VDA) to the client endpoint, increasing overall responsiveness for end users.
	
	
		Reduced CPU and bandwidth usage on Citrix VDA, allowing IT to support more concurrent users per host and enterprises to scale Citrix virtual desktop deployments cost-effectively.
	
	
		Lower total cost of ownership for enterprises, as optimized endpoints extend legacy virtual desktop lifespans and reduce host infrastructure needs, thus reducing capital expenditures and operating costs over time.
	
	
		Support for Windows, Mac, Linux, ChromeOS, and HTML5 endpoint platforms
	



	Unified Communication SDK Features



	With Unified Communications SDK 4.1, both Node-based, i.e., Electron, and browser-based applications can integrate the SDK and provide an optimized customer experience with all the features listed below. These are all the features that Unified Communications applications need to provide the best experience to the customers.     
 


	
		Audio / Video p2p &amp; Conference - Optimized audio, video and conference calls.
	
	
		Screen sharing - Optimized screen sharing capabilities.
	
	
		Multi-monitor sharing - Sharing multi-monitor screens.
	
	
		DTMF – Allow Dual-tone multi-frequency commands for all participants.
	
	
		Proxy server support – Support for several unified communications application proxy support functions.
	
	
		Application sharing - Share applications instead of the whole screen optimally.
	
	
		Dynamic e911 - Retrieve e911 data for compliance purposes.
	
	
		Remote audio - Rings and notifications from audio/video calls can be configured to ring on the endpoint.
	
	
		Stream restriction and Simulcast – the ability to restrict the number of video streams and quality based on the endpoint capabilities.
	
	
		Web HID API – Integrate seamlessly with Human Interface Devices (HID) for enhanced call control through headsets.
	
	
		Audio Context and Share system audio API—Deliver richer audio experiences with share system audio and a partial implementation of Audio Context.
	
	
		Advanced Overlay Clipping (Preview) - Enjoy a more polished and user-friendly experience with refined overlays and clipping functionalities.
	
	
		Screen Recording Support (Preview) - Vendor applications can fetch endpoint screen captures through UCSDK APIs.     
	



	Citrix Dependencies



	UCSDK feature functionality depends on three factors: the UCSDK version that the vendor has integrated, the Citrix Virtual Apps and Desktops, and the Citrix Workspace app version in use.
 


	Configuration



	On the Citrix side, please ensure the following is configured so that the optimized application is allowed to be optimized.
 


	
		Ensure the Microsoft Teams redirection policy is turned on. Refer here for more information.  Note that this policy is ON by default
	
	
		Third-party electron or browser-based applications that utilize the CitrixWebrtc.js SDK are not supported by default. The CtxHdxWebSocketService (WebSocketService.exe) will not allow connections from applications that are not on the allowlist. The desired application binary executable name must be added to a whitelist registry key. 
	



	On the VDA:
 


	
		Create a Key Path: HKLM\Software\WOW6432Node\Citrix\WebSocketService
	
	
		KeyName: ProcessWhitelist
	
	
		Type: MULTISZ
	
	
		Key Value: Mytestapp.exe
		
			
				If you have multiple applications, type each application in a new line. Do not copy and paste from a text file or insert commas.
			
			
				Make sure the name provided matches the executable name of the application.
			
			
				This registry value is not case-sensitive.
			
			
				If the application is accessed through a browser instead of a full-fledged desktop application, then you need to allow for, e.g., "chrome.exe" in the registry value.
			
		
	
	
		Once the above registry is configured successfully, reboot the VDA or restart CtxHdxWebSocketService to finish the Whitelist setup
	



	On the Client:
 


	On the Client - No configuration is needed. Just install the Citrix Workspace app.
 


	Each vendor might have a very specific application name. Hence, refer to the vendor documentation below to determine which application name must be allowed in the WebSocketService.
 


	Feature Matrix



	All the versions listed in the table indicated the minimum version of the specific component UCSDK, Virtual Delivery Agent, or Citrix Workspace app required to support a particular feature. Currently, three versions of UCSDK are available. UCSDK 3.1.0, UCSDK 4.0.2, and UCSDK 4.1.0 is the latest generally available version that vendor applications support.
 


	
		Note: 
	 

	
		As version updates happen frequently, please refer to Citrix Virtual Apps and Desktops and Citrix Workspace app product lifecycle pages to ensure a particular version is supported. For many of the older features, the versions mentioned are the last supported Current Release (CR) versions at the time of writing this document.
	 



	
		
			
				
					Feature
				 
			
			
				
					UCSDK Version
				 
			
			
				
					VDA
				 
			
			
				
					CWA Windows
				 
			
			
				
					CWA Mac
				 
			
			
				
					CWA Linux
				 
			
			
				
					CWA HTML5
				 
			
			
				
					CWA ChromeOS
				 
			
		
		
			
				
					Audio / Video (p2p &amp; conference)
				 
			
			
				
					3.1.0
				 
			
			
				
					2203 LTSR Latest CU5 / 2305 CR
				 
			
			
				
					2203.1 LTSR / 2305 CR
				 
			
			
				
					2305
				 
			
			
				
					2305
				 
			
			
				
					2305
				 
			
			
				
					2305
				 
			
		
		
			
				
					Screen sharing
				 
			
			
				
					3.1.0
				 
			
			
				
					2203 LTSR Latest CU5 / 2305 CR
				 
			
			
				
					2203.1 LTSR / 2305 CR
				 
			
			
				
					2305
				 
			
			
				
					2305
				 
			
			
				
					2305
				 
			
			
				
					2305
				 
			
		
		
			
				
					DTMF
				 
			
			
				
					3.1.0
				 
			
			
				
					2203 LTSR Latest CU5 / 2305 CR
				 
			
			
				
					2203.1 LTSR / 2305 CR
				 
			
			
				
					2305
				 
			
			
				
					2305
				 
			
			
				
					2305
				 
			
			
				
					2305
				 
			
		
		
			
				
					Proxy Server Support
				 
			
			
				
					3.1.0
				 
			
			
				
					2203 LTSR Latest CU5 / 2305 CR
				 
			
			
				
					2203.1 LTSR / 2305 CR
				 
			
			
				
					2305
				 
			
			
				
					2305
				 
			
			
				
					2305
				 
			
			
				
					2305
				 
			
		
		
			
				
					App Sharing
				 
			
			
				
					3.1.0
				 
			
			
				
					2203 LTSR Latest CU5 / 2305 CR
				 
			
			
				
					2203.1 LTSR / 2305 CR
				 
			
			
				
					2305
				 
			
			
				
					2305
				 
			
			
				
					N/A
				 
			
			
				
					N/A
				 
			
		
		
			
				
					Dynamic e911
				 
			
			
				
					3.1.0
				 
			
			
				
					2203 LTSR Latest CU5 / 2305 CR
				 
			
			
				
					2203.1 LTSR / 2305 CR
				 
			
			
				
					2305
				 
			
			
				
					2305
				 
			
			
				
					2305
				 
			
			
				
					2305
				 
			
		
		
			
				
					Multi Window
				 
			
			
				
					3.1.0
				 
			
			
				
					2203 LTSR Latest CU5 / 2305 CR
				 
			
			
				
					2203.1 LTSR / 2305 CR
				 
			
			
				
					2305
				 
			
			
				
					2305
				 
			
			
				
					2305
				 
			
			
				
					2305
				 
			
		
		
			
				
					SDP Unified Plan Support
				 
			
			
				
					3.1.0
				 
			
			
				
					2203 LTSR Latest CU5 / 2305 CR
				 
			
			
				
					2203.1 LTSR / 2305 CR
				 
			
			
				
					2305
				 
			
			
				
					2305
				 
			
			
				
					2305
				 
			
			
				
					2305
				 
			
		
		
			
				
					Stream resolution / Simulcast
				 
			
			
				
					3.1.0
				 
			
			
				
					2203 LTSR Latest CU5 / 2305 CR
				 
			
			
				
					2402 LTSR / 2307 CR
				 
			
			
				
					2305
				 
			
			
				
					2305
				 
			
			
				
					2312
				 
			
			
				
					2312
				 
			
		
		
			
				
					Remote Audio (with Loop)
				 
			
			
				
					3.1.0
				 
			
			
				
					2203 LTSR Latest CU5 / 2305 CR
				 
			
			
				
					2402 LTSR / 2307 CR
				 
			
			
				
					2307.1
				 
			
			
				
					2307.1
				 
			
			
				
					2405
				 
			
			
				
					2405
				 
			
		
		
			
				
					Browser-based UCSDK
				 
			
			
				
					4.0.2
				 
			
			
				
					2407 CR
				 
			
			
				
					2203.1 LTSR / 2305 CR
				 
			
			
				
					2305
				 
			
			
				
					2305
				 
			
			
				
					2305
				 
			
			
				
					2305
				 
			
		
		
			
				
					Web HID API
				 
			
			
				
					4.0.2
				 
			
			
				
					2203 LTSR Latest CU5 / 2305 CR
				 
			
			
				
					2409
				 
			
			
				
					2411
				 
			
			
				
					2411
				 
			
			
				
					2505
				 
			
			
				
					2505
				 
			
		
		
			
				
					Web Audio API
				 
			
			
				
					4.0.2
				 
			
			
				
					2203 LTSR Latest CU5 / 2305 CR
				 
			
			
				
					2405
				 
			
			
				
					2405
				 
			
			
				
					2405
				 
			
			
				
					Future
				 
			
			
				
					Future
				 
			
		
		
			
				
					Restart ICE
				 
			
			
				
					4.1.0
				 
			
			
				
					2203 LTSR Latest CU / 2305 CR
				 
			
			
				
					2503
				 
			
			
				
					2503
				 
			
			
				
					2503
				 
			
			
				
					2502.10
				 
			
			
				
					2502.10
				 
			
		
		
			
				
					Screen Recording (Preview) 1
				 
			
			
				
					4.1.0
				 
			
			
				
					2503
				 
			
			
				
					2503
				 
			
			
				
					N/A
				 
			
			
				
					N/A
				 
			
			
				
					N/A
				 
			
			
				
					N/A
				 
			
		
	



	
		 1Note 
	 

	
		We’ve introduced a new policy for screen recording, so you need the 2503 version of the delivery controller, which comes with CVAD 2503
	 



	Current Vendor Support



	Several third-party vendors have integrated the Unified Communications SDK into their products. As of this writing, the current list of vendors and their supporting documentation is listed here:
 


	
		
			
				
					Vendor
				 
			
			
				
					Documentation
				 
			
		
		
			
				
					Five9
				 
			
			
				
					Installing the Software for Virtual Desktop Environments 
				 
			
		
		
			
				
					Amazon Connect
				 
			
			
				
					https://docs.aws.amazon.com/connect/latest/adminguide/using-ccp-vdi-citrix-step-by-step.html
				 
			
		
		
			
				
					Microsoft Teams
				 
			
			
				
					Optimization for Microsoft Teams (New) | Citrix Virtual Apps and Desktops 7 2503
				 
			
		
		
			
				
					8x8
				 
			
			
				
					https://support-portal.8x8.com/helpcenter/viewArticle.html?d=b9270b6e-d5ca-4515-98fc-5a0e0d86db8
				 
			
		
		
			
				
					Intermedia
				 
			
			
				
					https://support.intermedia.com/app/articles/detail/a_id/24355
				 
			
		
		
			
				
					Avaya
				 
			
			
				
					Avaya Experience Platform™ Public Cloud VDI solution for Citrix
				 
			
		
		
			
				
					Rainbow
				 
			
			
				
					\CITRIX Optimization for Rainbow Desktop application
				 
			
		
		
			
				
					Ribbon
				 
			
			
				
					https://ribboncommunications.github.io/citrix-webrtc-sdk/tutorials/index.html#/Getting%20Started
				 

				
					 
				 

				
					GitHub - RibbonCommunications/citrix-webrtc-sdk
				 
			
		
		
			
				
					Ring Central
				 
			
			
				
					https://support.ringcentral.com/article-v2/Using-RingCentral-for-Teams-in-a-VDI-environment.html?brand=RingCentral&amp;product=RingEX&amp;language=en_US
				 
			
		
		
			
				
					Pexip
				 
			
			
				
					Deploying the Connect desktop app under Citrix | Pexip Infinity Docs
				 
			
		
		
			
				
					Vitero
				 
			
			
				
					https://www.vitero.com/en/software/components/
				 
			
		
		
			
				
					IPC Unigy
				 
			
			
				
					Unigy® Soft Client | IPC
				 
			
		
		
			
				
					Twilio
				 
			
			
				
					Flex on Citrix VDI | Twilio
				 
			
		
		
			
				
					Talkdesk
				 
			
			
				
					https://support.talkdesk.com/hc/en-us/articles/36439290457499--Preview-VDI-Connect
				 
			
		
	



	Summary



	The Citrix Unified Communication SDK integrates seamlessly with electron-based desktop or browser-based applications, allowing third-party vendors to provide an exceptional end-user experience. The SDK is a private SDK, so third-party vendors and partners are encouraged to reach out to our product management team to discuss how they can integrate the SDK into their technology and inform them of the latest industry trends so they can build the latest features within the Unified Communications SDK.]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_10/image.png.3099d4309d73cb440eeb3cba845d5db0.png" length="94372" type="image/png"/><pubDate>Tue, 22 Oct 2024 18:35:00 +0000</pubDate></item><item><title>PoC Guide: Deploying Citrix DaaS on Amazon EC2</title><link>https://community.stage.citrix.com/tech-zone/learn/poc-guides/daas-on-amazon-ec2/</link><description><![CDATA[Deploying Citrix DaaS on Amazon EC2 
	 



	Overview



	This guide will help you deploy CitrixDaaS on Amazon EC2.
 


	
		Note: 
	 

	
		For a fully automated deployment of Citrix DaaS using Terraform, please see our guide on Citrix Tech Zone: Citrix DaaS and Terraform - Automatic Deployment of a Resource Location on Amazon EC2. 
		 
	 



	Citrix DaaS



	Citrix DaaS provides employees with a complete workspace from any device while leaving most of the setup, upgrades, and monitoring to Citrix. Your IT department will continue to manage and control the virtual machines, applications, and policies on the back end, while Citrix will manage all the key infrastructure, installation, setup, and upgrades needed to maintain a Citrix environment. This reduces administrative overload and ensures you have all the latest features and functionality.
 


	
 


	By hosting Virtual Delivery Agents (VDAs) in Resource Locations managed by the customer, the amount of data and information accessible to the Citrix Cloud control plane is limited. The control plane only has access to metadata (machine names, application names, application shortcuts, and so forth). 
	This keeps intellectual property secure. All communication between the customer’s resource location and the Citrix Cloud control plane is encrypted using HTTPS port 443 through the TCP protocol. 
	All connections are outbound, and no inbound connections are accepted. Citrix collects only the necessary logs to troubleshoot potential issues.
 


	Components Managed by Citrix: All components that Citrix manages are kept highly available.
 


	
		Studio: Management console that is used to configure your environment
	
	
		Monitor: Monitoring tool that enables IT support and help desk personnel to troubleshoot issues
	
	
		License Server: The component that manages licenses for the environment and provides usage statistics
	
	
		Workspace Configuration: The collection of settings that allows you to create configurations and customizations for your users’ workspace
	
	
		Delivery Controllers: Communicates through the Cloud connectors to load balance applications and desktops, authenticate users, and broker or prioritize connections
	
	
		SQL: Server database used to store the data from the controllers
	
	
		Cloud Connectors: Communication channel between Citrix Cloud and the customer’s resource location. These are hosted in each of the customer’s resource locations but are pushed scheduled updates from the Citrix Cloud services. (the only parts handled by the customer are Cloud Connector Windows updates and patching)
	



	Components Managed by Customer:
 


	
		Virtual Delivery Agents: VDAs register with the Cloud Connectors to broker connections and provide users with the resources that they need to access. Citrix DaaS can use either the current release (CR) VDA or the long-term service release (LTSR) VDA (2402 or higher).

		
			
				CR VDAs reach end of maintenance six months after they are released and end of life 18 months after they have been released. CRs are not eligible for extended support programs.
			
			
				LTSR VDAs reach end of life five years after they are released. The end of extended support is ten years after they have been released.
			
			
				The full lifecycle matrix can be found here.
			
			
				An FAQ on LTSR can be found here.
			
		
	
	
		Active Directory: Used for authentication and authorization, Active Directory authenticates users and ensures that they are getting access to appropriate resources. A subscriber’s identity defines the services to which they have access in Citrix Cloud. This identity comes from Active Directory domain accounts provided from the domains within the resource location.
	
	
		Identity Provider: The final authority for the user’s identity. The following identity providers are supported: on-premises Active Directory, Active Directory plus token, Azure Active Directory, Citrix Gateway, and Okta. An in-depth look at how Citrix Workspace handles identity and authentication can be found here
	
	
		App and Desktop Workloads: The app and desktop instances published by Citrix customers can be on-prem, hosted in public clouds, or in a hybrid mixture of both. Citrix provides many tools to simplify and facilitate how these session hosts are built and maintained. By allowing customers to maintain their session hosts, intellectual property is protected.
	



	Components that can be managed either by Citrix or by Customer:
 


	
		Citrix Gateway: Used so external users can connect to internal resources. Citrix Gateway can either be deployed and managed within the customer’s resource location or deployed and managed in Citrix Cloud.
	
	
		StoreFront: Used as the web interface for access to applications and desktops. StoreFront is an optional component installed within a customer’s data center, or the cloud-hosted Workspace can be used for more functionality.
	
	
		
	



	 
 


	
		Note: 
	 

	
		For more information about Citrix DaaS, please visit the corresponding section in Citrix Tech Zone. 
		 
	 



	 
 


	Amazon Elastic Compute Cloud (Amazon EC2)



	
		Note: 
	 

	
		Some explanations and all Amazon EC2-related pictures were taken from Amazon´s Product Documentation pages. We want to provide up-to-date and in-depth information regarding Amazon-related entities. 
		 
	 



	Amazon EC2 provides on-demand and scalable computing capacity in the Amazon Cloud. 
 


	We need to deploy these features before installing Citrix DaaS:
 


	
		Virtual Private Cloud (VPC): A VPC is a virtual network containing all networking-relevant entities. 
	
	
		Availibility Zone: An Availibility Zone is a logical, stand-alone datacenter in a region.
	



	
		Subnets: A Subnet is a range of IP addresses in your VPC. Each Subnet can only be deployed in a single Availability Zone, it cannot be deployed over multiple Availability Zones.
	
	
		Gateways: A Gateway connects your VPC to another network. 
		An Internet Gateway connects your VPC to the Internet. 
		A NAT Gateway connects your Private Subnets to the Internet, to other VPCs, or to on-premises networks.
	



	
		Instances: Instances are Virtual Machines running your payload.
	
	
		Amazon Machine Images (AMIs): AMIs are preconfigured Virtual Machine templates for your instances. 
	
	
		Instance types: You can choose various Instance configurations e.g. different CPUs, different amounts of memory, storage, and networking configurations.
	
	
		Key pairs: Key pairs are needed to secure your Instances' login information.
	
	
		Security groups: Security groups act like virtual firewalls. 
		You can allow or reject Network traffic by specifying protocols, ports, source IP- and destination IP ranges.
	



	We will provide a more detailed description when we deploy each needed feature.
 


	The following diagram shows an example VPC: 
	The VPC has one Subnet in each of the Availability Zones in the Region, EC2 Instances in each Subnet, and an Internet Gateway to allow communication between the resources in your VPC and the Internet.
 


	
 


	 
 


	Prerequisites for deploying Citrix DaaS on Amazon EC2



	Before we can deploy Citrix DaaS on Amazon EC2, we need to create and configure all needed prerequisites on Amazon EC2:
 


	
 


	We assume you already have a Citrix Cloud tenant with an active trial or a paid subscription to the Citrix DaaS service and an active AWS account. 
	 
 


	Deploy an Amazon VPC



	An Amazon Virtual Private Cloud (VPC) enables you to launch AWS resources into a virtual network that you have defined.
 


	
		Important: 
	 

	
		It is possible to have multiple VPCs in your AWS tenant. 
		Create a dedicated VPC for your Citrix DaaS deployment. 
		Tag all resources for easier management. 
		The default VPC holds these IP address ranges: 172.31.0.0/16 
		 
	 



	
		Log on to your AWS console and open the VPC service dashboard (enter VPC in the Search bar on the top of the page). 
		
	
	
		No VPCs are defined, so we need to create one.
	
	
		Choose the AWS region where you want to create your VPC. 
		
	
	
		Fill out the VPC settings according to your needs (example screenshot). 
		 
		 
		
	
	
		After completing all settings, click filling out all settings and click Create VPC to start creation. It will take some moments before the VPC is ready. 
	
	
		
	



	You have successfully created a VPC.  
	 
 


	Deploy Subnets, Internet Gateways, NAT Gateways, Route Tables and Security Groups



	After creating the VPC, we deploy all needed network components, such as Subnets, Internet Gateways, NAT Gateways, and Router Tables.
 


	Deploying the Subnets



	A Subnet is a range of IP addresses in your VPC. 
	You can create AWS resources in specific subnets, such as EC2 instances. 
	Each Subnet can only be deployed in a single Availability Zone. 
	You can protect your infrastructure by deploying multiple Subnets in different Availability Zones. 
	You specify its IP addresses depending on the configuration of the VPC.
 


	The Subnet type is determined by how you configure routing for your Subnets:
 


	
		Public Subnet: The Subnet has a direct route to an Internet Gateway. 
		Resources in a Public Subnet can directly access the Internet.
	
	
		Private Subnet: The Subnet does not have a direct route to an Internet Gateway. 
		Resources in a Private Subnet require a NAT device to access the Internet.
	



	
 


	 
 


	
		Important: 
	 

	
		Each Subnet must be associated with a Route Table, specifying all allowed outbound traffic routes. Every Subnet is automatically associated with the main Route Table for the VPC. 
		Disable the Auto-assign IP settings , as otherwise, a public IPv4 address is automatically requested for each new Network Interface in this Subnet. 
		 
	 



	 
 


	
		Note: 
	 

	
		Security Groups enable you to increase security for the resources in your VPC as they act like virtual firewalls. 
		 
	 



	
		Log on to your AWS console, open the VPC service dashboard (enter VPC in the Search bar on top of the page), and choose the Subnets option:
	
	
		In this guide, we create 3 different Subnets:
		
			
				A Public Subnet containing the Jump Host VM
			
			
				A Private Subnet for the Cloud Connector VMs and other Infrastructure-related VMs
			
			
				A Private Subnet for all Worker VMs/VDAs 
				 
				 
				 
				
			
		
	
	
		Remember to disable the Auto-assign setting. 
		
	



	After creating the Subnets, you must create an Internet Gateway to enable the Public Subnet and the Jump Host-VM to connect to the Internet. 
	 
 


	Deploying an Internet Gateway



	An Internet Gateway allows communication between your VPC and the Internet. It supports IPv4 and IPv6 traffic and provides a target in your VPC Route Tables. 
	Resources in your Public Subnets can connect to the Internet if they have a public IPv4 address or an IPv6 address. The communication flow is two-way—the resources can also be contacted from the Internet. 
	For communication using IPv4, the Internet Gateway also performs Network Address Translation (NAT).
 


	
		Important: 
	 

	
		Make sure to secure the communication flow of your resources by using Network ACLs or Security Groups. 
		 
	 



	
		Log on to your AWS console, open the VPC service dashboard (enter VPC in the Search bar at the top of the page), and choose the Internet Gateways option.
	
	
		Give it a comprehensive name and click Create internet gateway. 
		
	
	
		After creating the Internet Gateway, you must attach it to the VPC by clicking the Attach to a VPC button. Choose the correct VPC. 
		
	



	To give the Public Subnet connection to the Internet, you need to adjust the adjacent Route Table. A Route Table contains a set of routes directing the network traffic from your Subnet or Gateway.
 


	
		Log on to your AWS console, open the VPC service dashboard (enter VPC in the Search bar at the top of the page), and choose the Route Tables option.
	
	
		Choose Create Route Table and set the values according to your needs. 
		
	
	
		Create the Route Table by clicking the adjacent button.
	
	
		After creation, choose the Route Table that has just been created and edit the route. 
		
	
	
		Enter 0.0.0.0/0 into the Destination field and choose the Internet Gateway you created as the Target. Click Add route to save the route to the Route Table.
	



	The Public Subnet now has a connection to the Internet. 
	 
 


	Deploying a NAT Gateway



	After creating the Internet Gateway, you need to create a NAT Gateway in the Private Subnet(s) to enable the Private Subnet(s) to connect to the Internet.
 


	A NAT Gateway is a Network Address Translation (NAT) device. 
 


	
		Important: 
	 

	
		You can use a NAT Gateway to enable instances in a Private Subnet to connect to services outside your VPC. External services cannot initiate a connection with those instances. 
		A NAT Gateway is for use with IPv4 traffic only. 
		 
	 



	When you create a NAT Gateway, you specify one of the following connectivity types:
 


	
		Public – (Default) Instances in Private Subnets can connect to the Internet.
	
	
		Private – Instances in Private Subnets can connect to other VPCs or your on-premises network but not to the Internet.
	



	
		Log on to your AWS console, open the VPC service dashboard (enter VPC in the Search bar at the top of the page), and choose the NAT gateways Tables option.
	
	
		Choose the settings according to your needs and click on Create NAT gateway to create it. 
		
	
	
		After creation, you need to add a Route Table and change the Route Table for the NAT Gateway as you did before for the Internet Gateway. 
		
	
	
		Add the correct settings and click on Add route.
	
	
		After changing the Route Table, you must edit the Subnet settings by choosing Subnet associations. Select the Internal Subnet and click on Save associations. 
		
	



	All instances in the Internal Subnet should be able to connect to the Internet. 
	 
 


	Set the IAM Permissions



	After creating all needed Network components, we must set all required IAM permissions.
 


	Identity and Access Management (IAM) allows control of access to AWS resources. 
	IAM controls who can be authenticated (signed in) and authorized (have permissions) to use Amazon VPC resources. 
	 
 


	Deploying the initial IAM Policy



	You can control access by attaching policies and attaching them to identities or resources. 
	A Policy defines the permissions associated with it. Policies are evaluated when a user or a root user wants to access a resource. 
	Permissions in the Policies determine whether the request is allowed or denied. 
	Most Policies are stored in AWS as JSON documents.
 


	
		Log on to your AWS console, enter IAM in the Search bar at the top of the page, and choose the IAM option.
	
	
		Click on Policies on the Dashboard. 
		
	
	
		Click on Create policy. 
		
	
	
		Click JSON and copy the JSON-code provided later into the Policy editor. 
		
	
	
		Click Next to name, review, and save the Policy. 
		
	



	You can find the current recommendations for the IAM settings in the Citrix Product Documentation.
 


	More information about IAM JSON-based policies can be found in the IAM JSON policy reference.
 


	You can see the JSON-defined policy we used in this guide here:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						{ 
						    "Version": "2012-10-17", 
						    "Statement": [ 
						        { 
						            "Action": [ 
						                "ec2:AttachVolume", 
						                "ec2:AssociateIamInstanceProfile", 
						                "ec2:AuthorizeSecurityGroupEgress", 
						                "ec2:AuthorizeSecurityGroupIngress", 
						                "ec2:CreateImage", 
						                "ec2:CreateLaunchTemplate", 
						                "ec2:CreateNetworkInterface", 
						                "ec2:CreateTags", 
						                "ec2:CreateVolume", 
						                "ec2:DeleteLaunchTemplate", 
						                "ec2:DeleteNetworkInterface", 
						                "ec2:DeleteSecurityGroup", 
						                "ec2:DeleteSnapshot", 
						                "ec2:DeleteTags", 
						                "ec2:DeleteVolume", 
						                "ec2:DeregisterImage", 
						                "ec2:DescribeAccountAttributes", 
						                "ec2:DescribeAvailabilityZones", 
						                "ec2:DescribeIamInstanceProfileAssociations", 
						                "ec2:DescribeImages", 
						                "ec2:DescribeInstances", 
						                "ec2:DescribeInstanceTypes", 
						                "ec2:DescribeLaunchTemplates", 
						                "ec2:DescribeLaunchTemplateVersions", 
						                "ec2:DescribeNetworkInterfaces", 
						                "ec2:DescribeRegions", 
						                "ec2:DescribeSecurityGroups", 
						                "ec2:DescribeSnapshots", 
						                "ec2:DescribeSubnets", 
						                "ec2:DescribeTags", 
						                "ec2:DescribeSpotInstanceRequests", 
						                "ec2:DescribeInstanceCreditSpecifications", 
						                "ec2:DescribeInstanceAttribute", 
						                "ec2:DescribeElasticGpus", 
						                "ec2:GetLaunchTemplateData", 
						                "ec2:DescribeVolumes", 
						                "ec2:DescribeVpcs", 
						                "ec2:DetachVolume", 
						                "ec2:DisassociateIamInstanceProfile", 
						                "ec2:RebootInstances", 
						                "ec2:RunInstances", 
						                "ec2:StartInstances", 
						                "ec2:StopInstances", 
						                "ec2:TerminateInstances" 
						            ], 
						            "Effect": "Allow", 
						            "Resource": "*" 
						        }, 
						        { 
						            "Action": [ 
						                "ec2:AuthorizeSecurityGroupEgress", 
						                "ec2:AuthorizeSecurityGroupIngress", 
						                "ec2:CreateSecurityGroup", 
						                "ec2:DeleteSecurityGroup", 
						                "ec2:RevokeSecurityGroupEgress", 
						                "ec2:RevokeSecurityGroupIngress" 
						            ], 
						            "Effect": "Allow", 
						            "Resource": "*" 
						        }, 
						        { 
						            "Action": [ 
						                "s3:CreateBucket", 
						                "s3:DeleteBucket", 
						                "s3:DeleteObject", 
						                "s3:GetObject", 
						                "s3:PutBucketAcl", 
						                "s3:PutObject", 
						                "s3:PutBucketTagging", 
						                "s3:PutObjectTagging" 
						            ], 
						            "Effect": "Allow", 
						            "Resource": "arn:aws:s3:::citrix*" 
						        }, 
						        { 
						            "Action": [ 
						                "ebs:StartSnapshot", 
						                "ebs:GetSnapshotBlock", 
						                "ebs:PutSnapshotBlock", 
						                "ebs:CompleteSnapshot", 
						                "ebs:ListSnapshotBlocks", 
						                "ebs:ListChangedBlocks", 
						                "ec2:CreateSnapshot" 
						            ], 
						            "Effect": "Allow", 
						            "Resource": "*" 
						        }, 
						        { 
						            "Effect": "Allow", 
						            "Action": [ 
						                "kms:CreateGrant", 
						                "kms:Decrypt", 
						                "kms:DescribeKey", 
						                "kms:GenerateDataKeyWithoutPlainText", 
						                "kms:GenerateDataKey", 
						                "kms:ReEncryptTo", 
						                "kms:ReEncryptFrom" 
						            ], 
						            "Resource": "*" 
						        }, 
						        { 
						            "Effect": "Allow", 
						            "Action": "iam:PassRole", 
						            "Resource": "arn:aws:iam::*:role/*" 
						        } 
						    ] 
						}
					 
				
			
		
	



	You have successfully created the IAM Policy. 
	 
 


	Deploying an IAM User Group



	An IAM User Group is a collection of IAM Users. You can specify permissions for multiple users. Any user in that user group automatically has Admins group permissions. 
 


	You can attach an Identity-based Policy to an IAM User Group so that all users in the user group receive the policy's permissions. 
 


	
		Log on to your AWS console, enter IAM in the Search bar at the top of the page, and choose the IAM option.
	
	
		Click on Groups on the Dashboard. 
		
	
	
		Enter and choose all needed information, such as the group name, before clicking Create user group. Add the AWS account root user and the previously created Policy here. 
		
	
	
		After creation, you can review the settings.
	



	You have successfully created the IAM User Group and associated the previously created Policy. 
	 
 


	Deploying an IAM User 



	An IAM User represents a user or workload who uses the IAM User to interact with AWS resources. An IAM User consists of a name and credentials.
 


	The IAM User can access AWS in different ways depending on its credentials – these are the relevant ones for this guide:
 


	
		Console password: The IAM User can type a password to sign in to interactive sessions such as the AWS Management Console. 
	
	
		Access keys: These are used to make programmatic calls to AWS. If the IAM User has active access keys, they continue to function and allow access through the AWS CLI, Tools for Windows PowerShell, AWS API, or the AWS Console Mobile Application.
	



	For this guide, we create an IAM User with assigned Access keys.
 


	
		Log on to your AWS console, enter IAM in the Search bar at the top of the page, and choose the IAM option.
	
	
		Click on Users on the Dashboard. 
		
	
	
		You will see the IAM Root User listed. Click on Create user to continue. 
		
	
	
		Enter the name of the IAM User and click Next to continue. 
		
	
	
		Choose Add user to group and select the previously created IAM User Group. 
		Click Next to review and create the IAM User. 
		
	
	
		As we need an IAM User with assigned Access Keys, we must create the needed keys.
	
	
		Open the just created IAM user and click on Create access key. 
		
	
	
		Choose Third-party service and click Next. 
		
	
	
		The Access Keys are now created. You can download them as a CSV file and store them in a safe place. 
		
	
	
		Click Done to complete this step. 
		 
	



	Deploying Security Groups on the Network Entities 



	A Security Group acts like a virtual firewall. 
	If you associate a Security Group with an EC2 instance, it controls the instance's inbound and outbound traffic.
 


	When you create a VPC, it comes with a default Security Group. 
	You can create additional Security Groups for a VPC, each with their own inbound and outbound rules:
 


	
		You can specify each inbound rule's source, port range, and protocol. 
	
	
		You can specify each outbound rule's destination, port range, and protocol.
	



	The following diagram shows a VPC with a Subnet, an Internet Gateway, and a Security Group. 
	The Subnet contains an EC2 instance. The Security Group is assigned to the instance and acts as a virtual firewall. The only traffic that reaches the instance is the traffic allowed by the Security Group rules. 
	
 


	
		Log on to your AWS console, enter VPC in the Search bar at the top of the page, and choose the Security option. Click on Security Groups on the Dashboard. 
		
	
	
		Create a new Security Group by clicking on Create security group. 
		
	
	
		Fill out all relevant fields and add the Inbound Rules by clicking Add rule on the Inbound Rules part. 
		For the Security Group bound to the Instances in the Public Subnet, use the following rules:
	



	Type: HTTP 
	Protocol: TCP 
	Port range: 80 
	Source: Custom 0.0.0.0/0
 


	Type: HTTPS 
	Protocol: TCP 
	Port range: 443 
	Source: Custom 0.0.0.0/0
 


	Type: RDP 
	Protocol: TCP 
	Port range: 3389 
	Source: MyIP – your current external IP should be entered automatically
 


	Create more Inbound Rules according to your needs. 
 


	After creating all the needed rules, click on Create security group to create it.
 


	
 


	 
 


	For the Security Group bound to the Instances in the Private Subnet, use the following rules:
 


	Type: HTTP 
	Protocol: TCP 
	Port range: 80 
	Source: Custom 0.0.0.0/0
 


	Type: HTTPS 
	Protocol: TCP 
	Port range: 443 
	Source: Custom 0.0.0.0/0
 


	Type: RDP 
	Protocol: TCP 
	Port range: 3389 
	Source: Custom 0.0.0.0/0
 


	Type: Custom TCP 
	Protocol: TCP 
	Port range: 1494 
	Source: Custom 0.0.0.0/0
 


	Type: Custom UDP 
	Protocol: UDP 
	Port range: 1494 
	Source: Custom 0.0.0.0/0
 


	Type: Custom TCP 
	Protocol: TCP 
	Port range: 2598 
	Source: Custom 0.0.0.0/0
 


	Type: Custom UDP 
	Protocol: UDP 
	Port range: 2598 
	Source: Custom 0.0.0.0/0
 


	Create more Inbound Rules according to your needs. 
 


	4. After creating all the needed rules, click on Create security group to create it. 
	
 


	You have successfully created all needed Security Groups. All necessary Network-related prerequisites have been created. 
	 
 


	Deploy all needed Instances in your VPC



	An Amazon EC2 Instance is a Virtual Machine in the AWS Cloud environment. Amazon EC2 provides a wide range of Instance types. You can choose an Instance type that provides the compute resources, memory, storage, and network configurations that meet your needs. 
 


	In this section, we will initially deploy the following Instances:
 


	
		(1) Jump Host for accessing the environment over the Internet using RDP
	
	
		(1) Domain Controller for providing all needed Active Directory functionalities
	
	
		(2) Cloud Connectors
	
	
		(1) Master Image from which the Machine Catalog(s) will be deployed
	



	Further Instances will be created automatically during the Machine Catalog creation.
 


	
		Important: 
	 

	
		You need an EC2 Key Pair to decrypt the administrator password to access a non-domain-joined Windows-based Instance. A Key Pair, consisting of a public key and a private key, is a set of security credentials you use to prove your identity. 
		 
		After joining the Instance to a Domain, you can log on using the Domain credentials. 
		The Key Pair is used only for local Admin access. 
		 
	 



	 
 


	Creating the EC2 Key Pair



	Before deploying Windows Instances, you must create a Key Pair to gain access to the Instance after it is created.
 


	
		Log on to your AWS console, enter EC2 in the Search bar at the top of the page, and choose the Network &amp; Security option. Click on Key Pairs on the Dashboard. If no Key Pair was already created, click Create key pair. 
		
	
	
		Choose the needed settings and click Create key pair. 
		
	



	
		Important: 
	 

	
		After successful creation, download and store the Key Pair in a safe place. 
		You will need it each time you access the Windows Instance with local administrator credentials. 
		 
	 



	 
 


	Deploying the Jump Host Instance



	For security reasons, our environment will be completely shielded and deployed in the already-created Private Subnet. 
	Administrative access to the Instances is only possible using a dedicated Jump Host Instance in the Public Subnet.
 


	
		Important: 
	 

	
		Choose the right Instance type that fits your needs. 
		Each type offers different compute, memory, and storage capabilities, which reflect different costs. An overview of all available Instance types can be found here. 
		 
		In this guide, we chose a t2.medium type for the Jump Host. 
		 
	 



	Select all Instance types according to your needs - you can find a detailed overview of all available Instance types on AWS product documentation: 
	 
	 
 


	
		Log on to your AWS console, enter EC2 in the Search bar at the top of the page, and choose the Instances option. Click on Launch instance on the Dashboard. 
		
	
	
		Choose all relevant settings for the new Instance, such as Name and Tags, AMI Type and AMI Image, Instance Type, Key Pair to log in, Security Group to which this Instance will be bound, and Storage configuration. 
		Set advanced details according to your needs.
	
	
		After choosing and setting all the needed information, click Launch instance to create the Instance. 
		 
		 
		
	
	
		After successful creation, the Jump Host Instance is listed on the Instances tab. 
		
	



	 
 


	Connecting to the Jump Host Instance Instance



	For security reasons, the Jump Host Instance is not Domain-joined. You need to log on to it using local administrator credentials.
 


	As mentioned in the Key Pair section, you need an EC2 Key Pair to decrypt the administrator password.
 


	
		To log on to the Instance, open the AWS console, enter EC2 in the Search bar at the top of the page, and choose the Instances option.
	
	
		Mark the Instance you want to connect to and click on Connect. 
		
	
	
		Choose RDP client as the connection method and click Get password. 
		
	
	
		Now upload the mentioned Key Pair using Upload private key file. Click Decrypt password after uploading to get the administrator credentials. 
		
	
	
		Now you have the decrypted login information and can log on to the Jump Host Instance using RDP. 
		
	



	Deploying the Domain Controller Instance



	We followed our standard deployment type for Domain Controllers in this environment. 
	The AD deployment consists of a Hub-and-Spoke model. Each Resource Location running on a Hypervisor/Hyperscaler is connected to the primary Domain Controller using IPSec-based Site-to-Site VPNs. Each Resource Location can have its subdomain if needed.
 


	
		Note: 
	 

	
		You can follow the steps mentioned above to deploy the Jump Host Instance. 
		 
	 



	After the successful creation of the Domain Controller Instance, you can access it by:
 


	
		Connect to the Jump Host Instance using RDP and log on using the Key Pair
	
	
		Connect to the Domain Controller Instance using RDP from the Jump Host Instance
	



	The Domain Controller and Replication Configuration is out of this guide's scope. 
	 
 


	Deploying and Configuring the Cloud Connector Instances



	At least two (2) Cloud Connectors are required to create a highly available connection between Citrix Cloud and your resource location. Depending on your environment and the workloads you support, you might need more Cloud Connectors to ensure the best user experience.
 


	As a best practice, Citrix recommends using the N+1 redundancy model when determining the number of Cloud Connectors you need to deploy. Determine the number of Cloud Connectors you need in a resource location based on your environment, workloads, Active Directory configuration, and services.
 


	
		Note: 
	 

	
		You can follow the steps mentioned above for deploying the Jump Host Instance, but we recommend choosing another Instance type, such as a t3.large. 
		 
	 



	After the successful creation of the Cloud Connector Instances, you can access them by:
 


	
		Connecting to the Jump Host Instance using RDP and log on using the Key Pair
	
	
		Connecting to the Cloud Connector Instances using RDP from the Jump Host Instance
	



	Before deploying the Cloud Connector software, you must add the Cloud Connector Instances to the Domain. This step is outside the scope of this guide.
 


	The Cloud Connector Instance is successfully registered in the Domain: 
	
 


	
		Important: 
	 

	
		To install the Cloud Connector software, you must log on to the Cloud Connector Instances with Domain-Admin credentials. 
		 
	 



	
		Important: 
	 

	
		Follow the next steps on each Cloud Connector Instance. 
		 
	 



	
		Log on to your Citrix Cloud Account. 
		
	
	
		Add a new Resource Location dedicated to your AWS deployment. 
		 
		 
		
	
	
		After creating the Resource Location, you must add the Cloud Connector Instances. 
		Click the + to add the current Instance and download the Cloud Connector software installer. 
		 
		 
		
	
	
		After successfully downloading it, start it using administrative rights. 
		Run the Advanced Connectivity Check. If the check completes, start the installation. 
		
	
	
		Sign in to Citrix Cloud and continue with the installation. 
		
	
	
		The installer asks in which Resource Location you want to put this Cloud Connector Instance. Choose the AWS-related Resource Location you just created. 
		
	
	
		The installation process takes several minutes, and after completion, the installer runs a thorough communication check. 
		 
		 
		 
		 
		
	
	
		After the installation completes, the Cloud Connector Instances can be seen registered in the adjacent Resource Location. 
		
	



	 
 


	Deploying and Configuring the Master Image Instance



	Image Management is an approach of creating a Master or Golden Image that contains the operating systems and all the required applications to deliver that single virtual image to multiple target virtual machines. 
	The key concepts behind this are reusability and simplified management, which allow the Citrix administrator to deliver the operating systems with the required set of applications to appropriate users based on their needs.
 


	This guide uses Citrix Machine Creation Services (MCS) for Image Management. MCS connects Using Hosting Connections. In conjunction with the underlying hypervisor or cloud provider, MCS builds intelligent linked clones from a Master Image to provide multiple virtual desktops. The clones include a differencing disk and an identity disk linked from a base disk.
 


	MCS configures, starts, stops, and deletes virtual machines using the Hosting Connections. 
 


	Machine Creation Services is a disk-based provisioning approach that works with major hypervisors and leading cloud platforms.
 


	The Master Image Instance is a domain-joined Instance that requires all the software and applications mentioned above to deploy the Master Image Instance, but we recommend choosing another instance type, such as a t3.large installed, before deploying target machines.
 


	 
 


	
		Note: 
	 

	
		You can follow the same steps mentioned above for deploying the Master Image Instance, but we recommend choosing another Instance type, such as a t3.large.
	 

	
		After deploying the Master Image Instance, add it to the Domain before proceeding. 
		 
	 



	Depending on the Master Image Instance’s operating system, you must download the suitable Virtual Desktop Agent (VDA) from the Citrix Homepage or install it from the Citrix Virtual Apps and Desktops-ISO file. If you use a Desktop OS, you must install the Workstation-type VDA. If you use a Server OS, you must install the Server-type VDA. 
	 
 


	
		Important: 
	 

	
		To install the VDA, log on to the Master Image Instance with Domain-Admin credentials. 
		 
	 



	
		After downloading/mounting the installer, start the installation.  During the installation, the installer asks for configuration information – select the needed parts/configurations according to your needs. 
		
	
	
		As this installation runs on the Master Image Instance, select Create a Master MCS image and click Next. 
		
	
	
		Select all additional components to be installed according to your needs, and click Next. 
		
	
	
		During the creation of the Worker VMs, MCS will automatically register the Worker VMs on the Cloud Connector Instances, so select Let Machine Creation Services do it automatically. 
		
	
	
		As the Worker VMs are deployed on AWS, select "Is this VDA installed on a VM in the Cloud?" and click Next. 
		
	
	
		Select further components according to your needs. Click Next.
	
	
		Now, the Installer takes a while to install the VDA. After completion, do not forget to run Citrix Optimizer – the Installer provides the link to more information.
	
	
		Download Citrix Optimizer to the Master Image Instance. Click Finish.
	
	
		Before running Citrix Optimizer, install the required software and components for the Worker VMs. In this example, the Worker VMs will provide LibreOffice as the primary Office suite for the users. 
		
	
	
		After installation of all software that is needed further, run the Citrix Optimizer tool. 
		
	
	
		Choose the correct Operating System to check all supported optimizations. Citrix Optimizer presents you with various optimizations – select/unselect these according to your needs and click on Optimize to start the process: 
		 
		 
		
	
	
		Optimization is complete. 
		
	



	
		Note: 
	 

	
		The Citrix Optimizer Tool is a Windows tool that helps Citrix administrators optimize various components in their environment, most notably operating systems with the Virtual Delivery Agent (VDA) installed. 
		 
	 



	 
 


	Creating an Amazon Machine Image (AMI) based on the Master Image Instance



	An Amazon Machine Image (AMI) is an image that provides the software that is required to set up and boot an Amazon EC2 instance. Each AMI also contains a block device mapping specifying the block devices to attach to the instances you launch. The AMI must be compatible with the instance type that you chose for your instance. 
 


	You can create an AMI from your Amazon EC2 instances and then use it to launch instances with the same configuration. You can copy an AMI to another AWS Region and then use it to launch instances in that Region.
 


	
		The first step in creating the AMI is to set the Amazon EC2Launch settings according to your needs. 
		
	
	
		Choose the settings according to your needs, but click Shutdown without Sysprep. 
		
	
	
		Ensure the Master Image Instance is in a Stopped state before continuing.
	
	
		Select the Master Image Instance in the dashboard. In the Actions pane, select Images and templates and click on Create image to convert the Master Image Instance into an AMI. 
		
	
	
		Set the AMI's configuration according to your needs, then click on Create image to start the creation process. 
		
	
	
		After successful completion, the new AMI is shown and can be used as the reference image for the Worker VMs. The Machine Creation Services will look for such AMIs to use. 
		
	



	
		Important: 
	 

	
		Start Amazon EC2Launch settings using Administrative Credentials. 
		 
	 



	All required prerequisites for deploying Citrix DaaS are completed. 
	 
 


	Configure Citrix DaaS



	The successful configuration of Citrix DaaS relies on many entities. You need to configure at least the following entities in the correct order to get a working DaaS environment:
 


	
		A Host Connection and a Host Connection Resource Pool connect DaaS to a host (hypervisor or cloud service) in a Resource Location. Creating Host Connections and Host Connection Resource Pools is required to create and manage Worker VMs on Hosts or to power manage existing Worker VMs.
	
	
		A Machine Catalog is a collection of identical Worker VMs, which can be virtual or physical, depending on your needs. 
	
	
		A Delivery Group: This contains machines from Machine Catalogs.  
		It also specifies which users can use those Worker VMs and which Applications and Desktops are available to those users.
	
	
		Citrix Policies: Citrix Policies are a collection of settings that define how sessions, bandwidth, and security are managed for a group of users, devices, or connection types.
	



	
		Important: 
	 

	
		You must have the correct privileges on the Hypervisor/HyperScaler and the DaaS Control Plane. 
		 
	 



	 
 


	Creating a Host Connection and a Host Connection Resource Pool



	Configuring a Host Connection and a Host Connection Resource Pool involves selecting the connection type from the list of supported hypervisors and cloud services and choosing the appropriate storage and network resources.
 


	
		Log on to Citrix Cloud WebStudio and open My Services -&gt; DaaS. 
		
	
	
		Choose Hosting to configure a Hosting Connection and a Hosting Connection Resource Pool. Click on Add Connection and Resources. 
		
	
	
		Select Create a new Connection.
	
	
		Choose the correct Zone – it should have the same name as the Resource Location you created earlier in this guide.
	
	
		Choose the correct Connection type – Amazon EC2.
	
	
		Enter the EC2 API key and the EC2 API secret you created earlier in this guide.
	
	
		Enter a meaningful name and click Next to proceed. 
		
	
	
		Choose the correct AWS Cloud Region. Click Next. 
		
	
	
		Look at the EC2 Dashboard in which Region and Availability Zone the Instances are deployed. 
		
	
	
		Select the correct Availability Zone. Click Next. 
		
	
	
		Select the correct Subnet. Click Next. 
		
	
	
		Review your settings on the Summary page. Click Next. 
		
	
	
		The Host Connection and the Host Connection Resource Pool should now be deployed. 
		It will take a while until the newly created entities are shown in the Hosting Dashboard. 
		
	
	
		To ensure both entities work fine, click More and choose Test the connection. 
		Afterward, Test Resources will be used to run some tests. 
		 
		 
		
	



	Both entities were successfully deployed and worked as intended. 
	 
 


	Creating a Machine Catalog



	Collections of physical or virtual machines are managed as a single entity called a Machine Catalog. Within a Machine Catalog, all machines share a common operating system type, which can be either multi-session or single-session OS, such as Windows or Linux-based systems.
 


	
		Note: 
	 

	
		The wizard needs plenty of information to deploy the Machine Catalog. Choose/Enter the correct settings according to your needs. The screenshots shown are only examples. 
		 
	 



	
		Log on to Citrix Cloud WebStudio and open My Services -&gt; DaaS. Choose Machine Catalogs to create a Machine Catalog. Click on Create a Machine Catalog. 
		
	
	
		Click Next. 
		
	
	
		Choose the correct Machine Type based on your Master Image Instance.  Click Next. 
		
	
	
		Choose Machines that are power managed and choose MCS as provisioning technology. 
		Select the correct Host Connection Resource Pool. Click Next. 
		
	
	
		Choose the Desktop Experience according to your needs. Click Next. 
		
	
	
		Choose the correct Image Type – in this case, we select Master Image. 
		You can find more information about Prepared Image in our Citrix Tech Zone guide Image Management - A new way of deploying Machine Catalogs and reducing Master Image Complexity. 
		
	
	
		Select a Machine Template enables you to select the Master Image – Select the AMI you created earlier. Click Done. 
		
	
	
		If suitable, choose the Master Image Instance as a Machine Profile. A Machine Profile enables you to build all Worker Machines using the same hardware configuration. Click Done. 
		
	
	
		Choose the minimum functional level of the VDA – using the latest level supported by the VDA is recommended. 
		Depending on your needs, apply Machine Tags. Click Next. 
		
	
	
		Choose the Number of VMs to create and select the Machine Specification. Click Next. 
		
	
	
		Choose the correct AWS Security Group and how the VMs should be deployed. Click Next. 
		
	
	
		Choose the correct AWS Subnet. Click Next. 
		
	
	
		Fill out all needed Active Directory-related settings according to your needs. Click Next. 
		
	
	
		Enter administrative credentials for Active Directory-related operations according to your needs. Click Done. 
		
	
	
		Click Next to proceed until reaching the Summary page. 
		
	
	
		Review the Summary and start the creation process. 
		
	
	
		Creating the Machine Catalog can take a long while depending on various parameters. You can see the progress in the pop-up window.
	
	
		After successful creation, the newly created Machine Catalog is visible on the Dashboard. 
		
	
	
		Selecting the Machine Catalog and clicking on More enables you to get more details – e.g., for the VMs in the catalog, etc. 
		
	
	
		You should test all functions of the Machine Catalog by choosing Test Machine Catalog in the More pane. 
		
	



	A new Machine Catalog and new Worker VMs were successfully deployed and work as intended. 
	 
 


	Creating a Delivery Group



	A Delivery Group is a collection of machines selected from one or more machine catalogs. 
	The Delivery Group specifies which users can use those machines and the available applications and desktops.
 


	
		Note: 
	 

	
		The wizard needs plenty of information to deploy the Delivery Group. Choose/Enter the correct settings according to your needs. The screenshots shown are only examples. 
		 
	 



	
		Log on to Citrix Cloud WebStudio and open My Services -&gt; DaaS. Choose Delivery Groups to create a Delivery Group. Click on Create Delivery Group. 
		
	
	
		Click Next. 
		
	
	
		You must choose how many unassigned VMs from which Machine Catalog should be assigned to this Delivery Group. Click Next. 
		
	
	
		Choose further settings according to your needs in the next pop-up windows and click Next to proceed. 
		 
		 
		
	
	
		You now can select if and which Applications can be published – click on Add if you want to publish Applications. 
		 
		 
		
	
	
		You can review your selection and add more Applications. Click OK. 
		
	
	
		You can also publish the entire Desktop of the Master Image Instance if needed. Click on Add if you want to publish the Desktop. 
		 
		 
		
	
	
		Enter all needed information for publishing according to your needs. Click Add to add AD users or groups. 
		
	
	
		Click Done.
	
	
		In the next step, you can select more restrictive Security Features. Select them according to your needs, then click Next. 
		
	
	
		If you want to assign Scopes to this Delivery Group, choose the Scope according to your needs. Click Next. 
		
	
	
		Choose the correct License Assignment. Click Next. 
		
	
	
		Review all settings in the Summary pop-up. Add a comprehensive name and description for this Delivery Group. Click Next. 
		
	
	
		The Delivery Group is created. After successful creation, you can find the newly created Delivery Group in the Dashboard. Click on the tabs shown below the Dashboard to get more information. 
		 
		
	
	
		Click on More to run the recommended tests. 
		
	



	You successfully deployed a Delivery Group, which works as intended.
 


	That completes the mandatory steps for deploying Citrix DaaS on Amazon EC2. 
	 
 


	
		Note: 
	 

	
		All further steps mentioned in this guide are optional but recommended. 
		 
	 



	 
 


	Setting AutoScale for the Delivery Group



	AutoScale provides a consistent, high-performance solution to power manage your machines proactively. It aims to balance costs and user experience. AutoScale incorporates the deprecated Smart Scale technology into Studio’s power management solution. 
	AutoScale enables proactive Power Management of all registered single-session and multi-session OS machines in a Delivery Group.
 


	
		Important: 
	 

	
		To reduce cost, it is highly recommended that AutoScale be configured for all Delivery Groups containing Worker VMs running on HyperScalers. 
		 
	 



	
		Log on to Citrix Cloud WebStudio and open My Services -&gt; DaaS. ChooseDelivery Groups. Select the Delivery Group you want to configure and click Manage AutoScale. Click on the General tab for initial configuration.
	
	
		Click on Enable AutoScale to enable it. Set all further configurations according to your needs. 
		 
		 
	
	
		By adjusting the Schedule(s) and Peak Times, you can configure when and how many Worker VMs will be pre-started and kept running. 
		Adjust the settings according to your needs. 
		
	
	
		Defining the Load-based Settings ensures that spare Worker VMs are pre-started and kept running idle so that users can immediately start a session without waiting until a new Worker VM needs to be started. Adjust the settings according to your needs. 
		
	
	
		Remember to configure the User Logoff Notifications. AutoScale can start Worker VMs automatically and shut them down if necessary to reduce cost. 
		Adjust the settings according to your needs. 
		
	



	 
 


	Test User Access to this Environment



	
		It is vital to test User connectivity before enrolling Users. Log on to your environment using Citrix Workspace App using User credentials assigned to this environment.
	
	
		Check if all assigned Applications and Desktops are available, and launch your desktop.
	



	
 


	 
 


	Summary



	Deploying Citrix DaaS on Amazon EC2 is straightforward. However, all prerequisites must be fulfilled on both the Amazon EC2 and Citrix DaaS sides.
 


	We have shown examples of creating all the needed entities and configurations to deploy a working Citrix DaaS environment running on Amazon EC2.
 


	For further DaaS configurations, such as creating Policies, creating Delegated Administration, enabling Configuration Logging, etc., please consult the corresponding parts of the Product documentation.]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_10/DaaSAWSWorkspacesCore.png.e9dab3542368a32d84b5d7c2577cbb8c.png" length="120657" type="image/png"/><pubDate>Tue, 15 Oct 2024 11:54:17 +0000</pubDate></item><item><title>Citrix DaaS for AWS WorkSpaces Core</title><link>https://community.stage.citrix.com/tech-zone/learn/tech-briefs/citrix-for-aws-workspaces-core/</link><description>Overview



	AWS WorkSpaces Core is a managed virtual desktop infrastructure designed to work with third-party VDI solutions such as Citrix DaaS. It is the compute layer of AWS workloads that the Citrix DaaS control plane can help orchestrate and manage to deliver HDX-optimized apps anywhere. AWS WorkSpaces Core and Citrix improve cost savings, simplify cloud management, and provide a superior user experience.
 


	Citrix DaaS treats AWS WorkSpaces Core as another Resource Location option to leverage within your deployment. It helps your IT teams manage and provision WorkSpace Core desktops directly from the Citrix platform, reducing application delivery complexity and simplifying operations. With security features like Zero Trust Network Access (ZTNA), contextual access policies, and secure browser redirection, Citrix protects your business from cyber threats and data leaks.
 


	Citrix improves the WorkSpaces Core experience with advanced HDX graphics, Unified Communication optimizations, USB redirection, and support for 3D workloads, ensuring smooth performance on any device or network. With centralized management, automated patching, and streamlined updates, Citrix optimizes your budget by reducing IT costs and complexity.
 


	Why Citrix DaaS for AWS WorkSpaces Core?



	Citrix DaaS for AWS WorkSpaces Core combines the best of everything, merged with Citrix, Amazon, and Microsoft, to build the most cloud-friendly virtual desktop solution. With Citrix&#x2019;s proven technologies, enterprises can improve scalability, reduce costs, maintain a strong security posture, and enhance user productivity.
 


	Improve Scalability
 


	Use your existing Citrix management software to provide users with the highly scalable AWS WorkSpaces Core solution without dealing with the long lead times associated with procuring new on-premises infrastructure. Manage the entire solution with your existing Citrix DaaS entitlement.
 


	Reduce Costs
 


	AWS WorkSpaces Core offers flexible billing options and pay-as-you-go pricing, allowing you to only pay for resources used. Eliminating the need for capacity planning frees internal resources to focus on higher ROI initiatives. Citrix DaaS additionally helps reduce costs with its Autoscale feature by automatically scaling resources up and down with demand and without over-provisioning infrastructure.
 


	Maintain a Strong Security Posture
 


	Running Citrix DaaS on WorkSpaces Core bolsters security by leveraging highly secure, fully managed AWS Cloud infrastructure. Additionally, you can secure your enterprise with Citrix Secure Private Access, which integrates single sign-on, remote access, and content inspection into a single solution for end-to-end access control. Network and end-user devices can be protected from malware and data leaks, and access security policies for secure access to SaaS and Web applications can be enforced. Additionally, printing, downloads, and clipboard access can be restricted.
 


	Enhance User Productivity
 


	Citrix DaaS and AWS WorkSpaces Core allow resources to be placed in close geographic proximity to end users. At the same time, data is transmitted through the high-speed AWS backbone, minimizing latency and optimizing end-user experience. User productivity is further enhanced with the globally deployed Citrix Gateway service, which provides highly available, secure remote access; end users are optimally routed to the closest Gateway service POP location.
 


	Citrix DaaS for AWS WorkSpaces Core Architecture



	
 


	Citrix DaaS interacts directly with AWS WorkSpaces Core via APIs. In appearance, AWS WorkSpaces Core is another resource location for Citrix DaaS, but communication happens at the API level between the services.
 


	
		Citrix Workspace app provides users with secure, self-service access to documents, applications, and desktops from any device, including smartphones, tablets, and PCs. It also provides on-demand access to Windows, web, and Software as a Service (SaaS) applications. For devices that cannot install Citrix Workspace app software, the Citrix Workspace app for HTML5 provides a connection through an HTML5-compatible web browser.
	
	
		Citrix Workspace is a service that provides secure access to your desktop from a web browser or Citrix Workspace app.
	
	
		Citrix Gateway service enables secure, remote access to desktops without deploying Citrix Gateway in the DMZ or reconfiguring your firewall. The infrastructure overhead of using Citrix Gateway moves to Citrix Cloud.
	
	
		Citrix DaaS is a cloud-based service managed by Citrix that provides application and desktop virtualization. Citrix manages the user access and management services and components in Citrix Cloud. The applications and desktops you deliver to users reside on machines in one or more resource locations. In a Citrix DaaS deployment, a resource location contains components from the access layer and resource layers.
	
	
		Cloud Monitor enables IT support and help desk teams to monitor an environment, troubleshoot issues before they become critical, and perform support tasks for end user
	
	
		Citrix Cloud Studio is a web-based, central portal that lets administrators configure, manage, and monitor their DaaS deployments to deliver virtual apps and desktops. 
	
	
		Cloud Connectors are the communications channel between the Citrix Cloud and resource location components. They are proxies for the Delivery Controller in Citrix Cloud in the resource location. Once the Cloud Connectors are installed, the software is managed and updated. Enterprises are responsible for Windows updates and patching.
	
	
		Microsoft Active Directory is used for authentication and authorization. It authenticates users and ensures that they are getting access to appropriate resources. A subscriber&#x2019;s identity defines the services to which they have access in Citrix Cloud. This identity comes from Active Directory domain accounts provided from the domains within the resource location.
	
	
		Citrix Virtual Delivery Agents (VDA) are virtual machines hosted in AWS WorkSpaces Core that must have a Citrix VDA installed. VDAs establish and manage the connection between the machine on which they&#x2019;re installed and the user device and apply policies configured for the session.
	
	
		AWS AD Connector provides a gateway within AWS WorkSpaces Core to your on-premises Microsoft Active Directory without needing to cache any information in the cloud.
	
	
		HDX is the Citrix proprietary high-definition HDX technology that delivers high-quality voice, video, multimedia, and 3D graphics applications even over low bandwidth. HDX technology makes cloud-hosted virtual applications and desktops perform as if installed locally on the user&#x2019;s device for seamless experiences.
	



	Citrix DaaS for AWS WorkSpaces Core Onboarding



	Information on the Citrix DaaS for AWS WorkSpaces Core prerequisites and supported configuration can be found here.
 


	
 


	To configure Citrix DaaS for AWS WorkSpaces Core, follow these steps in order:
 


	
		Create a resource location
	
	
		Connect your AWS account
	
	
		Create a directory connection
	
	
		Import an image
	
	
		Create a deployment
	



	After completing the configuration, end users can access their Citrix-enhanced AWS WorkSpaces Core desktop through the Citrix Workspace app alongside other Citrix-provided content.
 


	References



	Citrix DaaS for Amazon WorkSpaces Core</description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_10/image.png.a4065d778e31370614fe2e17b7b19ce4.png" length="120667" type="image/png"/><pubDate>Wed, 09 Oct 2024 19:40:00 +0000</pubDate></item><item><title>PoC Guide: Citrix for Windows 365</title><link>https://community.stage.citrix.com/tech-zone/learn/poc-guides/citrix-for-windows-365/</link><description><![CDATA[Overview



	Microsoft Windows 365 (Cloud PC) is an Azure-hosted service providing dedicated Windows virtual machines.  
 


	Citrix for Windows 365 extends the Cloud PC experience with high-end graphics technology, integration with third-party solutions, support for thousands of devices and peripherals, and added layers of security, benefiting IT admins and users alike.
 


	This Proof-of-Concept Guide provides the steps to integrate Windows 365 Cloud PCs with Citrix DaaS (Desktop as a Service). The following is covered in this guide:
 


	
		Enable the Citrix connector for Windows 365
	
	
		Connect Microsoft Entra ID to Citrix Cloud
	
	
		Connect Windows 365 to Citrix Cloud
	
	
		Assign Citrix licenses
	
	
		Provision Cloud PCs
	
	
		Enable Citrix VDA Upgrade service
	



	Prerequisites



	
		Citrix Cloud Account: You'll need an active Citrix Cloud account with the necessary licenses.
	
	
		Windows 365 Enterprise Subscription: Ensure you have a Windows 365 Enterprise subscription to access Cloud PCs.
	
	
		Microsoft Intune entitlement: You need an active Microsoft Intune environment in your Entra ID directory.
	
	
		Azure Administrator accounts: You will require an Entra ID global administrator and Intune Global administrator privileges or someone with access to these accounts.
	



	Enable Citrix Connector for Windows 365:



	
		Open your Intune administrator portal and log in with an account with Global Administrator privileges.
	
	
		Select Tenant administration.
	



	
 


	
		Select Connectors and tokens.
	



	
 


	
		Select Windows 365 partner connectors.
	



	
 


	
		Click Add.
	
	
		Select Citrix from the list. Set the toggle to On for "Allow people to use Citrix to connect to their Cloud PCs." Click Add.
	



	
 


	
		The Citrix Connector is now enabled.
	



	
 


	Connect Entra ID to Citrix Cloud



	
		Important:
	 

	
		If your Citrix Cloud tenant is already connected to Entra ID, you can skip this section.
	 



	
		Open the Citrix DaaS web console.
	
	
		Select Quick Deploy.
	



	
 


	
		Select Microsoft Windows 365.
	



	
 


	
		Click Connect.
	



	
 


	
		Citrix Cloud Identity and Access Management will open in a new tab.
	



	
 


	
		Click Add an identity provider.
	



	 
 


	
		Choose Azure Active Directory, provide an Identity provider name, and click Add.
	



	
 


	
		Select the three dots next to Azure Active directory and select Connect.
	



	
 


	
		Confirm your custom administrator sign-in URL if prompted.
	



	
 


	
		Log into Entra ID with a Global Administrator account.
	



	
 


	
		Review the requested permissions. Select Accept.
	



	
 


	 
 


	 
 


	Citrix Cloud is now connected to your Entra ID tenant. Return to the previous and continue with the next step.
 


	Configure Citrix Workspace:



	
		Important:
	 

	
		You can skip the next step if your Citrix Workspace configuration is already configured.
	 



	
		Select Review.
	



	
 


	
		Workspace Configuration will open in a new tab.
	



	
 


	
		Note the Workspace URL, which will be used to access the Cloud PCs. Select Edit to customize your URL.
	



	
 


	
		Enter the subdomain, review and accept the considerations, and click Save.
	



	
 


	
		Select Authentication.
	



	
 


	
		Choose the authentication option for your POC. Additional details on supported authentication methods are available here.
	



	
 


	
		Activating Azure Active Directory for subscriber access will impact the subscriber and administrative experience. Select ‘I understand the impact on the subscriber experience,’ Select Confirm
	



	
 


	
		[Optional) Configure Authentication with the Federated Authentication Service (FAS). 
	



	
 


	
		Select Customize.
	



	
 


	
		Select Preferences.
	



	
 


	 
 


	
		Scroll to Automatically Launch Desktop, set it to Enabled, and click Save.
	



	
 


	 
 


	
		[Optional) Disable the Federated Identity Provider Sessions option in Workspace if:

		
			
				Your users’ client devices are Entra joined, or Entra hybrid joined, and you want a single sign-on into Citrix Workspace (the IdP must be Entra ID).
			
			
				Your users will use the Windows 365 web portal instead of accessing Citrix Workspace directly, and you want single sign-on into Workspace to avoid prompting users for credentials a second time. Note that this is only relevant if your Azure and Workspace IdPs are the same.
			
		
	



	
 


	 
 


	
		Close the Workspace Configuration tab and return to the Windows 365 Get Started checklist.
	



	Connect Windows 365 to Citrix Cloud



	
		Select Connect in step 3.
	



	
 


	
		Login with an Azure account with Global Administrator privileges. 
	
	
		Review Permissions requested. Select Accept.
	



	
 


	
		Return to the Windows 365 Get Started checklist.
	



	Assign Citrix Licenses to Users



	
		Click Assign in step 4, Assign Citrix licenses.
	



	
 


	
		Click Assign licenses.
	



	
 


	
		Select Groups, Users.
	



	
 


	
		Search for the Groups or users to assign a Citrix license to and click Search.
	



	
 


	
		Select the Groups or Users and click Save.
	



	
 


	
		Validate your license assignments.
	



	
 


	 
 


	
		Note:
	 

	
		If the users selected already have Cloud PCs assigned, the Citrix Virtual Delivery Agent will be installed automatically and switch the user access to Citrix. If so, you can skip to the Access Your Cloud PC section.
	 



	Provision Cloud PCs



	
		Log into the Microsoft Intune admin portal with an account with global administrator privileges.
	
	
		Select Devices &gt; Windows 365 &gt; Provisioning policies &gt; Create policy.
	



	
 


	
		Enter a Policy name
	



	
 


	
		Select your License type. 
	



	
 


	
		For our deployment, we select Microsoft Entra Join and Azure network connection for the network. The Azure network connection Azure East US was selected.
	



	
 


	
		Check the Use Microsoft Entra single sign-on box and click Next.
	



	
 


	
		Select your image type and click Next.
	



	
 


	
		Select any additional configurations required for your Cloud PC and click Next.
	



	
 


	
		Configure scope tags for the provisioning policy and click Next.
	



	
 


	
		Select your User Groups to assign this policy and click Next.
	



	
 


	
		Review your policy options, then click Create.
	



	
 


	
		Your Cloud PCs will now be provisioned.
	



	
 


	Review Cloud PC Citrix Policy



	By default, the Cloud PCs are configured in a connector-less mode. A Citrix Policy is automatically created for these machines, which enables the Rendezvous Protocol. You can review and update this policy as required.
 


	
		Click Policies.
	



	
 


	
		Select CTX-Windows365-eastus policy and click Edit Policy.
	



	
 


	
		Select View selected only.
	



	
 


	
		The Rendezvous Protocol policy is Allowed.
	



	
 


	
		Note:
	 

	
		Additional settings can be added to this policy per your requirements and best practices.
	 



	 
 


	
		Click Assign Policy To. The policy is assigned to the Cloud PC Delivery Group.
	



	
 


	
		Click Save.
	



	Enable Citrix VDA Upgrade service



	The Citrix VDA Upgrade service is a cloud-based feature of Citrix DaaS. With it, administrators can improve operational efficiency by shifting VDA updates from manual or complex processes to automated processes that Citrix manages.
 


	
		Select Machine Catalogs, right-click on your Cloud PC catalog, and select Edit Machine Catalog.
	



	
 


	
		Select VDA Upgrade Service.
	



	
 


	
		Check Use VDA Upgrade Service, select Latest CR VDA, and click Save.
	



	
 


	The VDA Upgrade service is now enabled for your Cloud PCs.
 


	Access Your Cloud PC



	
		Open a browser window and access your Citrix Workspace URL.
	
	
		Use your Entra ID credentials to log in.
	
	
		Your Cloud PCs are displayed and will begin to launch if you enabled Desktop Auto Launch earlier.
	



	



	Summary



	Following this proof-of-concept guide, you successfully integrated Windows 365 with Citrix DaaS, unlocking a superior desktop virtualization experience. This integration empowers your workforce with secure, high-performance access to their Cloud PCs from anywhere while simplifying IT management and reducing costs.]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_10/image.png.85934a661145e01eedde554ec03aff5c.png" length="188706" type="image/png"/><pubDate>Wed, 09 Oct 2024 11:51:00 +0000</pubDate></item><item><title>Tech Brief: Citrix for Windows 365</title><link>https://community.stage.citrix.com/tech-zone/learn/tech-briefs/citrix-for-windows-365/</link><description>Overview



	Citrix for Windows 365 integrates Citrix Cloud with Windows 365 Cloud PCs to deliver a high-performance, secure, and easy-to-manage solution. This combination enables organizations to provide a seamless hybrid work experience, supporting diverse use cases beyond standard Windows 365 capabilities.
 


	Citrix helps IT teams manage and provision Cloud PCs directly from the Citrix platform, reducing application delivery complexity and simplifying operations. With security features like Zero Trust Network Access (ZTNA), contextual access policies, and secure browser redirection, Citrix protects your business from cyber threats and data leaks.
 


	Citrix improves the Windows 365 Cloud PC experience with adaptive HDX graphics, Microsoft Teams and Unified Communications optimizations, USB redirection, and support for 3D workloads, ensuring smooth performance on any device or network. 
 


	With Citrix, customers can support a wide range of hybrid work scenarios, such as remote and BYOD employees, high-turnover environments like call centers, and scaling up for seasonal demand. Citrix provides flexibility to run Cloud PCs and on-prem workloads side-by-side, avoiding vendor lock-in and offering seamless integration with existing infrastructure.
 


	 
 


	Why Citrix for Windows 365?



	Citrix extends Windows 365's value by delivering a solution that protects your business, integrates your ecosystem, optimizes your budget, and improves user satisfaction. With Citrix&#x2019;s proven technologies, organizations get a secure, efficient, and flexible environment to support their hybrid work strategy.
 


	Satisfied Users on Any Network
 


	Citrix HDX delivers an enhanced experience for Windows 365 Cloud PCs across any network or device. Telephony and unified communications optimizations, multimedia enhancements, and adaptive technologies ensure smooth performance, even in low-bandwidth or high-latency environments.
 


	Protect Your Business
 


	Citrix strengthens security for Windows 365 Cloud PCs with granular policy controls, giving admins control over printers, USB devices, and clipboards, protecting data from leaks or misuse. Features like Session Recording and Adaptive Access policies help keep your environment compliant and secure.
 


	Integrate Your Ecosystem
 


	Citrix supports a wide range of devices and peripherals, ensuring a consistent user experience on any endpoint. Whether it's advanced multi-monitor setups or specialty peripherals, Citrix integrates your ecosystem seamlessly.
 


	Simplify Authentication
 


	Citrix integrates with third-party identity providers, enabling secure Single Sign-On, Multi-factor Authentication, and SAML 2.0 support, so you can use your existing identity solutions without disruption.
 


	With Citrix for Windows 365, you can protect your business, integrate your ecosystem, optimize your budget, and improve user satisfaction&#x2014;all with a solution designed to make your hybrid work environment more secure, efficient, and adaptable.
 


	 
 


	Citrix for Windows 365 Architecture



	
 


	
		Citrix Cloud Monitor is a web-based, central portal for configuring, managing, and monitoring your DaaS deployments for delivering virtual apps and desktops.
	
	
		Citrix Workspace app provides users with secure, self-service access to documents, applications, and desktops from any device, including smartphones, tablets, and PCs. It also provides on-demand access to Windows, web, and Software as a Service (SaaS) applications. For devices that cannot install Citrix Workspace app software, the Citrix Workspace app for HTML5 provides a connection through an HTML5-compatible web browser.
	
	
		Citrix Workspace aggregates and integrates Citrix Cloud services, enabling unified access to all the resources available to your end-users either through the browser with the Workspace URL or through the Citrix Workspace app
	
	
		Citrix Gateway Service is part of Citrix Cloud Services and provides secure remote access. It is a globally distributed multitenant service. End users use the nearest Point-of-Presence (PoP), regardless of the location of the Citrix Cloud Control or the location of the applications being accessed.
	
	
		Citrix Virtual Delivery Agent (VDA) enables the Windows 365 Cloud PC to register with the Citrix control plane, allowing the machine and its hosting resources to be available to users. VDAs establish and manage the connection between the machine and the user device. VDAs also verify that a Citrix license is available for the user or session and apply policies configured for the session.
	
	
		HDX - Citrix has proprietary high-definition HDX technology that delivers high-quality voice, video, multimedia, and 3D graphics applications even over low bandwidth. HDX technology makes cloud-hosted virtual applications and desktops perform as if installed locally on the user&#x2019;s device for seamless experiences.
	



	 
 


	Citrix for Windows 365 Onboarding



	
 


	Once Citrix licenses are assigned to users, Citrix communicates to the Windows 365 service that the selected users are entitled to use Citrix to access their Cloud PCs. If the selected users already have Cloud PCs provisioned, Windows 365 automatically installs the Citrix Virtual Delivery Agent (VDA) on those Cloud PCs and switches users' access to Citrix. If the selected users do not have Cloud PCs assigned, the VDA is installed immediately after the Cloud PC is provisioned at the time of Windows 365 license assignment.
 


	After installing the VDA, it registers with Citrix Cloud, and any necessary Machine Catalogs and Delivery Groups are created automatically. Cloud PCs are then available through Citrix Workspace. A Citrix policy is also created for each Windows 365 delivery group to enable the required features.
 


	 
 


	Get started with Citrix for Windows 365



	Information on the Citrix for Windows 365 prerequisites and supported configuration can be found here.
 


	
 


	 
 


	To configure Citrix for Windows 365, complete the following steps in order:
 


	
		Enable the Citrix Connector for Windows 365
	
	
		Connect Entra ID to Citrix Cloud
	
	
		Configure Citrix Workspace
	
	
		Connect Windows 365 to Citrix Cloud
	
	
		Assign Citrix Licenses to Users
	
	
		Provision Cloud PCs
	



	After completing this initial configuration and granting access to Windows 365, admins choose users to receive their new enhanced Cloud PC. Existing Citrix administrators can manage Windows 365 Cloud PCs alongside their other Citrix apps and desktops, consistently applying access policies and security controls across their enterprise.
 


	End users can access their Citrix-enhanced Cloud PCs through Intune, windows365.microsoft.com, or the Citrix Workspace app alongside other Citrix-provided content.</description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_10/image.png.bc6b4042fac4a2c3541ce099b019566a.png" length="87062" type="image/png"/><pubDate>Tue, 08 Oct 2024 12:08:00 +0000</pubDate></item><item><title>Citrix Automation Resources</title><link>https://community.stage.citrix.com/tech-zone/automation/resources/</link><description>Citrix DaaSCitrix DaaS Remote PowerShell SDK - The Remote PowerShell SDK automates complex and repetitive tasks and provides the mechanism to set up and manage the Citrix DaaS environment without Studio. Citrix DaaS APIs&#x2014;Citrix DaaS APIs allow you to automate resource management within a Citrix Virtual Apps and Desktops deployment. Citrix Power Management of Azure Virtual Machines - REST API - Learn how to power manage Azure Virtual Machines with Citrix DaaS and REST APIs. Citrix Power Management of Azure Virtual Machines - PowerShell - Learn how to power manage Azure Virtual Machines with Citrix DaaS and PowerShell. Citrix Connector Appliance Local APIs - Use the Connector Appliance APIs to configure and manage your Connector Appliances. GitHub Plugin for Citrix Terraform Provider- Using Terraform with Citrix provider, you can manage your Citrix products via Infrastructure as Code, giving you higher efficiency and consistency on infrastructure management and better reusability on infrastructure configuration. Citrix develops and maintains the provider. Citrix DaaS and Terraform - Installing Terraform and Configuring the Citrix Terraform Provider - Learn how to install Terraform and Configuring the Citrix Terraform Provider Installing and Configuring Ansible, Terraform, and Packer for Citrix Infrastructure-as-Code-based Environments - Learn how to install a dedicated Virtual Machine with Terraform, Ansible, and Packer installed to use for IaC-based environments Using Citrix Automation with Terraform and Ansible to deploy Citrix DaaS on Microsoft Azure (2025 Update) - Learn how to use Terraform and Ansible together to automatically create a new Citrix DaaS deployment on Microsoft Azure Citrix DaaS and Terraform - Automatic Deployment of Citrix DaaS and Amazon WorkSpaces Core using Terraform - Learn how to automatically deploy Citrix DaaS and Amazon WorkSpaces Core using Terraform Citrix DaaS and Terraform - Automatic Deployment of a Resource Location on XenServer 8 - Learn how to use Terraform to automatically deploy a Citrix Cloud Resource Location on XenServer 8 Citrix DaaS and Terraform - Automatic Deployment of a Resource Location on Nutanix AHV - Learn how to use Terraform to automatically deploy a Citrix Cloud Resource Location on Nutanix AHV Citrix DaaS and Terraform - Automatic Deployment of a Resource Location on vSphere 8 - Learn how to use Terraform to deploy a Citrix Cloud Resource Location on vSphere 8 automatically Citrix DaaS and Terraform - Automatic Deployment of a Resource Location on Amazon EC2 - Learn how to use Terraform to automatically create a new Resource Location on Amazon EC2, including deploying all necessary VMs, a Machine Catalog, and a Delivery Group. Citrix DaaS and Terraform - Automatic Deployment of a Resource Location on Google Cloud Platform (GCP) - Learn how to use Terraform to automatically create a new Resource Location on Google Cloud Platform (GCP), including the deployment of all necessary VMs, a Machine Catalog, and a Delivery Group. Citrix DaaS and Terraform - Automatic Deployment of a Resource Location on Microsoft Azure - Learn how to use Terraform to automatically create a new Resource Location on Microsoft Azure, including the deployment of all necessary VMs, a new domain, a Machine Catalog, and a Delivery Group. Using HashiCorp Packer to Automate the Creation of Master Images - Learn how to use HashiCorp Packer to create Master Images for MCS automatically. Citrix Virtual Apps and DesktopsCitrix Virtual Apps and Desktops SDK - A set of PowerShell cmdlets that allows you to automate and manage various Citrix products and services. Citrix Virtual Apps and Desktops REST APIs - Citrix Virtual Apps and Desktops REST APIs allow you to automate the management of resources within a Citrix Virtual Apps and Desktops deployment. Citrix Automated Configuration tool - This tool is designed to help automate the migration of Virtual Apps and Desktop configuration (such as policies, applications, machine catalogs, and others) from one or more On-Premises site(s) to the Citrix DaaS. Automated Configuration Tool POC Guide -  Learn how to use the Automated Configuration tool to automate moving your Citrix Virtual Apps and Desktops configuration to your Citrix Virtual Apps and Desktops Service deployment, as well as moving your configuration between Citrix Virtual Apps and Desktops Service deployments. Using HashiCorp Packer to Automate the Creation of Master Images - Learn how to use HashiCorp Packer to create Master Images for MCS automatically. Using Infrastructure-as-Code for deploying Citrix&#xAE; Virtual Apps and Desktops&#x2122; 2507 LTSR on XenServer&#x2122; 8.4 - Learn how to use Terraform, Ansible, and Packer to fully automate the creation of a Citrix Virtual Apps and Desktops 2507 LTSR deployment on XenServer 8 Using Infrastructure-as-Code for deploying Citrix&#xAE; Virtual Apps and Desktops&#x2122; 2507 LTSR on vSphere 8 - Learn how to use Terraform, Ansible, and Packer to fully automate the creation of a Citrix Virtual Apps and Desktops 2507 LTSR deployment on vSphere 8 Citrix Secure Private AccessCitrix Secure Private Access REST APIs for Configuration - Using the Secure Private Access REST APIs allows you to directly configure multiple applications and automate certain tasks. SIEM Integration for Citrix Secure Private Access Logs&#x2014;Citrix Analytics for Security receives user events in real-time when users access applications through Citrix Secure Private Access, such as Citrix Workspace App, Citrix Secure Access clients, or Citrix Enterprise Browser. You can view your SIEM's user events associated with Secure Private Access. NetScalerNetScaler Next-Gen API - Through the use of NetScaler APIs, administrators can save time by automating their NetScaler configuration. NetScaler uses a proprietary protocol called NITRO that allows you to programmatically configure and monitor your NetScaler ADCs using a REST interface. NetScaler Automation Toolkit&#x2014;The NetScaler Automation Toolkit contains all the NetScaler tools needed to integrate NetScaler with DevOps and Automation pipelines. NetScaler Terraform Registry (ADC)&#x2014;Citrix has developed a custom Terraform provider to automate Citrix ADC deployments and configurations. You can custom configure your ADCs using Terraform for different use cases, such as Load Balancing, SSL, Content Switching, GSLB, WAF, etc. NetScaler Terraform Registry (SDK)&#x2014;The Terraform provider for NetScaler SDX provides Infrastructure as Code (IaC) to manage your ADCs via SDX. Using the Terraform provider, you can provision VPXs on SDX, start, stop, and reboot them. Red Hat Automation Hub (ADC) - The Red Hat Automation Hub is a central repository for content that can be used to automate NetScaler. Ansible Galaxy (ADC)&#x2014;The NetScaler Ansible Galaxy collection provides a complete declarative interface for configuring and managing NetScaler ADC.</description><pubDate>Wed, 02 Oct 2024 15:32:00 +0000</pubDate></item><item><title>Citrix Gateway service for StoreFront - Deployment Strategies</title><link>https://community.stage.citrix.com/tech-zone/build/tech-papers/gateway-service-for-storefront-deployment/</link><description><![CDATA[Overview



	This tech paper offers a comprehensive overview of leveraging Citrix Gateway service for StoreFront. It enables organizations to effectively support HDX launches through Gateway service in various deployment types (not limited to those discussed below). 
 


	
		Multi-Datacenter Architecture: Enabling satellite data centers to leverage Citrix Gateway service while maintaining NetScaler Gateway in the primary data center, using Resource Location (zone-based) Configuration 
	
	
		Hybrid-Site Architecture: Accessing applications and desktops in DaaS sites through Citrix Gateway service and resources in on-premises sites through NetScaler Gateway (on-premises).
	
	
		Multitenant Architecture: Supporting customers with resources segregated into multiple CustomerIDs as different DaaS sites.
	



	Each section provides a detailed use case, configuration steps, application launch flow, and deployment strategy to help organizations effectively deploy Citrix Gateway service for StoreFront in their specific environment.
 


	Multi-Datacenter Architecture



	Use case: Enable satellite data centers to leverage Citrix Gateway service while maintaining NetScaler Gateway in the primary data center.
 


	Approach: Zone-Based Configuration



	
		Represent each data center as a Zone and configure Optimal HDX Routing (Optimal Gateway Routing) per Zone.
	



	
 


	Configuration



	
		Create a StoreFront store (Store) and configure the NetScaler Gateway for authentication and enumeration.
	
	
		Create Citrix DaaS Site-&gt; Delivery Controller (DDC) for both ResourceLocation-1(RL1) and ResourceLocation-2. (RL2)
	
	
		Create a Citrix Gateway service (Gateway1) and point it to RL1's connectors (STA).
	
	
		Create a NetScaler Gateway (Gateway2) and point it to RL2's connectors (STA).
	
	
		In StoreFront- Optimal HDX Routing wizard binds Gateway service (Gateway1) with Datacenter-1 Zone and on-prem GW (Gateway2) with Datacenter-2 Zone using Optimal HDX Routing.
	



	Application Launch Flow



	
		End users authenticate to NetScaler Gateway.
	
	
		StoreFront enumerates apps from Citrix DaaS, fetches resources, and supplies them to the end user.
	
	
		When a user clicks an app from RL1's Citrix DaaS, StoreFront selects Gateway service (Gateway1), sends an STA Ticket creation request, and launches the app through the Citrix Gateway service.
	
	
		When a user clicks an app from RL2's Citrix DaaS, StoreFront selects on-prem GW and sends an STA Ticket creation request, launching the app through the NetScaler Gateway.
	



	
		Note:
	 

	
		In this approach, a single Delivery Controller (in StoreFront) serves both resource locations, and Optimal HDX Routing is configured per Zone (Resource Location) to direct traffic to the appropriate gateway.
	 



	Deployment Strategy: Datacenter-Based Deployment



	Customers can enable satellite data centers individually to leverage Citrix Gateway service, eliminating the need for NetScaler Gateway in mini/satellite data centers. This approach allows for a gradual migration while maintaining authentication and enumeration in the primary data center.
 


	Hybrid-Site Support



	Use case: Customers can access applications/desktops in DaaS sites through Citrix Gateway service and on-premises sites through NetScaler Gateway (on-premises). 
 


	Approach: Site-Based Configuration



	
		Represent each Site as a Delivery Controller (in StoreFront) and configure Optimal HDX Routing (Optimal Gateway Routing) per Site.
	



	 
 


	
 


	Configuration



	
		Create a StoreFront store (Store) and configure NetScaler Gateway (on-premises) for authentication and enumeration.
	
	
		Create Delivery Controllers (DDC1 and DDC2) for cloud Citrix DaaS and on-premises Citrix Virtual Apps and Desktops sites, respectively:
		
			
				DDC1 points to cloud Citrix DaaS through connectors.
			
			
				DDC2 points to an on-premises Citrix Virtual Apps and Desktops site.
			
		
	
	
		Create a Citrix Gateway service (Gateway1) and point it to cloud connectors (STA).
	
	
		Create a NetScaler Gateway (on-premises) (Gateway2) and point it to on-premises STA servers.
	
	
		In StoreFront, bind Gateway1 with DDC1 and Gateway2 with DDC2 using Optimal HDX Routing (Optimal Gateway Routing) to ensure:
		
			
				The Cloud site applications launch through the Citrix Gateway service.
			
			
				The on-premises site applications launch through NetScaler Gateway (on-premises).
			
		
	



	Application Launch Flow



	
		End users authenticate to NetScaler Gateway (on-premises).
	
	
		StoreFront enumerates apps from DDC1 and DDC2, aggregates them, and supplies them to the end user through NetScaler Gateway (on-premises).
	
	
		When a user clicks a cloud site app (DDC1), StoreFront selects Gateway1 and sends an STA Ticket creation request to the Ticketing Service, launching the app through the Citrix Gateway service.
	
	
		When a user clicks an on-premises site app (DDC2), StoreFront selects Gateway2 and sends an STA Ticket creation request, launching the app through NetScaler Gateway (on-premises).
	



	Deployment Strategy: Site-Based Deployment



	Although Citrix Gateway service doesn't support on-premises DDC yet, customers with hybrid site (Citrix DaaS and CITRIX VIRTUAL APPS AND DESKTOPS) deployments can use both sites while launching DaaS site apps through Citrix Gateway service and Citrix Virtual Apps and Desktopssite apps through NetScaler Gateway (on-premises).
 


	
		Note:
	 

	
		We look forward to adding support for Citrix Virtual Apps and Desktops on-premises site through the Citrix Gateway service for StoreFront.
	 



	Multi-Customer-ID Deployment Support



	Use case: Customers with multiple Citrix Cloud Customer IDs can aggregate apps and access them through the Citrix Gateway service.
 


	
 


	Configuration



	
		Create a StoreFront store (Store) and configure NetScaler Gateway (on-premises) for authentication and enumeration.
	
	
		Create Delivery Controllers (DDC1 and DDC2) for Customer1 and Customer2, respectively.
	
	
		Create Citrix Gateway services (Gateway1 and Gateway2) and point them to the corresponding Cloud Connectors (STA) for each customer.
	
	
		In StoreFront, bind Gateway1 with DDC1 and Gateway2 with DDC2 using Optimal HDX Routing (Optimal Gateway Routing) to ensure apps use the respective CustomerID's Cloud connector for STA flow.
	



	Application Launch Flow



	
		End users authenticate to NetScaler Gateway (on-premises).
	
	
		StoreFront enumerates apps from DDC1 and DDC2, aggregates them, and supplies them to the end user through NetScaler Gateway (on-premises).
	
	
		When a user clicks an app belonging to Customer1's DDC1, StoreFront selects Gateway1, sends an STA Ticket creation request to the Ticketing Service, and launches the app through Gateway1(Citrix Gateway service).
	
	
		Similarly, when a user clicks an app belonging to Customer2's DDC2, StoreFront selects Gateway2, sends an STA Ticket creation request to the Ticketing Service, and launches the app through Gateway2(Citrix Gateway service).
	



	Deployment Strategy: Customer ID-Based Deployment



	For customers with multiple Citrix Cloud CustomerIDs pointing to different DaaS sites (DDCs), replace the existing NetScaler Gateway (on-premises) with Citrix Gateway service using Optimal HDX Routing.
 


	Summary



	This tech paper has explored different deployment scenarios for Citrix Gateway service for StoreFront, including Multi-Datacenter, Hybrid-Site, and Multi-CustomerID deployment. Each deployment scenario presents unique challenges and opportunities for organizations to optimize their Citrix deployment.
 


	By following the configuration steps, application launch flows, and deployment strategies outlined in this guide, organizations can successfully deploy Citrix Gateway service for StoreFront in various environments, ensuring seamless and secure access to applications and desktops for their users.
 


	Remember to carefully evaluate your organization's needs and requirements before choosing a deployment scenario. With proper planning and execution, the Citrix Gateway service for StoreFront can help you achieve a more agile, flexible, and secure digital workspace.
 


	Key Takeaways



	
		Citrix Gateway service for StoreFront offers flexibility and scalability in various deployment scenarios.
	
	
		Proper configuration and deployment strategies are crucial for successful deployment.
	
	
		Understanding user needs and requirements is essential for choosing the right deployment scenario.
	



	Resources



	
		Citrix Gateway service for StoreFront]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_09/image.png.a10aed0034166b3873e9b09cbc4c2bb4.png" length="119218" type="image/png"/><pubDate>Thu, 19 Sep 2024 18:01:00 +0000</pubDate></item><item><title>Deploying Citrix VDA for macOS on AWS EC2 Mac Instances</title><link>https://community.stage.citrix.com/tech-zone/build/deployment-guides/macos-vda-aws/</link><description><![CDATA[Introduction



	AWS EC2 supports launching Mac instances from a custom base image. The succeeding instances will inherit the same applications and configurations. Hence, installing and pre-configuring Citrix Virtual Delivery Agent for macOS (Mac VDA) in a custom base image for further volume deployment can be automated without any manual intervention, providing enterprise administrators ultimate flexibility in their daily workflows. The following steps are covered in this guide:
 


	
		Launch a new Mac instance from AWS EC2.
	
	
		Install and configure Mac VDA on this instance.
	
	
		Set the essential permissions, such as Screen Recording and Accessibility enabled for the instance.
	
	
		Create a custom base image from this instance.
	
	
		The base image can be used to create new Mac instances in AWS EC2
	
	
		On new instances, enroll Mac VDA to CVAD or DaaS.
		
			
				This can be automated by integrating with MDM solutions such as Jamf Pro
			
		
	



	AWS EC2 supports automatic enrollments into Jamf Pro when creating a new instance. Combined with the Jamf PRO guide in the Mac VDA product document, enterprises can now deploy Citrix VDA for macOS with “zero-touch.”
 


	Prerequisites



	This guide uses Citrix DaaS as the deployment option. Mac VDA needs to communicate with Citrix Cloud for registration and HDX traffic (when Rendezvous is enabled). Follow the Rendezvous V2 | Citrix DaaS to configure firewalls in the Amazon VPC.
 


	Creating an Amazon EC2 Mac instance



	To create an Amazon EC2 Mac instance from instance family mac2, which is M1/M2 Mac, follow this guide to launch and connect to an Amazon EC2 Mac instance through SSH. Then, follow this guide to enable GUI access to this Mac instance.
 


	We will install Mac VDA and perform some basic configurations on this instance. This instance will be used to create an Amazon Machine Image (AMI), so the subsequent instances from this image will automatically have Mac VDA installed and configured.
 


	Installing Mac VDA on the Mac instance



	First, we need to upload the Mac VDA install package to the Mac instance using the following command:
 


	$ scp -i /path/to/your/private/key/file mac-vda.dmg ec2-user@mac-instance-fqdn-or-ip-address:/Users/ec2-user/
 


	Once the package is copied, we can access the Mac instance using a VNC client and install Mac VDA. 
 


	
		Important:
	 

	
		DO NOT try to enroll the VDA into Citrix DaaS.
	 



	 
 


	
		Install dotnet 8.0 by downloading .NET Runtime 8.0 installer for macOS.
	
	
		Run the install package to install Mac VDA to the system.
	
	
		Enable Screen &amp; System Audio Recording for the Citrix Graphics Service.
	
	
		Enable Accessibility for Citrix Input Service.
	



	
 


	Refer to the installation guide for additional details.
 


	(Optional) Enrolling the Mac instance into Jamf Pro



	Follow this guide to preconfigure some profiles, restrictions, or apps on this Mac instance with Jamf Pro.
 


	Creating an Amazon Machine Image based on the Mac instance



	Creating an image of an EC2 Mac instance allows you to launch new instances.
 


	
		In the AWS console, click Amazon EC2, Instances, and the running instance in the list. 
	
	
		Click Actions &gt; Image &gt; Templates, and finally, the Create image button at the bottom right.
	
	
		If No Reboot is checked, the instance will not reboot before creating the image and will create the image in the background. If Delete on termination is checked, new instances launched from this image will not have their root volume deleted when the instance is terminated.
	



	
 


	Creating a new Amazon EC2 Mac instance from the AMI



	Once the AMI is created, we can then create a new instance from this AMI. The new instance will have Mac VDA installed and configured automatically.
 


	Enrolling Mac VDA into Citrix DaaS



	Mac VDA needs to be enrolled into Citrix DaaS before it can be launched from a storefront or workspace. You need to create an enrollment token from the Citrix Cloud console or Citrix Web Studio. 
 


	See Prepare Installation Non-Domain joined VDAs | Citrix Virtual Delivery Agent for macOS on creating an enrollment token.
 


	Once an enrollment token has been created, you can then enroll Mac VDA from the VDA Configuration app.
 


	
 


	If you use Jamf Pro to manage these subsequent instances, you can also deploy and run a script to complete the enrollment automatically from Jamf Pro onto this instance.
 


	# /opt/Citrix/VDA/bin/VdaEnrollmentTool -EnrollmentToken:&lt;your-enrollment-token&gt; -Restart
 


	Refer to Example using UEM / MDM | Citrix Virtual Delivery Agent for macOS for more details.
 


	Once the VDA is enrolled, admins can create delivery groups to publish this VDA to users.
 


	(Optional) Connecting Amazon VPC and Your Remote Network



	If you want users to access Mac VDAs from your internal corporate network via a NetScaler Gateway or Citrix Virtual Apps and Desktops instead of Citrix DaaS, you must connect the Amazon VPN and your corporate network. See Amazon Virtual Private Cloud Connectivity Options for details and instructions on this.]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_09/image.png.b2046f5199593252d33d415478b16138.png" length="74138" type="image/png"/><pubDate>Thu, 19 Sep 2024 10:42:00 +0000</pubDate></item><item><title>Deploying Citrix VDA for macOS on MacStadium</title><link>https://community.stage.citrix.com/tech-zone/build/deployment-guides/citrix-vda-for-macos-macstadium/</link><description>Overview



	MacStadium is the industry-leading Mac cloud provider that provides access to genuine Apple Mac mini and Studio hardware in their Cloud Compute environment. MacStadium has partnered with Citrix to certify the Citrix VDA for macOS with MacStadium Cloud Compute. Examples of mini and studio models are available at https://www.macstadium.com/bare-metal-mac.
 


	
		Note:
	 

	
		Virtualization on macOS has different limitations compared to Windows and Linux. Specifically, macOS limits users to a maximum of 2 VMs running at a time and restricts specific actions within VMs, such as signing into an Apple ID or using a USB passthrough. As a result, we recommend and support bare metal Mac deployments with the Citrix VDA for macOS today.
	 



	 
 


	This deployment guide provides the steps to procure and configure your MacStadium solution with the Citrix VDA for macOS. The following steps are covered in the guide:
 


	
		Talk to the MacStadium team to assist with your needs.
	
	
		Prepare your Citrix DaaS environment by creating a Machine Catalog and capturing the Enrollment Token.
	
	
		Procure your MacStadium Cloud Compute from MacStadium and share the Enrollment Token with the MacStadium Support team, who will automate the installation and registration of the Citrix VDA on your chosen MacStadium models.
	
	
		Create a Delivery Group with user assignments.
	
	
		(Optional) Enable Rendezvous
	
	
		Launch macOS VDA via the Citrix Workspace app that is installed on your user&#x2019;s desktops.
	



	Talk to the MacStadium team



	Learn more about how MacStadium can be a part of your Citrix DaaS solution. Review the web page and contact us via the form at https://www.macstadium.com/citrix. We&#x2019;ll reach out to discuss options for your deployment.
 


	MacStadium will provide the Cloud Compute, and customers will bring their own Citrix license/subscription directly from Citrix or through a partner. Whether or not your organization uses an MDM tool (e.g., JAMF or Kandji), MacStadium can deploy Cloud Compute resources. 
 


	Prepare your Citrix DaaS environment



	
		Note: 
	 

	
		The steps for preparation and installation are quite similar if you&#x2019;re deploying with Citrix Virtual Apps and Desktops; please refer to the Installation section in the product documentation for details.
	 



	 
 


	1. Log in to Citrix DaaS, open Web Studio, and select Machine Catalogs.
 


	 
 


	2. Click Create Machine Catalog.
 


	
 


	3. Select Remote PC Access and click Next. (The Single-session OS option is also supported.)
 


	
 


	4. Select "I want users to connect to the same (static) desktop each time they log on" and click Next.
 


	
 


	5. Select the minimum functional level for this catalog and click Next on the Machine Accounts page.
 


	
 


	6. Click Next on the Scopes page.
 


	
 


	7. Click Next on the Workspace Environment Management (Optional) page.
 


	
 


	8. Leave the "Enable VDA upgrade" unchecked and click Next.
 


	
 


	9. Name your Machine Catalog and click Finish.
 


	
 


	10. The Machine Catalog is now created.
 


	
 


	11. Right-click on the Citrix VDA for macOS Machine Catalog and select Manage Enrollment Tokens.
 


	
 


	12. Click Generate.
 


	
 


	13. Enter your Token name, select Use current date and time for start, enter 100 into the Specify how many times the token can register VDAs, choose an appropriate end date for the token to allow VDA registrations, and click Generate.
 


	
 


	14. Click Copy.
 


	
 


	15. Optionally, click Download to download this token for later usage.
 


	
 


	16. You will now see your active Enrollment token. Click Close.
 


	Procure your MacStadium Cloud Compute



	 
	
 


	MacStadium offers various models of dedicated Bare Metal Mac minis and Studios, especially compared to competitors. Standard models that are available immediately in the Cloud Compute environment are listed by MacStadium here. Custom configurations can be made available in large quantities. Contact MacStadium via the form at https://www.macstadium.com/citrix.
 


	
		Purchase your Cloud Compute directly from the MacStadium self-service portal or by speaking with their sales team via the form at https://www.macstadium.com/citrix. 
	
	
		Choose your version of macOS Ventura 13, Sonoma 14, or Sequoia 15. You do not need to prepare your macOS base image for deployment, as MacStadium provides multiple options.
	
	
		Share the enrollment token and VDA agent version with the MacStadium Support team through the support ticket system.
	



	 
	
 


	The MacStadium team will ensure the VDA for macOS is installed and registered with your enrollment tokens. The team will respond to the support ticket with confirmation that the machines have enrolled successfully.
 


	Your organization&#x2019;s network environment may need configuring to support the Citrix VDA, Delivery Controller, and/or Director. For specific guidance, see the documentation here.
 


	Create a Delivery Group with user assignments.



	1. Within Citrix DaaS Web Studio, select Delivery Groups, then click Create Delivery Group.
 


	
 


	2. Select your Citrix VDA for macOS Machine Catalog and click Next.
 


	
 


	3. Select which users can access the Delivery Group and click Next.
 


	
 


	4. Add the required Desktop Assignment Rule and click Next.
 


	
 


	5. Select Next on the App Protection screen.
 


	
 


	6. Click Next on the Scopes window.
 


	
 


	7. Select the appropriate License Assignment for your Citrix DaaS deployment and click Next.
 


	
 


	8. Click Next on the Policy Set window.
 


	
 


	9. Provide a name for your Delivery Group and click Finish.
 


	
 


	10. Your Delivery Group is now created and ready for user access.
 


	
 


	(Optional) Enable Rendezvous



	If your deployment contains non-domain-joined macOS devices, enable the Rendezvous Citrix policy to allow access.
 


	1. Within Citrix Web Studio, select Policies and then click Create Policy.
 


	
 


	2. Select ICA within View by Category, then scroll to and select Rendezvous Protocol. Click Allow.
 


	
 


	3. Click Next.
 


	
 


	4. Select Filtered users and computers and expand the Delivery Group.
 


	
 


	5. Select Allow in the Mode drop-down menu, and then choose your Citrix VDA for macOS Delivery Group, select Enable, and click Save.
 


	 
 


	6. review your filters, then click Next.
 


	
 


	7. Select Enable policy, provide a Policy name, then click Finish.
 


	
 


	8. The Rendezvous policy is now active and enabled.
 


	
 


	Launch macOS VDA via Citrix Workspace app



	Your users can now launch the macOS VDA via the Citrix Workspace app.</description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_09/image.png.417331d5ce7c81c00e386687e16bb7b1.png" length="145229" type="image/png"/><pubDate>Thu, 19 Sep 2024 10:42:00 +0000</pubDate></item><item><title>Deployment Guide: Using Terraform to deploy a Citrix Virtual Apps and Desktops 2402 LTSR CU1 site on XenServer 8</title><link>https://community.stage.citrix.com/tech-zone/automation/cvad-terraform-xenserver/</link><description><![CDATA[Using Terraform to deploy a Citrix Virtual Apps and Desktops 2402 LTSR CU1 site on XenServer 8  
	 



	Overview



	This guide will showcase the possibility of deploying a Citrix DaaS Virtual Apps and Desktops 2402 LTSR CU1 site on XenServer 8 using Terraform. We want to reduce manual interventions to the absolute minimum. 
	 
	In the end, you will have created:
 


	
		2 Virtual Machines registered with the Domain for the deployment of the Delivery Controllers 
	
	
		1 Virtual Machine registered with the Domain for deploying all Terraform scripts (explanations follow later in this guide)
	
	
		A complete CVAD 2402 deployment containing 2 Delivery Controllers, StoreFront, WebStudio, and the connection to a central License Server
	
	
		A Hosting Connection to XenServer 8
	
	
		A Machine Catalog containing 2 VDI machines based on a Master Image
	
	
		A Delivery Group with full AutoScale support
	



	 
	Installation and Configuration of Terraform



	Please find a description of installing Terraform and initial configurations in our Tech Zone Deployment Guide: Installing Terraform and Configuring the Citrix Terraform Provider. 
	All Terraform configuration files can be found later on GitHub.
 


	Deployment overview



	This guide uses an existing domain and will not deploy a new one. For further instructions on deploying a new domain, refer to the guide Citrix DaaS and Terraform - Automatic Deployment of a Resource Location on Microsoft Azure.  
	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 primary Domain Controller using IPSec-based Site-to-Site VPNs. Each Resource Location has its subdomain. 
	 
 


	Note: 
	 
	We did not use loop-constructs or count keywords —each resource is created explicitly—for easier reading and understanding. 
	 
 


	The Terraform flow is split into five different modules:
 


	
		Module One - this part can be run on any computer where Terraform is installed :

		
			
				Creating the initially needed Resources on XenServer 8:

				
					
						Create two Windows Server 2022-based VMs used as Delivery Controller VMs in Module 2
					
					
						Create a Windows Server 2022-based VM acting as an Administrative Workstation for running Terraform Modules 3 and 4. 
						This workstation is necessary because WinRM is used for further configuration and deployment. 
						 
					
				
			
		
	



	Important:  
	 
	The Image Template VM must be configured before Terraform uses it to create the Machine Catalog. Please ensure that WinRM is functioning even though the VM is NOT part of a Domain. Instructions are provided later in this guide. 
	 
	The Administrative VM must have Terraform installed - use the Chocolately Guidance provided previously for installation. 
	 
 


	
		Module Two—This part can be run on any computer where Terraform is installed but relies heavily on WinRM. It can only be run after Module One is completed. 
		Due to the very early version of the Terraform provider for XenServer, the following essential steps must be done by calling PowerShell scripts on the three previously created VMs using WinRM:

		
			
				Rename the VM names to meet the AD requirements
			
			
				Set the correct IP addresses
			
			
				Add the 3 VMs to the Domain 
				 
			
		
	
	
		Module Three -  this part can only be run after completion of Module Two. 
		It must be run on the previously created Administrative VM as the deployment relies heavily on WinRM:
		
			
				Creating the needed databases
			
			
				Installing Citrix Virtual Apps and Desktops on the Delivery Controller VMs
			
			
				Creating the Citrix Virtual Apps and Desktops site
			
			
				Installing Citrix StoreFront on the Delivery Controller VMs
			
			
				Configuring a Citrix StoreFront site 
				 
			
		
	



	Important:  
	 
	The Terraform Provider can only configure StoreFront on one StoreFront server/Delivery Controller (listed in the Provider configuration). Due to current limitations In PowerShell-Invoking, we cannot automatically add the second Delivery Controller/Storefront Server to the configured StoreFront Cluster. 
	 
	Be aware that you must manually sync the configuration to the second StoreFront server/Delivery Controller.  
	 
 


	
 


	
		Module Four - this part can only be run after completion of Module Three. 
		It must be run on the previously created Administrative VM as the deployment relies heavily on WinRM:

		
			
				Changing the SSL certificate on the Delivery Controllers/StoreFront servers to a publicly signed certificate to avoid certificate issues
			
			
				Uploading a valid license file to a License Server locally installed on the Primary Delivery Controller (optional)
			
			
				Setting the License Configuration of the site to point to a central License Server (optional) 
				 
			
		
	
	
		Module Five - this part can only be run after the completion of Module Four. 
		It must be run on the previously created Administrative VM as the deployment relies heavily on WinRM:
		
			
				Creating all needed entities in the Citrix Virtual Apps and Desktops site:
			
		
	



	
		Creating a dedicated Hypervisor Connection to XenServer 8
	
	
		Creating a dedicated Hypervisor Resource Pool
	
	
		Creating a Machine Catalog based on the referenced Master Image
	
	
		Creating a Delivery Group
	
	
		Creating AutoScale settings and applying these to the newly created Delivery Group 
		 
	



	Important: 
	 
	Make sure that all Terraform-related VMs can communicate using WinRM. The deployment fails if the Admin-VM cannot connect to the CCs using WinRM. Various configuration guides for WinRM can be found on the Internet. We rely on GPOs related to WinRM to configure all Domain Members accordingly. 
	 
 


	Important: 
	 
	Ensure that the VM planned to be the Master Image for the Worker VMs is installed and configured and that all the needed software is installed and configured. 
	It also needs to have the VDA installed and configured. 
	Providing a Worker-Master Image VM without a VDA breaks the Terraform flow. 
	 
 


	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 td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\_TACG&gt; test-wsman -ComputerName &lt;IP-Address of the computer you want to reach&gt; -Credential &lt;IP-Address of the computer you want to reach&gt;\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 &lt;IP-Address of the computer you want to reach&gt; -Credential &lt;IP-Address of the computer you wish to reach&gt;\administrator  
					 

					
						 
					 

					
						The response should look like:  
					 

					
						[172.31.22.104]: PS C:\Users\Administrator\Documents&gt;
					 
				
			
		
	

	
		 
		A short Terraform script also checks if the communication via WinRM between the Admin-VM and, in this example, the DDC1-VM is working as intended:
	 

	
		
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}		
		
			
				
					
						
							 
							
								
									
										
									
									
										
											
												
													locals {
												 

												
													  #### Test the WinRM communication
												 

												
													  #### Need to invoke PowerShell as Domain User as the provisioner does not allow it to be run in a Domain Users-context
												 

												
													  TerraformTestWinRMScript     = &lt;&lt;-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 td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;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.  
						 

						
							#&lt; CLIXML
						 

						
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
						 

						
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;
						 

						
							null_resource.CreateTestScriptOnCC1 (remote-exec): C:\Users\Administrator&gt;powershell -File c:/temp/xdinst/DATA/Terraform-Test-WinRM.ps1
						 

						
							#&lt; CLIXML
						 

						
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
						 

						
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;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 that must be set.  
		Some configuration settings are propagated throughout the whole Terraform configuration...
	 

	
		You must start each module 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. 
		 
	 

	
		Important: 
		 
		Each module/step must be completed successfully before the next module can be started. 
		 
	 

	
		 
	 

	
		File System structure
	

	
		Root-Directory
	 

	
		Module 1: _CVADOnXenServer-Creation:
	 

	
		
			
				
				
			
			
				
					
						
							Filename                                                                                                       
						 
					
					
						
							Purpose                                                
						 
					
				
			
			
				
					
						
							CVADOnXenServer-Create.tf
						 
					
					
						
							Resource configuration and primary flow definition
						 
					
				
				
					
						
							CVADOnXenServer-Create-variables.tf
						 
					
					
						
							Definition of Variables
						 
					
				
				
					
						
							CVADOnXenServer-Create.auto.tfvars.json
						 
					
					
						
							Setting the values of the Variables
						 
					
				
				
					
						
							CVADOnXenServer-Create-Provider.tf
						 
					
					
						
							Provider definition and configuration
						 
					
				
				
					
						
							CVADOnXenServer-Create-Provider-variables.tf
						 
					
					
						
							Definition of Variables
						 
					
				
				
					
						
							CVADOnXenServer-Create-Provider.auto.tfvars.json
						 
					
					
						
							Setting the values of the Variables
						 
					
				
			
		
	

	
		 
		 
		Module 2: _CVADOnXenServer-InitialConfiguration:
	 

	
		
			
				
				
			
			
				
					
						
							Filename                                                                                                        
						 
					
					
						
							Purpose                                               
						 
					
				
			
			
				
					
						
							CVADOnXenServer-InitialConfiguration.tf
						 
					
					
						
							Resource configuration and primary flow definition
						 
					
				
				
					
						
							CVADOnXenServer-InitialConfiguration-variables.tf
						 
					
					
						
							Definition of Variables
						 
					
				
				
					
						
							CVADOnXenServer-InitialConfiguration.auto.tfvars.json
						 
					
					
						
							Setting the values of the Variables
						 
					
				
				
					
						
							CVADOnXenServer-Provider.tf
						 
					
					
						
							Provider definition and configuration
						 
					
				
				
					
						
							CVADOnXenServer-Provider-variables.tf
						 
					
					
						
							Definition of Variables
						 
					
				
				
					
						
							CVADOnXenServer-Provider.auto.tfvars.json
						 
					
					
						
							Setting the values of the Variables
						 
					
				
				
					
						
							DATA Directory
						 
					
					
						
							Contains dynamically created scripts which will be uploaded and executed on the Cloud Connector VMs
						 
					
				
			
		
	

	
		 
	 

	
		Module 3: _CVADOnXenServer-InitialCVADConfiguration:
	 

	
		
			
				
				
			
			
				
					
						
							Filename                                                                                                        
						 
					
					
						
							Purpose                                               
						 
					
				
			
			
				
					
						
							CVADOnXenServer-InitialCVADConfiguration.tf
						 
					
					
						
							Resource configuration and primary flow definition
						 
					
				
				
					
						
							CVADOnXenServer-InitialCVADConfiguration-variables.tf
						 
					
					
						
							Definition of Variables
						 
					
				
				
					
						
							CVADOnXenServer-InitialCVADConfiguration.auto.tfvars.json
						 
					
					
						
							Setting the values of the Variables
						 
					
				
				
					
						
							CVADOnXenServer-Provider.tf
						 
					
					
						
							Provider definition and configuration
						 
					
				
				
					
						
							CVADOnXenServer-Provider-variables.tf
						 
					
					
						
							Definition of Variables
						 
					
				
				
					
						
							CVADOnXenServer-Provider.auto.tfvars.json
						 
					
					
						
							Setting the values of the Variables
						 
					
				
				
					
						
							terraform.tfvars
						 
					
					
						
							Setting the values of Variables that can not be set by a .json-based file
						 
					
				
			
		
	
	 

	
		Module 4: _CVADOnXenServer-Configuration-AfterStorefront:
	 

	
		
			
				
				
			
			
				
					
						
							Filename                                                                                                        
						 
					
					
						
							Purpose                                               
						 
					
				
			
			
				
					
						
							CVADOnXenServer-Configuration-AfterStorefront-scripts.tf
						 
					
					
						
							Resource configuration and primary flow definition
						 
					
				
				
					
						
							CVADOnXenServer-Configuration-AfterStorefront-scripts-variables.tf
						 
					
					
						
							Definition of Variables
						 
					
				
				
					
						
							CVADOnXenServer-Configuration-AfterStorefront-scripts.auto.tfvars.json
						 
					
					
						
							Setting the values of the Variables
						 
					
				
				
					
						
							terraform.tvars
						 
					
					
						
							Setting the values of Variables that can not be set by a .json-based file
						 
					
				
				
					
						
							CVADOnXenServer-Provider.tf
						 
					
					
						
							Provider definition and configuration
						 
					
				
				
					
						
							CVADOnXenServer-Provider-variables.tf
						 
					
					
						
							Definition of Variables
						 
					
				
				
					
						
							CVADOnXenServer-Provider.auto.tfvars.json
						 
					
					
						
							Setting the values of the Variables
						 
					
				
			
		
	

	
		 
	 

	
		Module 5: _CVADOnXenServer-CVAD-Entities:
	 

	
		
			
				
				
			
			
				
					
						
							Filename                                                                                                        
						 
					
					
						
							Purpose                                               
						 
					
				
			
			
				
					
						
							CVADOnXenServer-CVAD-CreateEntities.tf
						 
					
					
						
							Resource configuration and primary flow definition
						 
					
				
				
					
						
							CVADOnXenServer-CVAD-CreateEntities-variables.tf
						 
					
					
						
							Definition of Variables
						 
					
				
				
					
						
							CVADOnXenServer-CVAD-CreateEntities.auto.tfvars.json
						 
					
					
						
							Setting the values of the Variables
						 
					
				
				
					
						
							terraform.tvars
						 
					
					
						
							Setting the values of Variables that can not be set by a .json-based file
						 
					
				
				
					
						
							CVADOnXenServer-Provider.tf
						 
					
					
						
							Provider definition and configuration
						 
					
				
				
					
						
							CVADOnXenServer-Provider-variables.tf
						 
					
					
						
							Definition of Variables
						 
					
				
				
					
						
							CVADOnXenServer-Provider.auto.tfvars.json
						 
					
					
						
							Setting the values of the Variables
						 
					
				
			
		
	

	
		 
	 

	
		Caution: 
		 
		All Terraform-related directories and files (.terraform, -terraform.lock.hcl, terraform.tfstate, terraform.tfstate) must not be changed or deleted - doing so might break the deployment. 
		 
	 

	
		Change the settings in the .json or .tfvars files to match your needs.
	 

	
		To ensure a smooth and error-free build, the following prerequisites must be met before setting the corresponding settings or running the Terraform workflow.
	 

	
		 
	 

	
		 Software Components for Configuration and Deployment
	

	
		Important: 
		 
		Please ensure that the Delivery Controller VMs have the CVAD 2402 LTSR CU1-ISO mounted so Terraform can access it during the configuration run. 
		 
	 

	
		The Terraform deployment needs actual versions of the following software components:
	 

	
		
			XenServer 8 PowerShell SDK. Download the XenServer 8 PowerShell SDK Installer
		
		
			The CVAD 2402 CU1 installer. Terraform anticipates that the Installer ISO is mounted.
		
		
			The StoreFront installer. Terraform anticipates that the Installer ISO is mounted.
		
	

	
		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 to which all the required software is uploaded - or directly from the Internet.  
		The URIs of the Storage Repository can be set in the corresponding variables:
	 

	
		
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}		
		
			
				
					
						
							...
							
								 "CVAD_Install_Source-Drive":"D:",
							 

							
								 "CVAD_Install_Source-Path":"x64/\"XenDesktop Setup\"/XenDesktopServerSetup.exe",
							 

							
								 "SF_Install_Source-Path":"x64/StoreFront/CitrixStoreFront-x64.exe -silent",
							 

							
								...
							 
						
					
				
			
		
	

	
		We must also determine various information outside the XenServer Cluster to correctly set specific variables, such as Network and Master Image names.
	 

	
		The XenServer Cmdlets are a PowerShell extension for accessing a XenServer 8 environment. 
		You must install the  XenServer PowerShell cmdlets to proceed with the following steps.
	 

	
		The first step is to connect to the XenServer-Cluster:
	 

	
		
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}		
		
			
				
					
						
							PS C:\_TACG&gt; Connect-Xenserver -Url https://10.10.111.11 -Username ********** -Password !**********!
						 

						
							 
						 

						
							New Security Certificate
						 

						
							The certificate fingerprint of the server you have connected to is :
						 

						
							4B:89:BF:DA:B6:B4:20:71:D9:45:28:A8:FC:9E:60:12:96:0A:5F:C3
						 

						
							The certificate on this server is not trusted.
						 

						
							Do you wish to continue?
						 

						
							[J] Ja  [N] Nein  [H] Anhalten  [?] Hilfe (Standard ist "J"): J
						 

						
							 
						 

						
							PS C:\_TACG&gt;
						 
					
				
			
		
	

	
		
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}		
		
			
			
		
	

	
		Now we need to get information about the available VM templates:
	 

	
		
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}		
		
			
				
					
						
							PS C:\_TACG&gt; Import-Module XenServerPSModule 
							PS C:\_TACG&gt; Connect-Xenserver -Url https://10.10.111.11 -Username ********** -Password !**********! 
							PS C:\_TACG&gt; Get-XenVM |  Where {$_.is_a_template -eq $True -and $_.domid -ne 0}  | select name_label | Sort-Object -Property name_label 
							 
							name_label 
							---------- 
							1 
							1 
							1 
							2 
							2 
							2 
							3 
							3 
							CentOS 7 
							CentOS Stream 9 (preview) 
							Citrix_XD_MC-TACG-TF-CVAD-XS8 
							Debian Bookworm 12 (preview) 
							Debian Bullseye 11 
							Debian Buster 10 
							Gooroom Platform 2.0 
							NeoKylin Linux Server 7 
							Oracle Linux 7 
							Oracle Linux 8 
							Other install media 
							Red Hat Enterprise Linux 7 
							Red Hat Enterprise Linux 8 
							Red Hat Enterprise Linux 9 (preview) 
							Rocky Linux 8 
							Rocky Linux 9 (preview) 
							Scientific Linux 7 
							SUSE Linux Enterprise 15 (64-bit) 
							SUSE Linux Enterprise Server 12 SP5 (64-bit) 
							TMPL-W11 
							TMPL-W11-VDA 
							TMPL-W2K22 
							TMPL-W2K22-WinRM 
							Ubuntu Focal Fossa 20.04 
							Ubuntu Jammy Jellyfish 22.04 
							Windows 10 (64-bit) 
							Windows 11 
							Windows Server 2016 (64-bit) 
							Windows Server 2019 (64-bit) 
							Windows Server 2022 (64-bit) 
							 
							 
							PS C:\_TACG&gt;
						 
					
				
			
		
	

	
		We have determined all the values needed and can start the deployment.
	 

	
		Important:  
		 
		Remember to put all previously determined entity values in the corresponding .auto.tfvars.json variable files. 
		 
	 

	
		Module 1: Create the initially needed Resources on XenServer 8
	

	
		This module is split into the following configuration parts:
	 

	
		
			Creating the initially needed Resources on XenServer 8:

			
				
					Create two Windows Server 2022-based VMs used as Delivery Controller VMs in Module 2
				
				
					Create a Windows Server 2022-based VM acting as an Administrative Workstation for running Terraform Modules 3 and 4. 
					This is necessary because WinRM is used for further configuration and deployment.
				
			
		
	

	
		Terraform automatically does all these steps.
	 

	
		Important: 
		 
		The first and second modules are separated from the following steps as we assume no Admin-VM is installed in the corresponding environment on the XenServer cluster.  
		The Admin-VM is created now in Module 1 and configured in Module 2, and all subsequent modules must be run from it. Working with WinRM calls over the Internet would pose a security threat and significantly reduce the performance of the Terraform-based deployment.  
		These are the reasons for the split approach. 
		 
	 

	
		Important: 
		 
		Before starting the Terraform workflow, please ensure you have configured the variables according to your needs using the corresponding .auto.tfvars.json and/or terraform.tfvars file. 
		 
	 

	
		The configuration can be started by following the standard Terraform workflow:
	 

	
		terraform init, 
		terraform plan 
		 
	 

	
		and if no errors occur
	 

	
		terraform apply
	 

	
		
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}		
		
			
				
					
						
							PS C:\_TACG\_CVADOnXenServer\_CVADOnXenServer-Creation&gt; terraform init 
							Initializing the backend... 
							Initializing provider plugins... 
							- Finding latest version of xenserver/xenserver... 
							- Installing xenserver/xenserver v0.1.1... 
							- Installed xenserver/xenserver v0.1.1 (self-signed, key ID FABD99A0D7648275) 
							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\_CVADOnXenServer\_CVADOnXenServer-Creation&gt; terraform plan 
							data.xenserver_sr.sr: Reading... 
							data.xenserver_network.network: Reading... 
							data.xenserver_network.network: Read complete after 0s 
							data.xenserver_sr.sr: Read complete after 0s 
							 
							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: 
							 
							  # xenserver_sr.local will be created 
							  + resource "xenserver_sr" "local" { 
							      + content_type     = "ISOs" 
							      + device_config    = { 
							          + "cifspassword" = "************" 
							          + "location"     = "\\\\tacg-dc.the-austrian-citrix-guy.at\\_sw" 
							          + "type"         = "cifs" 
							          + "username"     = "************@the-austrian-citrix-guy.at" 
							          + "vers"         = "1.0" 
							        } 
							      + host             = (known after apply) 
							      + id               = (known after apply) 
							      + name_description = "ISO repository on TACG-DC" 
							      + name_label       = "ISO on TACG-DC" 
							      + shared           = true 
							      + sm_config        = { 
							          + "iso_type" = "cifs" 
							        } 
							      + type             = "iso" 
							      + uuid             = (known after apply) 
							    } 
							 
							  # xenserver_vm.vm["windows-vm"] will be created 
							  + resource "xenserver_vm" "vm" { 
							      + boot_mode         = (known after apply) 
							      + boot_order        = (known after apply) 
							      + cdrom             = "Citrix_Virtual_Apps_and_Desktops_7_2402_LTSR_CU1_1100.iso" 
							      + check_ip_timeout  = 0 
							      + cores_per_socket  = 2 
							      + default_ip        = (known after apply) 
							      + dynamic_mem_max   = (known after apply) 
							      + dynamic_mem_min   = (known after apply) 
							      + hard_drive        = (known after apply) 
							      + id                = (known after apply) 
							      + name_description  = "" 
							      + name_label        = "TACG-XS-DDC1" 
							      + network_interface = [ 
							          + { 
							              + device       = "0" 
							              + mac          = (known after apply) 
							              + network_uuid = "14c664c3-1d69-5baa-6e51-21f7059aec3a" 
							              + other_config = (known after apply) 
							              + vif_ref      = (known after apply) 
							            }, 
							        ] 
							      + other_config      = {} 
							      + static_mem_max    = 8589934592 
							      + static_mem_min    = (known after apply) 
							      + template_name     = "TMPL-W2K22-WinRM" 
							      + uuid              = (known after apply) 
							      + vcpus             = 2 
							    } 
							 
							  # xenserver_vm.vm["windows-vm1"] will be created 
							  + resource "xenserver_vm" "vm" { 
							      + boot_mode         = (known after apply) 
							      + boot_order        = (known after apply) 
							      + cdrom             = "Citrix_Virtual_Apps_and_Desktops_7_2402_LTSR_CU1_1100.iso" 
							      + check_ip_timeout  = 0 
							      + cores_per_socket  = 2 
							      + default_ip        = (known after apply) 
							      + dynamic_mem_max   = (known after apply) 
							      + dynamic_mem_min   = (known after apply) 
							      + hard_drive        = (known after apply) 
							      + id                = (known after apply) 
							      + name_description  = "" 
							      + name_label        = "TACG-XS-DDC2" 
							      + network_interface = [ 
							          + { 
							              + device       = "0" 
							              + mac          = (known after apply) 
							              + network_uuid = "14c664c3-1d69-5baa-6e51-21f7059aec3a" 
							              + other_config = (known after apply) 
							              + vif_ref      = (known after apply) 
							            }, 
							        ] 
							      + other_config      = {} 
							      + static_mem_max    = 8589934592 
							      + static_mem_min    = (known after apply) 
							      + template_name     = "TMPL-W2K22-WinRM" 
							      + uuid              = (known after apply) 
							      + vcpus             = 2 
							    } 
							 
							  # xenserver_vm.vm["windows-vm2"] will be created 
							  + resource "xenserver_vm" "vm" { 
							      + boot_mode         = (known after apply) 
							      + boot_order        = (known after apply) 
							      + cdrom             = "Citrix_Virtual_Apps_and_Desktops_7_2402_LTSR_CU1_1100.iso" 
							      + check_ip_timeout  = 0 
							      + cores_per_socket  = 2 
							      + default_ip        = (known after apply) 
							      + dynamic_mem_max   = (known after apply) 
							      + dynamic_mem_min   = (known after apply) 
							      + hard_drive        = (known after apply) 
							      + id                = (known after apply) 
							      + name_description  = "" 
							      + name_label        = "TACG-XS-CVADAVM" 
							      + network_interface = [ 
							          + { 
							              + device       = "0" 
							              + mac          = (known after apply) 
							              + network_uuid = "14c664c3-1d69-5baa-6e51-21f7059aec3a" 
							              + other_config = (known after apply) 
							              + vif_ref      = (known after apply) 
							            }, 
							        ] 
							      + other_config      = {} 
							      + static_mem_max    = 8589934592 
							      + static_mem_min    = (known after apply) 
							      + template_name     = "TMPL-W2K22-WinRM" 
							      + uuid              = (known after apply) 
							      + vcpus             = 2 
							    } 
							 
							Plan: 4 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\_CVADOnXenServer\_CVADOnXenServer-Creation&gt; terraform validate 
							Success! The configuration is valid.
						 

						
							PS C:\_TACG\_CVADOnXenServer\_CVADOnXenServer-Creation&gt; terraform apply 
							data.xenserver_sr.sr: Reading... 
							data.xenserver_network.network: Reading... 
							data.xenserver_network.network: Read complete after 0s 
							data.xenserver_sr.sr: Read complete after 0s 
							 
							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:
						 

						
							  # xenserver_sr.local will be created 
							  + resource "xenserver_sr" "local" { 
							      + content_type     = "ISOs" 
							      + device_config    = { 
							          + "cifspassword" = "************" 
							          + "location"     = "\\\\tacg-dc.the-austrian-citrix-guy.at\\_sw" 
							          + "type"         = "cifs" 
							          + "username"     = "************@the-austrian-citrix-guy.at" 
							          + "vers"         = "1.0" 
							        } 
							      + host             = (known after apply) 
							      + id               = (known after apply) 
							      + name_description = "ISO repository on TACG-DC" 
							      + name_label       = "ISO on TACG-DC" 
							      + shared           = true 
							      + sm_config        = { 
							          + "iso_type" = "cifs" 
							        } 
							      + type             = "iso" 
							      + uuid             = (known after apply) 
							    } 
							  
						 

						
							  # xenserver_vm.vm["windows-vm"] will be created 
							  + resource "xenserver_vm" "vm" { 
							      + boot_mode         = (known after apply) 
							      + boot_order        = (known after apply) 
							      + cdrom             = "Citrix_Virtual_Apps_and_Desktops_7_2402_LTSR_CU1_1100.iso" 
							      + check_ip_timeout  = 0 
							      + cores_per_socket  = 2 
							      + default_ip        = (known after apply) 
							      + dynamic_mem_max   = (known after apply) 
							      + dynamic_mem_min   = (known after apply) 
							      + hard_drive        = (known after apply) 
							      + id                = (known after apply) 
							      + name_description  = "" 
							      + name_label        = "TACG-XS-DDC1" 
							      + network_interface = [ 
							          + { 
							              + device       = "0" 
							              + mac          = (known after apply) 
							              + network_uuid = "14c664c3-1d69-5baa-6e51-21f7059aec3a" 
							              + other_config = (known after apply) 
							              + vif_ref      = (known after apply) 
							            }, 
							        ] 
							      + other_config      = {} 
							      + static_mem_max    = 8589934592 
							      + static_mem_min    = (known after apply) 
							      + template_name     = "TMPL-W2K22-WinRM" 
							      + uuid              = (known after apply) 
							      + vcpus             = 2 
							    } 
							 
							  # xenserver_vm.vm["windows-vm1"] will be created 
							  + resource "xenserver_vm" "vm" { 
							      + boot_mode         = (known after apply) 
							      + boot_order        = (known after apply) 
							      + cdrom             = "Citrix_Virtual_Apps_and_Desktops_7_2402_LTSR_CU1_1100.iso" 
							      + check_ip_timeout  = 0 
							      + cores_per_socket  = 2 
							      + default_ip        = (known after apply) 
							      + dynamic_mem_max   = (known after apply) 
							      + dynamic_mem_min   = (known after apply) 
							      + hard_drive        = (known after apply) 
							      + id                = (known after apply) 
							      + name_description  = "" 
							      + name_label        = "TACG-XS-DDC2" 
							      + network_interface = [ 
							          + { 
							              + device       = "0" 
							              + mac          = (known after apply) 
							              + network_uuid = "14c664c3-1d69-5baa-6e51-21f7059aec3a" 
							              + other_config = (known after apply) 
							              + vif_ref      = (known after apply) 
							            }, 
							        ] 
							      + other_config      = {} 
							      + static_mem_max    = 8589934592 
							      + static_mem_min    = (known after apply) 
							      + template_name     = "TMPL-W2K22-WinRM" 
							      + uuid              = (known after apply) 
							      + vcpus             = 2 
							    } 
							 
							  # xenserver_vm.vm["windows-vm2"] will be created 
							  + resource "xenserver_vm" "vm" { 
							      + boot_mode         = (known after apply) 
							      + boot_order        = (known after apply) 
							      + cdrom             = "Citrix_Virtual_Apps_and_Desktops_7_2402_LTSR_CU1_1100.iso" 
							      + check_ip_timeout  = 0 
							      + cores_per_socket  = 2 
							      + default_ip        = (known after apply) 
							      + dynamic_mem_max   = (known after apply) 
							      + dynamic_mem_min   = (known after apply) 
							      + hard_drive        = (known after apply) 
							      + id                = (known after apply) 
							      + name_description  = "" 
							      + name_label        = "TACG-XS-CVADAVM" 
							      + network_interface = [ 
							          + { 
							              + device       = "0" 
							              + mac          = (known after apply) 
							              + network_uuid = "14c664c3-1d69-5baa-6e51-21f7059aec3a" 
							              + other_config = (known after apply) 
							              + vif_ref      = (known after apply) 
							            }, 
							        ] 
							      + other_config      = {} 
							      + static_mem_max    = 8589934592 
							      + static_mem_min    = (known after apply) 
							      + template_name     = "TMPL-W2K22-WinRM" 
							      + uuid              = (known after apply) 
							      + vcpus             = 2 
							    } 
							 
							Plan: 4 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 
							 
							xenserver_sr.local: Creating... 
							xenserver_sr.local: Creation complete after 1s [id=3efad889-3928-09be-a321-9097264fed21] 
							xenserver_vm.vm["windows-vm"]: Creating... 
							xenserver_vm.vm["windows-vm1"]: Creating... 
							xenserver_vm.vm["windows-vm2"]: Creating... 
							xenserver_vm.vm["windows-vm1"]: Creation complete after 1s [id=46826138-236c-d683-73ba-5706dae7a4ee] 
							xenserver_vm.vm["windows-vm"]: Still creating... [10s elapsed] 
							xenserver_vm.vm["windows-vm2"]: Still creating... [10s elapsed] 
							xenserver_vm.vm["windows-vm"]: Creation complete after 14s [id=44bc72a7-0ce9-0c6c-442d-e5281a1c3814] 
							xenserver_vm.vm["windows-vm2"]: Creation complete after 19s [id=cfd97b84-1b3a-1454-afa4-95c64d6b9bd7] 
							 
							Apply complete! Resources: 4 added, 0 changed, 0 destroyed. 
							 
							Outputs: 
							 
							vm_out = { 
							  "TACG-XS-CVADAVM" = { 
							    "boot_mode" = "bios" 
							    "boot_order" = "cd" 
							    "cdrom" = "Citrix_Virtual_Apps_and_Desktops_7_2402_LTSR_CU1_1100.iso" 
							    "check_ip_timeout" = 0 
							    "cores_per_socket" = 2 
							    "default_ip" = "" 
							    "dynamic_mem_max" = 8589934592 
							    "dynamic_mem_min" = 8589934592 
							    "hard_drive" = toset(null) /* of object */ 
							    "id" = "cfd97b84-1b3a-1454-afa4-95c64d6b9bd7" 
							    "name_description" = "" 
							    "name_label" = "TACG-XS-CVADAVM" 
							    "network_interface" = toset([ 
							      { 
							        "device" = "0" 
							        "mac" = "d2:12:40:7c:ae:1b" 
							        "network_uuid" = "14c664c3-1d69-5baa-6e51-21f7059aec3a" 
							        "other_config" = tomap({}) 
							        "vif_ref" = "OpaqueRef:e5f7b521-54a2-3a25-b7f3-29f5844ef148" 
							      }, 
							    ]) 
							    "other_config" = tomap({}) 
							    "static_mem_max" = 8589934592 
							    "static_mem_min" = 8589934592 
							    "template_name" = "TMPL-W2K22-WinRM" 
							    "uuid" = "cfd97b84-1b3a-1454-afa4-95c64d6b9bd7" 
							    "vcpus" = 2 
							  } 
							  "TACG-XS-DDC1" = { 
							    "boot_mode" = "bios" 
							    "boot_order" = "cd" 
							    "cdrom" = "Citrix_Virtual_Apps_and_Desktops_7_2402_LTSR_CU1_1100.iso" 
							    "check_ip_timeout" = 0 
							    "cores_per_socket" = 2 
							    "default_ip" = "" 
							    "dynamic_mem_max" = 8589934592 
							    "dynamic_mem_min" = 8589934592 
							    "hard_drive" = toset(null) /* of object */ 
							    "id" = "44bc72a7-0ce9-0c6c-442d-e5281a1c3814" 
							    "name_description" = "" 
							    "name_label" = "TACG-XS-DDC1" 
							    "network_interface" = toset([ 
							      { 
							        "device" = "0" 
							        "mac" = "d6:57:b5:9e:8a:46" 
							        "network_uuid" = "14c664c3-1d69-5baa-6e51-21f7059aec3a" 
							        "other_config" = tomap({}) 
							        "vif_ref" = "OpaqueRef:f9f7e8f8-0da2-209a-9571-78567d640395" 
							      }, 
							    ]) 
							    "other_config" = tomap({}) 
							    "static_mem_max" = 8589934592 
							    "static_mem_min" = 8589934592 
							    "template_name" = "TMPL-W2K22-WinRM" 
							    "uuid" = "44bc72a7-0ce9-0c6c-442d-e5281a1c3814" 
							    "vcpus" = 2 
							  } 
							  "TACG-XS-DDC2" = { 
							    "boot_mode" = "bios" 
							    "boot_order" = "cd" 
							    "cdrom" = "Citrix_Virtual_Apps_and_Desktops_7_2402_LTSR_CU1_1100.iso" 
							    "check_ip_timeout" = 0 
							    "cores_per_socket" = 2 
							    "default_ip" = "" 
							    "dynamic_mem_max" = 8589934592 
							    "dynamic_mem_min" = 8589934592 
							    "hard_drive" = toset(null) /* of object */ 
							    "id" = "46826138-236c-d683-73ba-5706dae7a4ee" 
							    "name_description" = "" 
							    "name_label" = "TACG-XS-DDC2" 
							    "network_interface" = toset([ 
							      { 
							        "device" = "0" 
							        "mac" = "c2:4d:eb:72:c8:5d" 
							        "network_uuid" = "14c664c3-1d69-5baa-6e51-21f7059aec3a" 
							        "other_config" = tomap({}) 
							        "vif_ref" = "OpaqueRef:14701b3b-5b60-5e30-63f6-c73a9ba2fea1" 
							      }, 
							    ]) 
							    "other_config" = tomap({}) 
							    "static_mem_max" = 8589934592 
							    "static_mem_min" = 8589934592 
							    "template_name" = "TMPL-W2K22-WinRM" 
							    "uuid" = "46826138-236c-d683-73ba-5706dae7a4ee" 
							    "vcpus" = 2 
							  } 
							} 
							PS C:\_TACG\_CVADOnXenServer\_CVADOnXenServer-Creation&gt;
						 
					
				
			
		
	

	
		XenCenter shows the created and running VMs: 
		
	 

	
		The Terraform flow was completed successfully, and the next module can begin.
	 

	
		Module 2: Install and Configure all Resources needed on XenServer 8
	

	
		Due to the very early version of the Terraform provider for XenServer, the following essential steps must be done by calling PowerShell scripts on the 3 previously created VMs using WinRM as the Terraform provider for XenServer 8 is not able to fulfill these:
	 

	
		
			
				Rename the VM names to meet the AD requirements
			 
		
		
			
				Set the correct IP addresses
			 
		
		
			
				Add the 3 VMs to the Domain
			 
		
	

	
		In the example below, the IP addresses during VM creation were the DHCP addresses 10.10.11.122-12, the VMs had not the needed names, and they were not part of the Domain: 
		
	 

	
		 
	 

	
		Important: 
		 
		Before starting the Terraform workflow, please ensure you have configured the variables according to your needs using the corresponding .auto.tfvars.json and/or terraform.tfvars file. 
		 
	 

	
		The configuration can be started by following the standard Terraform workflow:
	 

	
		terraform init, 
		terraform plan 
		 
	 

	
		and if no errors occur
	 

	
		terraform apply
	 

	
		
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}		
	

	
		 
	 

	
		
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}		
		
			
				
					
						
							PS C:\TACG\_CVADOnXenServer\_CVADOnXenServer-InitialConfiguration&gt; terraform init 
							Initializing the backend... 
							Initializing provider plugins... 
							- Finding latest version of hashicorp/null... 
							- Finding latest version of hashicorp/time... 
							- Finding latest version of hashicorp/local... 
							- Finding latest version of xenserver/xenserver... 
							- Installing hashicorp/local v2.5.1... 
							- Installed hashicorp/local v2.5.1 (signed by HashiCorp) 
							- Installing xenserver/xenserver v0.1.1... 
							- Installed xenserver/xenserver v0.1.1 (self-signed, key ID FABD99A0D7648275) 
							- Installing hashicorp/null v3.2.2... 
							- Installed hashicorp/null v3.2.2 (signed by HashiCorp) 
							- Installing hashicorp/time v0.12.0... 
							- Installed hashicorp/time v0.12.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\_CVADOnXenServer\_CVADOnXenServer-InitialConfiguration&gt; terraform plan 
							data.xenserver_network.network: Reading... 
							data.xenserver_sr.sr: Reading... 
							data.xenserver_vm.vm_data: Reading... 
							data.xenserver_sr.sr: Read complete after 0s 
							data.xenserver_network.network: Read complete after 0s 
							data.xenserver_vm.vm_data: Read complete after 1s 
							 
							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.WriteAddToDomainScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteAddToDomainScriptIntoDataDirectory" { 
							      + 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             = "./data/AddToDomain-Script.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WriteCVADAVMIPScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteCVADAVMIPScriptIntoDataDirectory" { 
							      + 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             = "./data/CVADAVM-IP-Script.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WriteDDC1IPScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteDDC1IPScriptIntoDataDirectory" { 
							      + 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             = "./data/DDC1-IP-Script.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WriteDDC2IPScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteDDC2IPScriptIntoDataDirectory" { 
							      + 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             = "./data/DDC2-IP-Script.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WriteInstallationScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteInstallationScriptIntoDataDirectory" { 
							      + content              = &lt;&lt;-EOT 
							            New-Item -Path C:/TEMP/XDINST -ItemType Directory 
							        EOT 
							      + 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             = "./data/InstallationLogPath-Script.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WriteRebootScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteRebootScriptIntoDataDirectory" { 
							      + content              = &lt;&lt;-EOT 
							            Restart-Computer -Force 
							        EOT 
							      + 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             = "./data/Reboot-Script.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WriteRenameComputerCVADAVMScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteRenameComputerCVADAVMScriptIntoDataDirectory" { 
							      + 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             = "./data/RenameComputer-CVADAVM-Script.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WriteRenameComputerDDC1ScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteRenameComputerDDC1ScriptIntoDataDirectory" { 
							      + 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             = "./data/RenameComputer-DDC1-Script.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WriteRenameComputerDDC2ScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteRenameComputerDDC2ScriptIntoDataDirectory" { 
							      + 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             = "./data/RenameComputer-DDC2-Script.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # null_resource.UploadAndExecuteChangeIPScript-CVADAVM will be created 
							  + resource "null_resource" "UploadAndExecuteChangeIPScript-CVADAVM" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.UploadAndExecuteChangeIPScript-DDC1 will be created 
							  + resource "null_resource" "UploadAndExecuteChangeIPScript-DDC1" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.UploadAndExecuteChangeIPScript-DDC2 will be created 
							  + resource "null_resource" "UploadAndExecuteChangeIPScript-DDC2" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.UploadAndExecuteRenameScripts-CVADAVM will be created 
							  + resource "null_resource" "UploadAndExecuteRenameScripts-CVADAVM" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.UploadAndExecuteRenameScripts-DDC1 will be created 
							  + resource "null_resource" "UploadAndExecuteRenameScripts-DDC1" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.UploadAndExecuteRenameScripts-DDC2 will be created 
							  + resource "null_resource" "UploadAndExecuteRenameScripts-DDC2" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.UploadAndExecuteScripts-CVADAVM will be created 
							  + resource "null_resource" "UploadAndExecuteScripts-CVADAVM" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.UploadAndExecuteScripts-DDC1 will be created 
							  + resource "null_resource" "UploadAndExecuteScripts-DDC1" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.UploadAndExecuteScripts-DDC2 will be created 
							  + resource "null_resource" "UploadAndExecuteScripts-DDC2" { 
							      + id = (known after apply) 
							    } 
							 
							  # time_sleep.WaitToRebootCVADAVMAfterIPChange will be created 
							  + resource "time_sleep" "WaitToRebootCVADAVMAfterIPChange" { 
							      + create_duration = "60s" 
							      + id              = (known after apply) 
							    } 
							 
							  # time_sleep.WaitToRebootCVADAVMAfterRename will be created 
							  + resource "time_sleep" "WaitToRebootCVADAVMAfterRename" { 
							      + create_duration = "60s" 
							      + id              = (known after apply) 
							    } 
							 
							  # time_sleep.WaitToRebootDDC1AfterIPChange will be created 
							  + resource "time_sleep" "WaitToRebootDDC1AfterIPChange" { 
							      + create_duration = "60s" 
							      + id              = (known after apply) 
							    } 
							 
							  # time_sleep.WaitToRebootDDC1AfterRename will be created 
							  + resource "time_sleep" "WaitToRebootDDC1AfterRename" { 
							      + create_duration = "60s" 
							      + id              = (known after apply) 
							    } 
							 
							  # time_sleep.WaitToRebootDDC2AfterIPChange will be created 
							  + resource "time_sleep" "WaitToRebootDDC2AfterIPChange" { 
							      + create_duration = "60s" 
							      + id              = (known after apply) 
							    } 
							 
							  # time_sleep.WaitToRebootDDC2AfterRename will be created 
							  + resource "time_sleep" "WaitToRebootDDC2AfterRename" { 
							      + create_duration = "60s" 
							      + id              = (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\_CVADOnXenServer\_CVADOnXenServer-InitialConfiguration&gt; terraform validate 
							Success! The configuration is valid. 
							 
							PS C:\_TACG\_CVADOnXenServer\_CVADOnXenServer-InitialConfiguration&gt; terraform apply 
							data.xenserver_sr.sr: Reading... 
							data.xenserver_vm.vm_data: Reading... 
							data.xenserver_network.network: Reading... 
							data.xenserver_sr.sr: Read complete after 0s 
							data.xenserver_network.network: Read complete after 0s 
							data.xenserver_vm.vm_data: Read complete after 0s 
							 
							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.WriteAddToDomainScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteAddToDomainScriptIntoDataDirectory" { 
							      + 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             = "./data/AddToDomain-Script.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WriteCVADAVMIPScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteCVADAVMIPScriptIntoDataDirectory" { 
							      + 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             = "./data/CVADAVM-IP-Script.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WriteDDC1IPScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteDDC1IPScriptIntoDataDirectory" { 
							      + 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             = "./data/DDC1-IP-Script.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WriteDDC2IPScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteDDC2IPScriptIntoDataDirectory" { 
							      + 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             = "./data/DDC2-IP-Script.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WriteInstallationScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteInstallationScriptIntoDataDirectory" { 
							      + content              = &lt;&lt;-EOT 
							            New-Item -Path C:/TEMP/XDINST -ItemType Directory 
							        EOT 
							      + 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             = "./data/InstallationLogPath-Script.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WriteRebootScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteRebootScriptIntoDataDirectory" { 
							      + content              = &lt;&lt;-EOT 
							            Restart-Computer -Force 
							        EOT 
							      + 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             = "./data/Reboot-Script.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WriteRenameComputerCVADAVMScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteRenameComputerCVADAVMScriptIntoDataDirectory" { 
							      + 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             = "./data/RenameComputer-CVADAVM-Script.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WriteRenameComputerDDC1ScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteRenameComputerDDC1ScriptIntoDataDirectory" { 
							      + 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             = "./data/RenameComputer-DDC1-Script.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WriteRenameComputerDDC2ScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteRenameComputerDDC2ScriptIntoDataDirectory" { 
							      + 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             = "./data/RenameComputer-DDC2-Script.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # null_resource.UploadAndExecuteChangeIPScript-CVADAVM will be created 
							  + resource "null_resource" "UploadAndExecuteChangeIPScript-CVADAVM" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.UploadAndExecuteChangeIPScript-DDC1 will be created 
							  + resource "null_resource" "UploadAndExecuteChangeIPScript-DDC1" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.UploadAndExecuteChangeIPScript-DDC2 will be created 
							  + resource "null_resource" "UploadAndExecuteChangeIPScript-DDC2" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.UploadAndExecuteRenameScripts-CVADAVM will be created 
							  + resource "null_resource" "UploadAndExecuteRenameScripts-CVADAVM" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.UploadAndExecuteRenameScripts-DDC1 will be created 
							  + resource "null_resource" "UploadAndExecuteRenameScripts-DDC1" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.UploadAndExecuteRenameScripts-DDC2 will be created 
							  + resource "null_resource" "UploadAndExecuteRenameScripts-DDC2" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.UploadAndExecuteScripts-CVADAVM will be created 
							  + resource "null_resource" "UploadAndExecuteScripts-CVADAVM" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.UploadAndExecuteScripts-DDC1 will be created 
							  + resource "null_resource" "UploadAndExecuteScripts-DDC1" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.UploadAndExecuteScripts-DDC2 will be created 
							  + resource "null_resource" "UploadAndExecuteScripts-DDC2" { 
							      + id = (known after apply) 
							    } 
							 
							  # time_sleep.WaitToRebootCVADAVMAfterIPChange will be created 
							  + resource "time_sleep" "WaitToRebootCVADAVMAfterIPChange" { 
							      + create_duration = "60s" 
							      + id              = (known after apply) 
							    } 
							 
							  # time_sleep.WaitToRebootCVADAVMAfterRename will be created 
							  + resource "time_sleep" "WaitToRebootCVADAVMAfterRename" { 
							      + create_duration = "60s" 
							      + id              = (known after apply) 
							    } 
							 
							  # time_sleep.WaitToRebootDDC1AfterIPChange will be created 
							  + resource "time_sleep" "WaitToRebootDDC1AfterIPChange" { 
							      + create_duration = "60s" 
							      + id              = (known after apply) 
							    } 
							 
							  # time_sleep.WaitToRebootDDC1AfterRename will be created 
							  + resource "time_sleep" "WaitToRebootDDC1AfterRename" { 
							      + create_duration = "60s" 
							      + id              = (known after apply) 
							    } 
							 
							  # time_sleep.WaitToRebootDDC2AfterIPChange will be created 
							  + resource "time_sleep" "WaitToRebootDDC2AfterIPChange" { 
							      + create_duration = "60s" 
							      + id              = (known after apply) 
							    } 
							 
							  # time_sleep.WaitToRebootDDC2AfterRename will be created 
							  + resource "time_sleep" "WaitToRebootDDC2AfterRename" { 
							      + create_duration = "60s" 
							      + id              = (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 
							 
							local_file.WriteRenameComputerDDC2ScriptIntoDataDirectory: Creating... 
							local_file.WriteAddToDomainScriptIntoDataDirectory: Creating... 
							local_file.WriteRenameComputerCVADAVMScriptIntoDataDirectory: Creating... 
							local_file.WriteInstallationScriptIntoDataDirectory: Creating... 
							local_file.WriteAddToDomainScriptIntoDataDirectory: Creation complete after 0s [id=d721c2c85f1b55f718484607eb4d908cd4117a89] 
							local_file.WriteRenameComputerDDC1ScriptIntoDataDirectory: Creating... 
							local_file.WriteDDC1IPScriptIntoDataDirectory: Creating... 
							local_file.WriteDDC2IPScriptIntoDataDirectory: Creating... 
							local_file.WriteRenameComputerDDC2ScriptIntoDataDirectory: Creation complete after 0s [id=e45f52cc5833ef6c95522d1dc76db06122b55325] 
							local_file.WriteRebootScriptIntoDataDirectory: Creating... 
							local_file.WriteCVADAVMIPScriptIntoDataDirectory: Creating... 
							local_file.WriteRenameComputerCVADAVMScriptIntoDataDirectory: Creation complete after 0s [id=05d0c11c3ed7b59e6f2f6b6477ac13efc1e0d745] 
							local_file.WriteInstallationScriptIntoDataDirectory: Creation complete after 1s [id=fc4ebc0269b8b4100c831a23c05d1851387a1259] 
							local_file.WriteDDC1IPScriptIntoDataDirectory: Creation complete after 1s [id=f806f929a4626994fe68e3d251c18052396d7787] 
							local_file.WriteRenameComputerDDC1ScriptIntoDataDirectory: Creation complete after 1s [id=29a62f39046e41393a3c0b33998a54e42e348122] 
							local_file.WriteDDC2IPScriptIntoDataDirectory: Creation complete after 1s [id=bb7c3a8db9c945a57450d43836d6da1b50998e11] 
							local_file.WriteRebootScriptIntoDataDirectory: Creation complete after 1s [id=2baf7fa209ad203f96017f90f5727642e5f44a03] 
							local_file.WriteCVADAVMIPScriptIntoDataDirectory: Creation complete after 1s [id=fa26e26dd3191bc0f9ba8ffe9622c847b327e881] 
							null_resource.UploadAndExecuteScripts-DDC2: Creating... 
							null_resource.UploadAndExecuteScripts-CVADAVM: Creating... 
							null_resource.UploadAndExecuteScripts-DDC1: Creating... 
							null_resource.UploadAndExecuteScripts-DDC1: Provisioning with 'file'... 
							null_resource.UploadAndExecuteScripts-DDC2: Provisioning with 'file'... 
							null_resource.UploadAndExecuteScripts-CVADAVM: Provisioning with 'file'... 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadAndExecuteScripts-CVADAVM: Provisioning with 'remote-exec'... 
							null_resource.UploadAndExecuteScripts-CVADAVM (remote-exec): Connecting to remote host via WinRM... 
							null_resource.UploadAndExecuteScripts-CVADAVM (remote-exec):   Host: 10.10.11.130 
							null_resource.UploadAndExecuteScripts-CVADAVM (remote-exec):   Port: 5985 
							null_resource.UploadAndExecuteScripts-CVADAVM (remote-exec):   User: administrator 
							null_resource.UploadAndExecuteScripts-CVADAVM (remote-exec):   Password: true 
							null_resource.UploadAndExecuteScripts-CVADAVM (remote-exec):   HTTPS: false 
							null_resource.UploadAndExecuteScripts-CVADAVM (remote-exec):   Insecure: false 
							null_resource.UploadAndExecuteScripts-CVADAVM (remote-exec):   NTLM: false 
							null_resource.UploadAndExecuteScripts-CVADAVM (remote-exec):   CACert: false 
							null_resource.UploadAndExecuteScripts-CVADAVM (remote-exec): Connected! 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadAndExecuteScripts-DDC1: Provisioning with 'remote-exec'... 
							null_resource.UploadAndExecuteScripts-DDC1 (remote-exec): Connecting to remote host via WinRM... 
							null_resource.UploadAndExecuteScripts-DDC1 (remote-exec):   Host: 10.10.11.129 
							null_resource.UploadAndExecuteScripts-DDC1 (remote-exec):   Port: 5985 
							null_resource.UploadAndExecuteScripts-DDC1 (remote-exec):   User: administrator 
							null_resource.UploadAndExecuteScripts-DDC1 (remote-exec):   Password: true 
							null_resource.UploadAndExecuteScripts-DDC1 (remote-exec):   HTTPS: false 
							null_resource.UploadAndExecuteScripts-DDC1 (remote-exec):   Insecure: false 
							null_resource.UploadAndExecuteScripts-DDC1 (remote-exec):   NTLM: false 
							null_resource.UploadAndExecuteScripts-DDC1 (remote-exec):   CACert: false 
							null_resource.UploadAndExecuteScripts-DDC1 (remote-exec): Connected! 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
							null_resource.UploadAndExecuteScripts-CVADAVM (remote-exec): C:\Users\Administrator&gt;powershell -File C:/temp/InstallationLogPath-Script.ps1 
							 
							 
							null_resource.UploadAndExecuteScripts-CVADAVM (remote-exec):     Directory: C:\TEMP 
							 
							 
							null_resource.UploadAndExecuteScripts-CVADAVM (remote-exec): Mode                 LastWriteTime         Length Name 
							null_resource.UploadAndExecuteScripts-CVADAVM (remote-exec): ----                 -------------         ------ ---- 
							null_resource.UploadAndExecuteScripts-CVADAVM (remote-exec): d-----        04.09.2024     15:03                XDINST 
							 
							 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
							null_resource.UploadAndExecuteScripts-DDC1 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/temp/InstallationLogPath-Script.ps1 
							 
							 
							null_resource.UploadAndExecuteScripts-DDC1 (remote-exec):     Directory: C:\TEMP 
							 
							 
							null_resource.UploadAndExecuteScripts-DDC1 (remote-exec): Mode                 LastWriteTime         Length Name 
							null_resource.UploadAndExecuteScripts-DDC1 (remote-exec): ----                 -------------         ------ ---- 
							null_resource.UploadAndExecuteScripts-DDC1 (remote-exec): d-----        04.09.2024     15:03                XDINST 
							 
							 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadAndExecuteScripts-CVADAVM: Creation complete after 2s [id=2757650365038078952] 
							null_resource.UploadAndExecuteRenameScripts-CVADAVM: Creating... 
							null_resource.UploadAndExecuteRenameScripts-CVADAVM: Provisioning with 'file'... 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadAndExecuteScripts-DDC1: Creation complete after 2s [id=6398287523030180872] 
							null_resource.UploadAndExecuteRenameScripts-DDC1: Creating... 
							null_resource.UploadAndExecuteRenameScripts-DDC1: Provisioning with 'file'... 
							time_sleep.WaitToRebootDDC1AfterRename: Creating... 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadAndExecuteRenameScripts-CVADAVM: Provisioning with 'remote-exec'... 
							null_resource.UploadAndExecuteRenameScripts-CVADAVM (remote-exec): Connecting to remote host via WinRM... 
							null_resource.UploadAndExecuteRenameScripts-CVADAVM (remote-exec):   Host: 10.10.11.130 
							null_resource.UploadAndExecuteRenameScripts-CVADAVM (remote-exec):   Port: 5985 
							null_resource.UploadAndExecuteRenameScripts-CVADAVM (remote-exec):   User: administrator 
							null_resource.UploadAndExecuteRenameScripts-CVADAVM (remote-exec):   Password: true 
							null_resource.UploadAndExecuteRenameScripts-CVADAVM (remote-exec):   HTTPS: false 
							null_resource.UploadAndExecuteRenameScripts-CVADAVM (remote-exec):   Insecure: false 
							null_resource.UploadAndExecuteRenameScripts-CVADAVM (remote-exec):   NTLM: false 
							null_resource.UploadAndExecuteRenameScripts-CVADAVM (remote-exec):   CACert: false 
							null_resource.UploadAndExecuteRenameScripts-CVADAVM (remote-exec): Connected! 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadAndExecuteRenameScripts-DDC1: Provisioning with 'remote-exec'... 
							null_resource.UploadAndExecuteRenameScripts-DDC1 (remote-exec): Connecting to remote host via WinRM... 
							null_resource.UploadAndExecuteRenameScripts-DDC1 (remote-exec):   Host: 10.10.11.129 
							null_resource.UploadAndExecuteRenameScripts-DDC1 (remote-exec):   Port: 5985 
							null_resource.UploadAndExecuteRenameScripts-DDC1 (remote-exec):   User: administrator 
							null_resource.UploadAndExecuteRenameScripts-DDC1 (remote-exec):   Password: true 
							null_resource.UploadAndExecuteRenameScripts-DDC1 (remote-exec):   HTTPS: false 
							null_resource.UploadAndExecuteRenameScripts-DDC1 (remote-exec):   Insecure: false 
							null_resource.UploadAndExecuteRenameScripts-DDC1 (remote-exec):   NTLM: false 
							null_resource.UploadAndExecuteRenameScripts-DDC1 (remote-exec):   CACert: false 
							null_resource.UploadAndExecuteRenameScripts-DDC1 (remote-exec): Connected! 
							 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadAndExecuteRenameScripts-CVADAVM (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/RenameComputer-CVADAVM-Script.ps1 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
							null_resource.UploadAndExecuteRenameScripts-DDC1 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/RenameComputer-DDC1-Script.ps1 
							null_resource.UploadAndExecuteRenameScripts-CVADAVM: Creation complete after 2s [id=6103142682856145408] 
							time_sleep.WaitToRebootCVADAVMAfterRename: Creating... 
							null_resource.UploadAndExecuteRenameScripts-DDC1: Creation complete after 3s [id=5450718398756922027] 
							null_resource.UploadAndExecuteScripts-DDC2: Still creating... [10s elapsed] 
							time_sleep.WaitToRebootDDC1AfterRename: Still creating... [10s elapsed] 
							time_sleep.WaitToRebootCVADAVMAfterRename: Still creating... [10s elapsed] 
							null_resource.UploadAndExecuteScripts-DDC2: Still creating... [20s elapsed] 
							time_sleep.WaitToRebootDDC1AfterRename: Still creating... [20s elapsed] 
							time_sleep.WaitToRebootCVADAVMAfterRename: Still creating... [20s elapsed] 
							null_resource.UploadAndExecuteScripts-DDC2: Still creating... [30s elapsed] 
							time_sleep.WaitToRebootDDC1AfterRename: Still creating... [30s elapsed] 
							time_sleep.WaitToRebootCVADAVMAfterRename: Still creating... [30s elapsed] 
							time_sleep.WaitToRebootDDC1AfterRename: Still creating... [40s elapsed] 
							time_sleep.WaitToRebootCVADAVMAfterRename: Still creating... [40s elapsed] 
							time_sleep.WaitToRebootDDC1AfterRename: Still creating... [50s elapsed] 
							time_sleep.WaitToRebootCVADAVMAfterRename: Still creating... [50s elapsed] 
							time_sleep.WaitToRebootDDC1AfterRename: Creation complete after 1m0s [id=2024-09-04T13:04:54Z] 
							null_resource.UploadAndExecuteChangeIPScript-DDC1: Creating... 
							null_resource.UploadAndExecuteChangeIPScript-DDC1: Provisioning with 'file'... 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadAndExecuteChangeIPScript-DDC1: Provisioning with 'remote-exec'... 
							null_resource.UploadAndExecuteChangeIPScript-DDC1 (remote-exec): Connecting to remote host via WinRM... 
							null_resource.UploadAndExecuteChangeIPScript-DDC1 (remote-exec):   Host: 10.10.11.129 
							null_resource.UploadAndExecuteChangeIPScript-DDC1 (remote-exec):   Port: 5985 
							null_resource.UploadAndExecuteChangeIPScript-DDC1 (remote-exec):   User: administrator 
							null_resource.UploadAndExecuteChangeIPScript-DDC1 (remote-exec):   Password: true 
							null_resource.UploadAndExecuteChangeIPScript-DDC1 (remote-exec):   HTTPS: false 
							null_resource.UploadAndExecuteChangeIPScript-DDC1 (remote-exec):   Insecure: false 
							null_resource.UploadAndExecuteChangeIPScript-DDC1 (remote-exec):   NTLM: false 
							null_resource.UploadAndExecuteChangeIPScript-DDC1 (remote-exec):   CACert: false 
							null_resource.UploadAndExecuteChangeIPScript-DDC1 (remote-exec): Connected! 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;time_sleep.WaitToRebootCVADAVMAfterRename: Creation complete after 1m0s [id=2024-09-04T13:04:56Z] 
							null_resource.UploadAndExecuteChangeIPScript-CVADAVM: Creating... 
							null_resource.UploadAndExecuteChangeIPScript-CVADAVM: Provisioning with 'file'... 
							 
							null_resource.UploadAndExecuteChangeIPScript-DDC1 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/DDC1-IP-Script.ps1 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadAndExecuteChangeIPScript-CVADAVM: Provisioning with 'remote-exec'... 
							null_resource.UploadAndExecuteChangeIPScript-CVADAVM (remote-exec): Connecting to remote host via WinRM... 
							null_resource.UploadAndExecuteChangeIPScript-CVADAVM (remote-exec):   Host: 10.10.11.130 
							null_resource.UploadAndExecuteChangeIPScript-CVADAVM (remote-exec):   Port: 5985 
							null_resource.UploadAndExecuteChangeIPScript-CVADAVM (remote-exec):   User: administrator 
							null_resource.UploadAndExecuteChangeIPScript-CVADAVM (remote-exec):   Password: true 
							null_resource.UploadAndExecuteChangeIPScript-CVADAVM (remote-exec):   HTTPS: false 
							null_resource.UploadAndExecuteChangeIPScript-CVADAVM (remote-exec):   Insecure: false 
							null_resource.UploadAndExecuteChangeIPScript-CVADAVM (remote-exec):   NTLM: false 
							null_resource.UploadAndExecuteChangeIPScript-CVADAVM (remote-exec):   CACert: false 
							null_resource.UploadAndExecuteChangeIPScript-CVADAVM (remote-exec): Connected! 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
							null_resource.UploadAndExecuteChangeIPScript-CVADAVM (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/CVADAVM-IP-Script.ps1 
							null_resource.UploadAndExecuteChangeIPScript-DDC1: Still creating... [10s elapsed] 
							null_resource.UploadAndExecuteChangeIPScript-CVADAVM: Still creating... [10s elapsed] 
							null_resource.UploadAndExecuteChangeIPScript-DDC1: Still creating... [20s elapsed] 
							null_resource.UploadAndExecuteChangeIPScript-CVADAVM: Still creating... [20s elapsed] 
							null_resource.UploadAndExecuteChangeIPScript-DDC1: Still creating... [30s elapsed] 
							null_resource.UploadAndExecuteChangeIPScript-CVADAVM: Still creating... [30s elapsed] 
							null_resource.UploadAndExecuteChangeIPScript-DDC1: Still creating... [40s elapsed] 
							null_resource.UploadAndExecuteChangeIPScript-CVADAVM: Still creating... [40s elapsed] 
							null_resource.UploadAndExecuteChangeIPScript-DDC1: Still creating... [50s elapsed] 
							null_resource.UploadAndExecuteChangeIPScript-CVADAVM: Still creating... [50s elapsed] 
							null_resource.UploadAndExecuteChangeIPScript-DDC1: Still creating... [1m0s elapsed] 
							null_resource.UploadAndExecuteChangeIPScript-CVADAVM: Still creating... [1m0s elapsed] 
							null_resource.UploadAndExecuteChangeIPScript-DDC1: Still creating... [1m10s elapsed] 
							null_resource.UploadAndExecuteChangeIPScript-CVADAVM: Still creating... [1m10s elapsed] 
							╷ 
							│ Error: file provisioner error 
							│ 
							│   with null_resource.UploadAndExecuteScripts-DDC2, 
							│   on CVADOnXenServer-InitialConfiguration.tf line 235, in resource "null_resource" "UploadAndExecuteScripts-DDC2": 
							│  235:   provisioner "file" { 
							│ 
							│ timeout - last error: unknown error Post "http://10.10.11.123:5985/wsman": dial tcp 10.10.11.123:5985: connectex: A connection attempt failed because the connected party did not properly respond 
							│ after a period of time, or established connection failed because connected host has failed to respond. 
							╵ 
							╷ 
							│ Error: remote-exec provisioner error 
							│ 
							│   with null_resource.UploadAndExecuteChangeIPScript-DDC1, 
							│   on CVADOnXenServer-InitialConfiguration.tf line 399, in resource "null_resource" "UploadAndExecuteChangeIPScript-DDC1": 
							│  399:  provisioner "remote-exec" { 
							│ 
							│ error executing "C:/Temp/terraform_808752596.cmd": unknown error Post "http://10.10.11.129:5985/wsman": net/http: timeout awaiting response headers 
							╵ 
							╷ 
							│ Error: remote-exec provisioner error 
							│ 
							│   with null_resource.UploadAndExecuteChangeIPScript-CVADAVM, 
							│   on CVADOnXenServer-InitialConfiguration.tf line 455, in resource "null_resource" "UploadAndExecuteChangeIPScript-CVADAVM": 
							│  455:  provisioner "remote-exec" { 
							│ 
							│ error executing "C:/Temp/terraform_2139075182.cmd": unknown error Post "http://10.10.11.130:5985/wsman": net/http: timeout awaiting response headers 
							╵ 
							PS D:\_PPMM\__TF\_CVADOnXenServer\_CVADOnXenServer-InitialConfiguration&gt;
						 
					
				
			
		
	

	
		 
	 

	
		
			Important:
		 

		
			The errors shown above occur because Terraform changes the IP addresses of the VMs, and the provisioner can no longer connect to the previous IP address. 
			The provisioner runs in a timeout and produces these errors. 
			 
		 
	

	
		 
		Looking into Server Manager on the VM after running the Terraform script shows the correct renaming of the VMs, the successful change of the IP address to the needed ones, and the successful registration in the Domain: 
		
	 

	
		The Terraform flow was completed successfully. 
		The environment is now ready to install and configure Citrix Virtual Apps and Desktops and Citrix StoreFront. 
		 
	 

	
		Module 3: Installing and Configuring Citrix Virtual Apps and Desktops and Citrix StoreFront
	
	 

	
		In this Module, the following steps are done:
	 

	
		
			Creating the needed databases
		
		
			Installing Citrix Virtual Apps and Desktops on the Delivery Controller VMs
		
		
			Creating the Citrix Virtual Apps and Desktops site
		
		
			Installing Citrix StoreFront on the Delivery Controller VMs
		
		
			Configuring a Citrix StoreFront cluster on the primary Delivery Controller
		
	

	
		After running Module 2, the VMs are prepared for Installation and Configuration of Citrix Virtual Apps and Desktops and Citrix StoreFront: 
		
	 

	
		 
	 

	
		During the Installation process, the Installer also installs and configures StoreFront, but does not create a StoreFront cluster according to our needs: 
	 

	
		
	 

	
		Because of that, the first script in Module 3 purges all existing StoreFront deployments on the 2 Delivery Controllers to have a clear environment for deploying the StoreFront cluster.
	 

	
		
			Important:
		 

		
			The current Citrix Terraform Provider version 1.0.1 has significant improvements built in. There is no need to run various scripts to create the needed entities and determine the required information, etc. 
			Compared to the previous versions, it dramatically simplifies the Terraform workflow. 
			 
		 
	

	
		Example of previous Terraform scripts needed to create and configure a StoreFront site:
	 

	
		
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}		
		
			
				
					
						
							
								 #### Create Storefront-Configuration script
							

							
								  #### Need to invoke PowerShell as Domain User as the provisioner does not allow to be run in a Domain Users-context, but CVAD SDK needs to be run as a Domain User
							

							
								  ConfigureStorefrontScriptcontent     = &lt;&lt;-EOT
							

							
								  #$PSUsername = 'tacg\administrator'
							

							
								#$PSPassword = 'XXXXXXXXXX'
							

							
								#$PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
							

							
								#$PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
							

							
								#Invoke-Command -ComputerName localhost -Credential $PSCredential -ScriptBlock {
							

							
								Add-PSSnapin Citrix.*
							
							 

							
								#Clear-Existing StoreFront deployments
							

							
								Clear-STFDeployment -Confirm:$false
							

							
								Write-Output 'Existing Deployments deleted.'
							
							 

							
								#Add new StoreFront deployment
							

							
								Add-STFDeployment -HostBaseUrl 'https://storefront.the-austrian-citrix-guy.at' -Confirm:$false
							

							
								Write-Output 'New deployment created.'
							
							 

							
								#Add new Authentication Service
							

							
								Add-STFAuthenticationService '/Citrix/Authentication'
							

							
								$authentication = Get-STFAuthenticationService
							

							
								Write-Output 'Authentication service created.'
							
							 

							
								Start-Sleep -Seconds 120
							
							 

							
								#Enable Access through a NetScaler Gateway
							

							
								Enable-STFAuthenticationServiceProtocol -Name CitrixAGBasic -AuthenticationService ($authentication)
							

							
								$protocols = Get-STFAuthenticationServiceProtocol -AuthenticationService ($authentication)
							
							 

							
								#Add NetScaler Gateway
							

							
								Add-STFRoamingGateway -Name TACG-TF-NSGW -LogonType Domain -GatewayUrl 'https://ns.the-austrian-citrix-guy.at' -CallbackUrl 'https://ns.the-austrian-citrix-guy.at/CitrixAuthService/AuthService.asmx' -SessionReliability:$true -SecureTicketAuthorityUrls 'http://TACG-NTX-DDC1.the-austrian-citrix-guy.at/scripts/ctxsta.dll,http://TACG-NTX-DDC2.the-austrian-citrix-guy.at/scripts/ctxsta.dll' -IsCloudGateway:$false
							

							
								Write-Output 'NSGW created.'
							

							
								#Add Beacons for NetScaler Gateway
							

							
								Set-STFRoamingBeacon -Internal 'http://TACG-NTX-DDC1.the-austrian-citrix-guy.at' -External https://ns.the-austrian-citrix-guy.at,https://news.orf.at
							

							
								Write-Output 'Beacons created.'
							
							 

							
								#Add new Store Service
							

							
								Add-STFStoreService -VirtualPath '/Citrix/Store' -AuthenticationService $authentication -FarmName 'TACG-SF' -FarmType 'XenDesktop' -Servers 'TACG-NTX-DDC1.the-austrian-citrix-guy.at' -FriendlyName 'TACG-SF' -TransportType HTTPS -Port 443
							

							
								Write-Output 'Store service created.'
							
							 

							
								#Register NetScaler Gateway and Beacons
							

							
								$store = Get-STFStoreService -VirtualPath '/Citrix/Store'
							

							
								$gateway = Get-STFRoamingGateway -Name TACG-TF-NSGW
							

							
								Register-STFStoreGateway -Gateway $gateway -StoreService $store -DefaultGateway -UseFullVpn:$false
							

							
								Write-Output 'NSGW registered.'
							
							 

							
								#Enable WebReceiver
							

							
								Add-STFWebReceiverService -VirtualPath '/Citrix/StoreWeb' -StoreService $store
							

							
								$receiver = Get-STFWebReceiverService -VirtualPath '/Citrix/StoreWeb'
							

							
								Write-Output 'Web Receiver site created.'
							
							 

							
								#Enable PNAGent site
							

							
								Enable-STFStorePna -StoreService $store -AllowUserPasswordChange -DefaultPnaService
							

							
								Write-Output 'PNA site created.'
							
							 

							
								#Enable HTML5Fallback
							

							
								Set-STFWebReceiverPluginAssistant -WebReceiverService $receiver -Enabled $true -Html5Enabled "Fallback"
							

							
								Write-Output 'HTML5 site created.'
							

							
								Write-Output 'Storefront configuration complete.'
							

							
								Write-Output 'Registering Storefront in CVAD Site configuration.'
							
							 

							
								$PSUsername = 'tacg\administrator'
							

							
								$PSPassword = 'XXXXXXXXXX'
							

							
								$PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
							

							
								$PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
							

							
								Invoke-Command -ComputerName localhost -Credential $PSCredential -ScriptBlock {
							

							
								Add-PSSnapin Citrix.*
							
							 

							
								#Add StoreFront configuration to CVAD site
							

							
								$configurationSlot = Get-BrokerConfigurationSlot -Name "RS"
							

							
								$storefrontUrl = 'https://storefront.the-austrian-citrix-guy.at/Citrix/StoreWeb'
							

							
								$configuration = New-BrokerStorefrontAddress -Description "Citrix Storefront Address" -Enabled $true -Name "Citrix StoreFront" -Url $storefrontUrl
							

							
								New-BrokerMachineConfiguration -Policy $configuration -ConfigurationSlotUid $configurationSlot.Uid -Description "Citrix Storefront Address Configuration" -LeafName 'TACG-SF'
							

							
								Write-Output 'Registration in CVAD Site complete.'
							

							
								Write-Output 'Reboot needed...'
							

							
								}
							

							
								  EOT
							
						
					
				
			
		
	

	
		 
		The actual Terraform provider dramatically simplifies the process, and the code is much more structured:
	 

	
		
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}		
		
			
				
					
						
							
								
									resource "citrix_stf_deployment" "CreateSTFDeployment" {
								

								
									  site_id                 = var.STF-SiteID
								

								
									  host_base_url           = var.STF-BaseURL
								

								
									}
								
								 

								
									resource "citrix_stf_authentication_service" "CreateSTFAuthenticationService" {
								

								
									  depends_on          = [ citrix_stf_deployment.CreateSTFDeployment ]
								

								
									  site_id                 = citrix_stf_deployment.CreateSTFDeployment.site_id
								

								
									  friendly_name           = var.STF-Auth-FriendlyName
								

								
									  virtual_path            = var.STF-Auth-VirtualPath
								

								
									}
								
								 

								
									resource "citrix_stf_store_service" "CreateSTFStoreService" {
								

								
									  depends_on        = [ citrix_stf_authentication_service.CreateSTFAuthenticationService ]
								

								
									  site_id                             = citrix_stf_deployment.CreateSTFDeployment.site_id
								

								
									  friendly_name                       = var.STF-Store-FriendlyName
								

								
									  virtual_path                        = var.STF-Store-VirtualPath
								

								
									  authentication_service_virtual_path = "${citrix_stf_authentication_service.CreateSTFAuthenticationService.virtual_path}"
								

								
									  
								

								
									  pna               = {
								

								
									                          enable      = var.STF-EnablePNA
								

								
									                      }
								
								 

								
									 farms             = [
								

								
									               {
								

								
									                          farm_name   = var.CVAD-SiteName
								

								
									                          farm_type   = "XenDesktop"
								

								
									                          servers     = var.STF-Farm-Servers 
								

								
									                          port        = var.STF-Port
								

								
									               },
								

								
									                      ] 
								

								
									}
								
								 

								
									resource "citrix_stf_webreceiver_service" "CreateSTFWebReceiverService"{
								

								
									  depends_on          = [ citrix_stf_store_service.CreateSTFStoreService ]
								

								
									  site_id             = citrix_stf_deployment.CreateSTFDeployment.site_id
								

								
									  friendly_name       = var.STF-WebReceiver-FriendlyName
								

								
									  virtual_path        = var.STF-WebReceiver-VirtualPath
								

								
									  store_virtual_path  = "${citrix_stf_store_service.CreateSTFStoreService.virtual_path}"
								

								
									}
								
								 

								
									resource "citrix_stf_roaming_beacon" "CreateSTFRoamingBeacon" {
								

								
									  depends_on          = [ citrix_stf_store_service.CreateSTFStoreService ]
								

								
									  internal_ip         = var.STF-BaseURL
								

								
									  external_ips        = var.STF-External-Beacons
								

								
									  site_id             = var.STF-SiteID
								

								
									}
								
								 

								
									resource "citrix_stf_roaming_gateway" "CreateSTFRoamingGateway" {
								

								
									  depends_on                     = [ citrix_stf_deployment.CreateSTFDeployment ]
								

								
									  site_id                        = citrix_stf_deployment.CreateSTFDeployment.site_id
								

								
									  name                           = var.STF-GW-Name
								

								
									  logon_type                     = var.STF-GW-LogonType
								

								
									  gateway_url                    = var.STF-GW-URL
								

								
									  callback_url                   = var.STF-GW-CallbackURL
								

								
									  version                        = var.STF-GW-Version
								

								
									  subnet_ip_address              = var.STF-GW-SNIP
								

								
									  stas_bypass_duration           = var.STF-GW-Bypass
								

								
									  session_reliability            = var.STF-GW-SessionReliability
								

								
									  request_ticket_two_stas        = var.STF-GW-Request2STAs
								

								
									  stas_use_load_balancing        = var.STF-GW-UseSTALB
								

								
									  is_cloud_gateway               = var.STF-GW-IsCloudGateway
								

								
									  secure_ticket_authority_urls   = [ 
								

								
									      {
								

								
									      sta_url                    = var.STF-GW-STA1
								

								
									      },
								

								
									      {
								

								
									      sta_url                    = var.STF-GW-STA2
								

								
									      },
								

								
									    ]
								

								
									}
								
								 

								
									resource "citrix_stf_xenapp_default_store" "CreateSTFDefaultStore" {
								

								
									  depends_on                     = [ citrix_stf_store_service.CreateSTFStoreService ]
								

								
									  store_virtual_path             = citrix_stf_store_service.CreateSTFStoreService.virtual_path
								

								
									  store_site_id                  = citrix_stf_store_service.CreateSTFStoreService.site_id
								

								
									}
								
							
						
					
				
			
			 
			 
		
	

	
		Important: 
		 
		Before starting the Terraform workflow, please ensure you have configured the variables according to your needs using the corresponding .auto.tfvars.json and/or terraform.tfvars file. 
		 
	 

	
		The configuration can be started by following the standard Terraform workflow:
	 

	
		terraform init, 
		terraform plan 
		 
	 

	
		and if no errors occur
	 

	
		terraform apply
	 

	
		
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}		
	

	
		 
	 

	
		
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}		
		
			
				
					
						
							PS C:\_TACG\_CVADOnXenServer-InitialCVADConfiguration&gt; terraform init 
							Initializing the backend... 
							Initializing provider plugins... 
							- Finding latest version of hashicorp/null... 
							- Finding latest version of hashicorp/time... 
							- Finding latest version of hashicorp/local... 
							- Finding citrix/citrix versions matching "1.0.1"... 
							- Finding latest version of xenserver/xenserver... 
							- Installing hashicorp/null v3.2.2... 
							- Installed hashicorp/null v3.2.2 (signed by HashiCorp) 
							- Installing hashicorp/time v0.12.0... 
							- Installed hashicorp/time v0.12.0 (signed by HashiCorp) 
							- Installing hashicorp/local v2.5.1... 
							- Installed hashicorp/local v2.5.1 (signed by HashiCorp) 
							- Installing citrix/citrix v1.0.1... 
							- Installed citrix/citrix v1.0.1 (self-signed, key ID BD4BD0E690CB7D88) 
							- Installing xenserver/xenserver v0.1.1... 
							- Installed xenserver/xenserver v0.1.1 (self-signed, key ID FABD99A0D7648275) 
							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\_CVADOnXenServer-InitialCVADConfiguration&gt; terraform plan 
							 
							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.WriteCVADDBScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteCVADDBScriptIntoDataDirectory" { 
							      + 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             = "./data/CVAD-DBs.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WriteCVADScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteCVADScriptIntoDataDirectory" { 
							      + content              = &lt;&lt;-EOT 
							            if (!(Test-Path C:/TEMP/XDINST)){ 
							                New-Item -Path 'C:/TEMP/XDINST' -ItemType Directory 
							            } 
							            D:/x64/"XenDesktop Setup"/XenDesktopServerSetup.exe /components controller,webstudio,licenseserver,desktopdirector /noreboot /configure_firewall /quiet /nosql /Logpath C:/TEMP/XDINST 
							            #Restart-Computer -Force 
							        EOT 
							      + 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             = "./data/CVAD-Install.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WriteCVADSiteScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteCVADSiteScriptIntoDataDirectory" { 
							      + 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             = "./data/CVAD-Site.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WriteLicScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteLicScriptIntoDataDirectory" { 
							      + 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             = "./data/CVAD-CreateDNSARecordOnDC.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WriteRebootScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteRebootScriptIntoDataDirectory" { 
							      + content              = &lt;&lt;-EOT 
							            Restart-Computer -Force 
							        EOT 
							      + 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             = "./data/Reboot.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WriteSFScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteSFScriptIntoDataDirectory" { 
							      + content              = &lt;&lt;-EOT 
							            D:x64/StoreFront/CitrixStoreFront-x64.exe -silent 
							        EOT 
							      + 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             = "./data/SF-Install.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # null_resource.CreateCVADDBs will be created 
							  + resource "null_resource" "CreateCVADDBs" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.InstallCVADFromCDROMOnDDC1 will be created 
							  + resource "null_resource" "InstallCVADFromCDROMOnDDC1" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.InstallCVADFromCDROMOnDDC2 will be created 
							  + resource "null_resource" "InstallCVADFromCDROMOnDDC2" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.InstallSFOnDDC1 will be created 
							  + resource "null_resource" "InstallSFOnDDC1" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.InstallSFOnDDC2 will be created 
							  + resource "null_resource" "InstallSFOnDDC2" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.RebootDDC1 will be created 
							  + resource "null_resource" "RebootDDC1" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.RebootDDC1AfterStoreFrontInstall will be created 
							  + resource "null_resource" "RebootDDC1AfterStoreFrontInstall" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.RebootDDC2 will be created 
							  + resource "null_resource" "RebootDDC2" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.RebootDDC2AfterStoreFrontInstall will be created 
							  + resource "null_resource" "RebootDDC2AfterStoreFrontInstall" { 
							      + id = (known after apply) 
							    } 
							 
							  # time_sleep.WaitForRebootofDDC1 will be created 
							  + resource "time_sleep" "WaitForRebootofDDC1" { 
							      + create_duration = "120s" 
							      + id              = (known after apply) 
							    } 
							 
							  # time_sleep.WaitForRebootofDDC1AfterStoreFrontInstall will be created 
							  + resource "time_sleep" "WaitForRebootofDDC1AfterStoreFrontInstall" { 
							      + create_duration = "120s" 
							      + id              = (known after apply) 
							    } 
							 
							  # time_sleep.WaitForRebootofDDC2 will be created 
							  + resource "time_sleep" "WaitForRebootofDDC2" { 
							      + create_duration = "120s" 
							      + id              = (known after apply) 
							    } 
							 
							  # time_sleep.WaitForRebootofDDC2AfterStorefrontInstall will be created 
							  + resource "time_sleep" "WaitForRebootofDDC2AfterStorefrontInstall" { 
							      + create_duration = "120s" 
							      + id              = (known after apply) 
							    } 
							 
							  # time_sleep.WaitToRebootDDC1AfterCVADInstall will be created 
							  + resource "time_sleep" "WaitToRebootDDC1AfterCVADInstall" { 
							      + create_duration = "600s" 
							      + id              = (known after apply) 
							    } 
							 
							  # time_sleep.WaitToRebootDDC2AfterCVADInstall will be created 
							  + resource "time_sleep" "WaitToRebootDDC2AfterCVADInstall" { 
							      + create_duration = "600s" 
							      + id              = (known after apply) 
							    }
						 

						
							  # null_resource.AddToCVADSite will be created 
							  + resource "null_resource" "AddToCVADSite" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.CreateCVADSite will be created 
							  + resource "null_resource" "CreateCVADSite" { 
							      + id = (known after apply) 
							    } 
							 
							  # time_sleep.WaitAgainAgainForRebootofDDC1 will be created 
							  + resource "time_sleep" "WaitAgainAgainForRebootofDDC1" { 
							      + create_duration = "120s" 
							      + id              = (known after apply) 
							    } 
							 
							  # time_sleep.WaitAgainForRebootofDDC1 will be created 
							  + resource "time_sleep" "WaitAgainForRebootofDDC1" { 
							      + create_duration = "120s" 
							      + id              = (known after apply) 
							    } 
							 
							 
							Plan: 25 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\_CVADOnXenServer-InitialCVADConfiguration&gt; terraform validate 
							Success! The configuration is valid. 
							 
							PS C:\_TACG\_CVADOnXenServer-InitialCVADConfiguration&gt; terraform apply 
							 
							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.WriteCVADDBScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteCVADDBScriptIntoDataDirectory" { 
							      + 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             = "./data/CVAD-DBs.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WriteCVADScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteCVADScriptIntoDataDirectory" { 
							      + content              = &lt;&lt;-EOT 
							            if (!(Test-Path C:/TEMP/XDINST)){ 
							                New-Item -Path 'C:/TEMP/XDINST' -ItemType Directory 
							            } 
							            D:/x64/"XenDesktop Setup"/XenDesktopServerSetup.exe /components controller,webstudio,licenseserver,desktopdirector /noreboot /configure_firewall /quiet /nosql /Logpath C:/TEMP/XDINST 
							            #Restart-Computer -Force 
							        EOT 
							      + 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             = "./data/CVAD-Install.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WriteCVADSiteScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteCVADSiteScriptIntoDataDirectory" { 
							      + 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             = "./data/CVAD-Site.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WriteLicScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteLicScriptIntoDataDirectory" { 
							      + 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             = "./data/CVAD-CreateDNSARecordOnDC.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WriteRebootScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteRebootScriptIntoDataDirectory" { 
							      + content              = &lt;&lt;-EOT 
							            Restart-Computer -Force 
							        EOT 
							      + 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             = "./data/Reboot.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WriteSFScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteSFScriptIntoDataDirectory" { 
							      + content              = &lt;&lt;-EOT 
							            D:x64/StoreFront/CitrixStoreFront-x64.exe -silent 
							        EOT 
							      + 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             = "./data/SF-Install.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # null_resource.CreateCVADDBs will be created 
							  + resource "null_resource" "CreateCVADDBs" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.InstallCVADFromCDROMOnDDC1 will be created 
							  + resource "null_resource" "InstallCVADFromCDROMOnDDC1" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.InstallCVADFromCDROMOnDDC2 will be created 
							  + resource "null_resource" "InstallCVADFromCDROMOnDDC2" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.InstallSFOnDDC1 will be created 
							  + resource "null_resource" "InstallSFOnDDC1" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.InstallSFOnDDC2 will be created 
							  + resource "null_resource" "InstallSFOnDDC2" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.RebootDDC1 will be created 
							  + resource "null_resource" "RebootDDC1" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.RebootDDC1AfterStoreFrontInstall will be created 
							  + resource "null_resource" "RebootDDC1AfterStoreFrontInstall" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.RebootDDC2 will be created 
							  + resource "null_resource" "RebootDDC2" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.RebootDDC2AfterStoreFrontInstall will be created 
							  + resource "null_resource" "RebootDDC2AfterStoreFrontInstall" { 
							      + id = (known after apply) 
							    } 
							 
							  # time_sleep.WaitForRebootofDDC1 will be created 
							  + resource "time_sleep" "WaitForRebootofDDC1" { 
							      + create_duration = "120s" 
							      + id              = (known after apply) 
							    } 
							 
							  # time_sleep.WaitForRebootofDDC1AfterStoreFrontInstall will be created 
							  + resource "time_sleep" "WaitForRebootofDDC1AfterStoreFrontInstall" { 
							      + create_duration = "120s" 
							      + id              = (known after apply) 
							    } 
							 
							  # time_sleep.WaitForRebootofDDC2 will be created 
							  + resource "time_sleep" "WaitForRebootofDDC2" { 
							      + create_duration = "120s" 
							      + id              = (known after apply) 
							    } 
							 
							  # time_sleep.WaitForRebootofDDC2AfterStorefrontInstall will be created 
							  + resource "time_sleep" "WaitForRebootofDDC2AfterStorefrontInstall" { 
							      + create_duration = "120s" 
							      + id              = (known after apply) 
							    } 
							 
							  # time_sleep.WaitToRebootDDC1AfterCVADInstall will be created 
							  + resource "time_sleep" "WaitToRebootDDC1AfterCVADInstall" { 
							      + create_duration = "600s" 
							      + id              = (known after apply) 
							    } 
							 
							  # time_sleep.WaitToRebootDDC2AfterCVADInstall will be created 
							  + resource "time_sleep" "WaitToRebootDDC2AfterCVADInstall" { 
							      + create_duration = "600s" 
							      + id              = (known after apply) 
							    } 
							 
							  # null_resource.AddToCVADSite will be created 
							  + resource "null_resource" "AddToCVADSite" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.CreateCVADSite will be created 
							  + resource "null_resource" "CreateCVADSite" { 
							      + id = (known after apply) 
							    } 
							 
							  # time_sleep.WaitAgainAgainForRebootofDDC1 will be created 
							  + resource "time_sleep" "WaitAgainAgainForRebootofDDC1" { 
							      + create_duration = "120s" 
							      + id              = (known after apply) 
							    } 
							 
							  # time_sleep.WaitAgainForRebootofDDC1 will be created 
							  + resource "time_sleep" "WaitAgainForRebootofDDC1" { 
							      + create_duration = "120s" 
							      + id              = (known after apply) 
							    } 
							 
							Plan: 25 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 
							 
							local_file.WriteCVADDBScriptIntoDataDirectory: Creating... 
							local_file.WriteSFScriptIntoDataDirectory: Creating... 
							local_file.WriteRebootScriptIntoDataDirectory: Creating... 
							local_file.WriteCVADSiteScriptIntoDataDirectory: Creating... 
							local_file.WriteCVADSiteScriptIntoDataDirectory: Creation complete after 0s [id=fc78d2bfcadb8b641b6f19157858f4c4bae43840] 
							local_file.WriteCVADScriptIntoDataDirectory: Creating... 
							local_file.WriteLicScriptIntoDataDirectory: Creating... 
							local_file.WriteCVADDBScriptIntoDataDirectory: Creation complete after 0s [id=fc78d2bfcadb8b641b6f19157858f4c4bae43840] 
							local_file.WriteCVADScriptIntoDataDirectory: Creation complete after 0s [id=c0f5671aca3792327313b046df2e00a4dbeedaff] 
							local_file.WriteRebootScriptIntoDataDirectory: Creation complete after 0s [id=2baf7fa209ad203f96017f90f5727642e5f44a03] 
							local_file.WriteLicScriptIntoDataDirectory: Creation complete after 0s [id=70d502d21ae9969899f59d901757412a6f9ffc5f] 
							local_file.WriteSFScriptIntoDataDirectory: Creation complete after 0s [id=43e0764879f69aef387665571bc9fdc861e76795] 
							null_resource.InstallCVADFromCDROMOnDDC2: Creating... 
							null_resource.InstallCVADFromCDROMOnDDC2: Provisioning with 'file'... 
							null_resource.InstallCVADFromCDROMOnDDC1: Creating... 
							null_resource.InstallCVADFromCDROMOnDDC1: Provisioning with 'file'... 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.InstallCVADFromCDROMOnDDC1: Provisioning with 'remote-exec'... 
							null_resource.InstallCVADFromCDROMOnDDC2: Provisioning with 'remote-exec'... 
							null_resource.InstallCVADFromCDROMOnDDC2 (remote-exec): Connecting to remote host via WinRM... 
							null_resource.InstallCVADFromCDROMOnDDC2 (remote-exec):   Host: 10.10.111.33 
							null_resource.InstallCVADFromCDROMOnDDC2 (remote-exec):   Port: 5985 
							null_resource.InstallCVADFromCDROMOnDDC2 (remote-exec):   User: administrator 
							null_resource.InstallCVADFromCDROMOnDDC2 (remote-exec):   Password: true 
							null_resource.InstallCVADFromCDROMOnDDC2 (remote-exec):   HTTPS: false 
							null_resource.InstallCVADFromCDROMOnDDC2 (remote-exec):   Insecure: false 
							null_resource.InstallCVADFromCDROMOnDDC2 (remote-exec):   NTLM: false 
							null_resource.InstallCVADFromCDROMOnDDC2 (remote-exec):   CACert: false 
							null_resource.InstallCVADFromCDROMOnDDC1 (remote-exec): Connecting to remote host via WinRM... 
							null_resource.InstallCVADFromCDROMOnDDC1 (remote-exec):   Host: 10.10.111.31 
							null_resource.InstallCVADFromCDROMOnDDC1 (remote-exec):   Port: 5985 
							null_resource.InstallCVADFromCDROMOnDDC1 (remote-exec):   User: administrator 
							null_resource.InstallCVADFromCDROMOnDDC1 (remote-exec):   Password: true 
							null_resource.InstallCVADFromCDROMOnDDC1 (remote-exec):   HTTPS: false 
							null_resource.InstallCVADFromCDROMOnDDC1 (remote-exec):   Insecure: false 
							null_resource.InstallCVADFromCDROMOnDDC1 (remote-exec):   NTLM: false 
							null_resource.InstallCVADFromCDROMOnDDC1 (remote-exec):   CACert: false 
							null_resource.InstallCVADFromCDROMOnDDC2 (remote-exec): Connected! 
							null_resource.InstallCVADFromCDROMOnDDC1 (remote-exec): Connected! 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
							null_resource.InstallCVADFromCDROMOnDDC1 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/CVAD-Install.ps1 
							 
							null_resource.InstallCVADFromCDROMOnDDC2 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/CVAD-Install.ps1 
							null_resource.InstallCVADFromCDROMOnDDC2: Still creating... [10s elapsed] 
							null_resource.InstallCVADFromCDROMOnDDC1: Still creating... [10s elapsed] 
							null_resource.InstallCVADFromCDROMOnDDC2: Still creating... [20s elapsed] 
							null_resource.InstallCVADFromCDROMOnDDC1: Still creating... [20s elapsed] 
							... 
							null_resource.InstallCVADFromCDROMOnDDC1: Still creating... [5m11s elapsed] 
							null_resource.InstallCVADFromCDROMOnDDC2: Still creating... [5m21s elapsed] 
							null_resource.InstallCVADFromCDROMOnDDC1: Still creating... [5m21s elapsed] 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.InstallCVADFromCDROMOnDDC1: Creation complete after 5m21s [id=1840192384062047616] 
							time_sleep.WaitToRebootDDC1AfterCVADInstall: Creating... 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.InstallCVADFromCDROMOnDDC2: Creation complete after 5m21s [id=7135611835837462764] 
							time_sleep.WaitToRebootDDC2AfterCVADInstall: Creating... 
							time_sleep.WaitToRebootDDC1AfterCVADInstall: Still creating... [10s elapsed] 
							time_sleep.WaitToRebootDDC2AfterCVADInstall: Still creating... [10s elapsed] 
							time_sleep.WaitToRebootDDC1AfterCVADInstall: Still creating... [20s elapsed] 
							time_sleep.WaitToRebootDDC2AfterCVADInstall: Still creating... [20s elapsed] 
							time_sleep.WaitToRebootDDC1AfterCVADInstall: Still creating... [30s elapsed] 
							time_sleep.WaitToRebootDDC2AfterCVADInstall: Still creating... [30s elapsed] 
							... 
							time_sleep.WaitToRebootDDC1AfterCVADInstall: Still creating... [9m40s elapsed] 
							time_sleep.WaitToRebootDDC2AfterCVADInstall: Still creating... [9m41s elapsed] 
							time_sleep.WaitToRebootDDC1AfterCVADInstall: Still creating... [9m50s elapsed] 
							time_sleep.WaitToRebootDDC2AfterCVADInstall: Still creating... [9m51s elapsed] 
							time_sleep.WaitToRebootDDC1AfterCVADInstall: Creation complete after 10m0s [id=2024-09-09T13:11:10Z] 
							null_resource.RebootDDC1: Creating... 
							null_resource.RebootDDC1: Provisioning with 'file'... 
							time_sleep.WaitToRebootDDC2AfterCVADInstall: Creation complete after 10m0s [id=2024-09-09T13:11:11Z] 
							null_resource.RebootDDC2: Creating... 
							null_resource.RebootDDC2: Provisioning with 'file'... 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.RebootDDC1: Provisioning with 'remote-exec'... 
							null_resource.RebootDDC1 (remote-exec): Connecting to remote host via WinRM... 
							null_resource.RebootDDC1 (remote-exec):   Host: 10.10.111.31 
							null_resource.RebootDDC1 (remote-exec):   Port: 5985 
							null_resource.RebootDDC1 (remote-exec):   User: administrator 
							null_resource.RebootDDC1 (remote-exec):   Password: true 
							null_resource.RebootDDC1 (remote-exec):   HTTPS: false 
							null_resource.RebootDDC1 (remote-exec):   Insecure: false 
							null_resource.RebootDDC1 (remote-exec):   NTLM: false 
							null_resource.RebootDDC1 (remote-exec):   CACert: false 
							null_resource.RebootDDC1 (remote-exec): Connected! 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.RebootDDC2: Provisioning with 'remote-exec'... 
							null_resource.RebootDDC2 (remote-exec): Connecting to remote host via WinRM... 
							null_resource.RebootDDC2 (remote-exec):   Host: 10.10.111.33 
							null_resource.RebootDDC2 (remote-exec):   Port: 5985 
							null_resource.RebootDDC2 (remote-exec):   User: administrator 
							null_resource.RebootDDC2 (remote-exec):   Password: true 
							null_resource.RebootDDC2 (remote-exec):   HTTPS: false 
							null_resource.RebootDDC2 (remote-exec):   Insecure: false 
							null_resource.RebootDDC2 (remote-exec):   NTLM: false 
							null_resource.RebootDDC2 (remote-exec):   CACert: false 
							null_resource.RebootDDC2 (remote-exec): Connected! 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
							null_resource.RebootDDC1 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/Reboot.ps1 
							 
							null_resource.RebootDDC2 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/Reboot.ps1 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.RebootDDC1: Creation complete after 2s [id=4631577298475638261] 
							time_sleep.WaitForRebootofDDC1: Creating... 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.RebootDDC2: Creation complete after 3s [id=3236482949203626299] 
							time_sleep.WaitForRebootofDDC2: Creating... 
							time_sleep.WaitForRebootofDDC1: Still creating... [10s elapsed] 
							time_sleep.WaitForRebootofDDC2: Still creating... [10s elapsed] 
							time_sleep.WaitForRebootofDDC1: Still creating... [20s elapsed] 
							time_sleep.WaitForRebootofDDC2: Still creating... [20s elapsed] 
							... 
							time_sleep.WaitForRebootofDDC1: Still creating... [1m50s elapsed] 
							time_sleep.WaitForRebootofDDC2: Still creating... [1m50s elapsed] 
							time_sleep.WaitForRebootofDDC1: Creation complete after 2m0s [id=2024-09-09T13:13:13Z] 
							null_resource.CreateCVADDBs: Creating... 
							null_resource.CreateCVADDBs: Provisioning with 'file'... 
							time_sleep.WaitForRebootofDDC2: Creation complete after 2m0s [id=2024-09-09T13:13:13Z] 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.CreateCVADDBs: Provisioning with 'remote-exec'... 
							null_resource.CreateCVADDBs (remote-exec): Connecting to remote host via WinRM... 
							null_resource.CreateCVADDBs (remote-exec):   Host: 10.10.111.31 
							null_resource.CreateCVADDBs (remote-exec):   Port: 5985 
							null_resource.CreateCVADDBs (remote-exec):   User: administrator 
							null_resource.CreateCVADDBs (remote-exec):   Password: true 
							null_resource.CreateCVADDBs (remote-exec):   HTTPS: false 
							null_resource.CreateCVADDBs (remote-exec):   Insecure: false 
							null_resource.CreateCVADDBs (remote-exec):   NTLM: false 
							null_resource.CreateCVADDBs (remote-exec):   CACert: false 
							null_resource.CreateCVADDBs (remote-exec): Connected! 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
							null_resource.CreateCVADDBs (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/CVAD-DBs.ps1 
							null_resource.CreateCVADDBs: Still creating... [10s elapsed] 
							null_resource.CreateCVADDBs: Still creating... [20s elapsed] 
							null_resource.CreateCVADDBs: Still creating... [30s elapsed] 
							 
							 
							null_resource.CreateCVADDBs (remote-exec): PSComputerName      : localhost 
							null_resource.CreateCVADDBs (remote-exec): RunspaceId          : 85f56ddb-ead0-4f71-aa32-8cb956df8931 
							null_resource.CreateCVADDBs (remote-exec): DataStore           : Site 
							null_resource.CreateCVADDBs (remote-exec): IntegratedSecurity  : True 
							null_resource.CreateCVADDBs (remote-exec): MirrorServerAddress : 
							null_resource.CreateCVADDBs (remote-exec): Name                : CitrixTACG-TF-XS8Site 
							null_resource.CreateCVADDBs (remote-exec): ServerAddress       : TACG-SQL\CITRIX 
							 
							null_resource.CreateCVADDBs (remote-exec): PSComputerName      : localhost 
							null_resource.CreateCVADDBs (remote-exec): RunspaceId          : 85f56ddb-ead0-4f71-aa32-8cb956df8931 
							null_resource.CreateCVADDBs (remote-exec): DataStore           : Logging 
							null_resource.CreateCVADDBs (remote-exec): IntegratedSecurity  : True 
							null_resource.CreateCVADDBs (remote-exec): MirrorServerAddress : 
							null_resource.CreateCVADDBs (remote-exec): Name                : CitrixTACG-TF-XS8Logging 
							null_resource.CreateCVADDBs (remote-exec): ServerAddress       : TACG-SQL\CITRIX 
							 
							null_resource.CreateCVADDBs (remote-exec): PSComputerName      : localhost 
							null_resource.CreateCVADDBs (remote-exec): RunspaceId          : 85f56ddb-ead0-4f71-aa32-8cb956df8931 
							null_resource.CreateCVADDBs (remote-exec): DataStore           : Monitor 
							null_resource.CreateCVADDBs (remote-exec): IntegratedSecurity  : True 
							null_resource.CreateCVADDBs (remote-exec): MirrorServerAddress : 
							null_resource.CreateCVADDBs (remote-exec): Name                : CitrixTACG-TF-XS8Monitoring 
							null_resource.CreateCVADDBs (remote-exec): ServerAddress       : TACG-SQL\CITRIX 
							 
							 
							 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.CreateCVADDBs: Creation complete after 31s [id=2594150282709976995] 
							null_resource.InstallSFOnDDC1: Creating... 
							null_resource.InstallSFOnDDC2: Creating... 
							null_resource.InstallSFOnDDC1: Provisioning with 'file'... 
							null_resource.InstallSFOnDDC2: Provisioning with 'file'... 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.InstallSFOnDDC1: Provisioning with 'remote-exec'... 
							null_resource.InstallSFOnDDC1 (remote-exec): Connecting to remote host via WinRM... 
							null_resource.InstallSFOnDDC1 (remote-exec):   Host: 10.10.111.31 
							null_resource.InstallSFOnDDC1 (remote-exec):   Port: 5985 
							null_resource.InstallSFOnDDC1 (remote-exec):   User: administrator 
							null_resource.InstallSFOnDDC1 (remote-exec):   Password: true 
							null_resource.InstallSFOnDDC1 (remote-exec):   HTTPS: false 
							null_resource.InstallSFOnDDC1 (remote-exec):   Insecure: false 
							null_resource.InstallSFOnDDC1 (remote-exec):   NTLM: false 
							null_resource.InstallSFOnDDC1 (remote-exec):   CACert: false 
							null_resource.InstallSFOnDDC1 (remote-exec): Connected! 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.InstallSFOnDDC2: Provisioning with 'remote-exec'... 
							null_resource.InstallSFOnDDC2 (remote-exec): Connecting to remote host via WinRM... 
							null_resource.InstallSFOnDDC2 (remote-exec):   Host: 10.10.111.33 
							null_resource.InstallSFOnDDC2 (remote-exec):   Port: 5985 
							null_resource.InstallSFOnDDC2 (remote-exec):   User: administrator 
							null_resource.InstallSFOnDDC2 (remote-exec):   Password: true 
							null_resource.InstallSFOnDDC2 (remote-exec):   HTTPS: false 
							null_resource.InstallSFOnDDC2 (remote-exec):   Insecure: false 
							null_resource.InstallSFOnDDC2 (remote-exec):   NTLM: false 
							null_resource.InstallSFOnDDC2 (remote-exec):   CACert: false 
							null_resource.InstallSFOnDDC2 (remote-exec): Connected! 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
							null_resource.InstallSFOnDDC1 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/SF-Install.ps1 
							 
							null_resource.InstallSFOnDDC2 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/SF-Install.ps1 
							null_resource.InstallSFOnDDC2: Still creating... [11s elapsed] 
							null_resource.InstallSFOnDDC1: Still creating... [11s elapsed] 
							null_resource.InstallSFOnDDC2: Still creating... [21s elapsed] 
							null_resource.InstallSFOnDDC1: Still creating... [21s elapsed] 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.InstallSFOnDDC2: Creation complete after 30s [id=2505044489820912900] 
							null_resource.RebootDDC2AfterStoreFrontInstall: Creating... 
							null_resource.RebootDDC2AfterStoreFrontInstall: Provisioning with 'remote-exec'... 
							null_resource.RebootDDC2AfterStoreFrontInstall (remote-exec): Connecting to remote host via WinRM... 
							null_resource.RebootDDC2AfterStoreFrontInstall (remote-exec):   Host: 10.10.111.33 
							null_resource.RebootDDC2AfterStoreFrontInstall (remote-exec):   Port: 5985 
							null_resource.RebootDDC2AfterStoreFrontInstall (remote-exec):   User: administrator 
							null_resource.RebootDDC2AfterStoreFrontInstall (remote-exec):   Password: true 
							null_resource.RebootDDC2AfterStoreFrontInstall (remote-exec):   HTTPS: false 
							null_resource.RebootDDC2AfterStoreFrontInstall (remote-exec):   Insecure: false 
							null_resource.RebootDDC2AfterStoreFrontInstall (remote-exec):   NTLM: false 
							null_resource.RebootDDC2AfterStoreFrontInstall (remote-exec):   CACert: false 
							null_resource.RebootDDC2AfterStoreFrontInstall (remote-exec): Connected! 
							null_resource.InstallSFOnDDC1: Still creating... [31s elapsed] 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.InstallSFOnDDC1: Creation complete after 31s [id=8342555066784405312] 
							null_resource.RebootDDC1AfterStoreFrontInstall: Creating... 
							null_resource.RebootDDC1AfterStoreFrontInstall: Provisioning with 'remote-exec'... 
							null_resource.RebootDDC1AfterStoreFrontInstall (remote-exec): Connecting to remote host via WinRM... 
							null_resource.RebootDDC1AfterStoreFrontInstall (remote-exec):   Host: 10.10.111.31 
							null_resource.RebootDDC1AfterStoreFrontInstall (remote-exec):   Port: 5985 
							null_resource.RebootDDC1AfterStoreFrontInstall (remote-exec):   User: administrator 
							null_resource.RebootDDC1AfterStoreFrontInstall (remote-exec):   Password: true 
							null_resource.RebootDDC1AfterStoreFrontInstall (remote-exec):   HTTPS: false 
							null_resource.RebootDDC1AfterStoreFrontInstall (remote-exec):   Insecure: false 
							null_resource.RebootDDC1AfterStoreFrontInstall (remote-exec):   NTLM: false 
							null_resource.RebootDDC1AfterStoreFrontInstall (remote-exec):   CACert: false 
							null_resource.RebootDDC1AfterStoreFrontInstall (remote-exec): Connected! 
							 
							null_resource.RebootDDC2AfterStoreFrontInstall (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/Reboot.ps1 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
							null_resource.RebootDDC1AfterStoreFrontInstall (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/Reboot.ps1 
							null_resource.RebootDDC2AfterStoreFrontInstall: Creation complete after 3s [id=697642514356383378] 
							time_sleep.WaitForRebootofDDC2AfterStorefrontInstall: Creating... 
							null_resource.RebootDDC1AfterStoreFrontInstall: Creation complete after 3s [id=1616716412766144196] 
							time_sleep.WaitForRebootofDDC1AfterStoreFrontInstall: Creating... 
							time_sleep.WaitForRebootofDDC2AfterStorefrontInstall: Still creating... [10s elapsed] 
							time_sleep.WaitForRebootofDDC1AfterStoreFrontInstall: Still creating... [10s elapsed] 
							time_sleep.WaitForRebootofDDC2AfterStorefrontInstall: Still creating... [20s elapsed] 
							time_sleep.WaitForRebootofDDC1AfterStoreFrontInstall: Still creating... [20s elapsed] 
							... 
							time_sleep.WaitForRebootofDDC2AfterStorefrontInstall: Still creating... [1m50s elapsed] 
							time_sleep.WaitForRebootofDDC1AfterStoreFrontInstall: Still creating... [1m50s elapsed] 
							time_sleep.WaitForRebootofDDC2AfterStorefrontInstall: Creation complete after 2m0s [id=2024-09-09T13:16:16Z] 
							time_sleep.WaitForRebootofDDC1AfterStoreFrontInstall: Creation complete after 2m0s [id=2024-09-09T13:16:17Z]
						 

						
							null_resource.InstallCVADSite: Creating... 
							null_resource.InstallCVADSite: Provisioning with 'local-exec'... 
							null_resource.InstallCVADSite (local-exec): Executing: ["PowerShell" "-Command" ".'./data/CVAD-Site.ps1'"] 
							null_resource.InstallCVADSite: Still creating... [10s elapsed] 
							null_resource.InstallCVADSite: Still creating... [20s elapsed] 
							null_resource.InstallCVADSite: Still creating... [30s elapsed] 
							null_resource.InstallCVADSite: Still creating... [40s elapsed] 
							 
							 
							null_resource.InstallCVADSite (local-exec): Controllers        : {TACG-XS-DDC1.the-austrian-citrix-guy.at} 
							null_resource.InstallCVADSite (local-exec): Databases          : {Site, Logging, Monitor} 
							null_resource.InstallCVADSite (local-exec): DefaultIconUid     : 1 
							null_resource.InstallCVADSite (local-exec): LicenseInformation : PLT 
							null_resource.InstallCVADSite (local-exec): Metadata           : {[ConfiguredComponents, Admin Config Log Acct Hyp AppLib Prov Broker Lic Monitor Pvs Trust 
							null_resource.InstallCVADSite (local-exec):                      EnvTest AppV Sf Analytics Orch], [Studio_SiteConfigurationComplete, True], 
							null_resource.InstallCVADSite (local-exec):                      [Upgrade_PolicyPublishedNamesCompleted, true]} 
							null_resource.InstallCVADSite (local-exec): Name               : TACG-TF-XS8 
							 
							null_resource.InstallCVADSite: Still creating... [50s elapsed] 
							 
							 
							null_resource.InstallCVADSite: Creation complete after 52s [id=6498953504599977173]
						 

						
							time_sleep.WaitAgainForRebootofDDC1: Creating... 
							time_sleep.WaitAgainForRebootofDDC1: Still creating... [10s elapsed] 
							time_sleep.WaitAgainForRebootofDDC1: Still creating... [20s elapsed] 
							time_sleep.WaitAgainForRebootofDDC1: Still creating... [30s elapsed] 
							time_sleep.WaitAgainForRebootofDDC1: Still creating... [40s elapsed] 
							time_sleep.WaitAgainForRebootofDDC1: Still creating... [50s elapsed] 
							time_sleep.WaitAgainForRebootofDDC1: Still creating... [1m0s elapsed] 
							time_sleep.WaitAgainForRebootofDDC1: Still creating... [1m10s elapsed] 
							time_sleep.WaitAgainForRebootofDDC1: Still creating... [1m20s elapsed] 
							time_sleep.WaitAgainForRebootofDDC1: Still creating... [1m30s elapsed] 
							time_sleep.WaitAgainForRebootofDDC1: Still creating... [1m40s elapsed] 
							time_sleep.WaitAgainForRebootofDDC1: Still creating... [1m50s elapsed] 
							time_sleep.WaitAgainForRebootofDDC1: Creation complete after 2m0s [id=2024-09-09T14:11:11Z] 
							null_resource.AddToCVADSite: Creating... 
							null_resource.AddToCVADSite: Provisioning with 'file'... 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.AddToCVADSite: Provisioning with 'file'... 
							null_resource.AddToCVADSite: Creating... 
							null_resource.AddDNSRecord: Creating... 
							null_resource.AddDNSRecord: Provisioning with 'file'... 
							null_resource.AddToCVADSite: Provisioning with 'file'... 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Module werden für erstmalige Verwendung vorbereitet.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.AddToCVADSite: Provisioning with 'remote-exec'... 
							null_resource.AddToCVADSite (remote-exec): Connecting to remote host via WinRM... 
							null_resource.AddToCVADSite (remote-exec):   Host: 10.10.111.31 
							null_resource.AddToCVADSite (remote-exec):   Port: 5985 
							null_resource.AddToCVADSite (remote-exec):   User: administrator 
							null_resource.AddToCVADSite (remote-exec):   Password: true 
							null_resource.AddToCVADSite (remote-exec):   HTTPS: false 
							null_resource.AddToCVADSite (remote-exec):   Insecure: false 
							null_resource.AddToCVADSite (remote-exec):   NTLM: false 
							null_resource.AddToCVADSite (remote-exec):   CACert: false 
							null_resource.AddToCVADSite (remote-exec): Connected! 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Module werden für erstmalige Verwendung vorbereitet.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.AddDNSRecord: Provisioning with 'remote-exec'... 
							null_resource.AddDNSRecord (remote-exec): Connecting to remote host via WinRM... 
							null_resource.AddDNSRecord (remote-exec):   Host: 10.10.100.1 
							null_resource.AddDNSRecord (remote-exec):   Port: 5985 
							null_resource.AddDNSRecord (remote-exec):   User: administrator 
							null_resource.AddDNSRecord (remote-exec):   Password: true 
							null_resource.AddDNSRecord (remote-exec):   HTTPS: false 
							null_resource.AddDNSRecord (remote-exec):   Insecure: false 
							null_resource.AddDNSRecord (remote-exec):   NTLM: false 
							null_resource.AddDNSRecord (remote-exec):   CACert: false 
							null_resource.AddDNSRecord (remote-exec): Connected! 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Module werden für erstmalige Verwendung vorbereitet.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Module werden für erstmalige Verwendung vorbereitet.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
							null_resource.AddToCVADSite (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/CVAD-AddToSite.ps1 
							 
							null_resource.AddDNSRecord (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/CVAD-CreateDNSARecordOnDC.ps1#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
							null_resource.AddToCVADSite (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/CVAD-AddToSite.ps1 
							time_sleep.WaitAgainAgainForRebootofDDC1: Creating... 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Module werden für erstmalige Verwendung vorbereitet.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.AddDNSRecord: Creation complete after 4s [id=6275517347099279596] 
							time_sleep.WaitAgainAgainForRebootofDDC1: Still creating... [10s elapsed] 
							time_sleep.WaitAgainAgainForRebootofDDC1: Still creating... [20s elapsed] 
							time_sleep.WaitAgainAgainForRebootofDDC1: Still creating... [30s elapsed] 
							time_sleep.WaitAgainAgainForRebootofDDC1: Still creating... [40s elapsed] 
							time_sleep.WaitAgainAgainForRebootofDDC1: Still creating... [50s elapsed] 
							time_sleep.WaitAgainAgainForRebootofDDC1: Still creating... [1m0s elapsed] 
							time_sleep.WaitAgainAgainForRebootofDDC1: Still creating... [1m10s elapsed] 
							time_sleep.WaitAgainAgainForRebootofDDC1: Still creating... [1m20s elapsed] 
							time_sleep.WaitAgainAgainForRebootofDDC1: Still creating... [1m30s elapsed] 
							time_sleep.WaitAgainAgainForRebootofDDC1: Still creating... [1m40s elapsed] 
							time_sleep.WaitAgainAgainForRebootofDDC1: Still creating... [1m50s elapsed] 
							time_sleep.WaitAgainAgainForRebootofDDC1: Creation complete after 2m0s [id=2024-09-09T14:26:08Z] 
							 
							Apply complete! Resources: 25 added, 0 changed, 0 destroyed. 
							PS C:\_TACG\_CVADOnXenServer-InitialCVADConfiguration&gt;
						 
					
				
			
		
	

	
		
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}		
	

	
		Before configuring the Storefront cluster, Terraform deletes all existing StoreFront deployments;
	 

	
		
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}		
		
			
				
					
						
							PS C:\_TACG\_CVADOnXenServer-InitialCVADConfiguration&gt; terraform init 
							Initializing the backend... 
							Initializing provider plugins... 
							- Finding latest version of hashicorp/null... 
							- Finding latest version of xenserver/xenserver... 
							- Finding citrix/citrix versions matching "1.0.1"... 
							- Finding latest version of hashicorp/local... 
							- Installing hashicorp/null v3.2.2... 
							- Installed hashicorp/null v3.2.2 (signed by HashiCorp) 
							- Installing xenserver/xenserver v0.1.1... 
							- Installed xenserver/xenserver v0.1.1 (self-signed, key ID FABD99A0D7648275) 
							- Installing citrix/citrix v1.0.1... 
							- Installed citrix/citrix v1.0.1 (self-signed, key ID BD4BD0E690CB7D88) 
							- 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\_CVADOnXenServer-InitialCVADConfiguration&gt; terraform plan 
							 
							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.WriteClearStoreFrontDeploymentScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteClearStoreFrontDeploymentScriptIntoDataDirectory" { 
							      + content              = &lt;&lt;-EOT 
							            Clear-STFDeployment -Confirm:$false 
							        EOT 
							      + 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             = "./data/CVAD-ClearSTF.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # null_resource.UploadAndRunSFDeletionScriptOnDDC1 will be created 
							  + resource "null_resource" "UploadAndRunSFDeletionScriptOnDDC1" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.UploadAndRunSFDeletionScriptOnDDC2 will be created 
							  + resource "null_resource" "UploadAndRunSFDeletionScriptOnDDC2" { 
							      + id = (known after apply) 
							    } 
							 
							Plan: 3 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\_CVADOnXenServer-InitialCVADConfiguration&gt; terraform apply 
							 
							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.WriteClearStoreFrontDeploymentScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteClearStoreFrontDeploymentScriptIntoDataDirectory" { 
							      + content              = &lt;&lt;-EOT 
							            Clear-STFDeployment -Confirm:$false 
							        EOT 
							      + 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             = "./data/CVAD-ClearSTF.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # null_resource.UploadAndRunSFDeletionScriptOnDDC1 will be created 
							  + resource "null_resource" "UploadAndRunSFDeletionScriptOnDDC1" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.UploadAndRunSFDeletionScriptOnDDC2 will be created 
							  + resource "null_resource" "UploadAndRunSFDeletionScriptOnDDC2" { 
							      + id = (known after apply) 
							    } 
							 
							Plan: 3 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 
							 
							local_file.WriteClearStoreFrontDeploymentScriptIntoDataDirectory: Creating... 
							local_file.WriteClearStoreFrontDeploymentScriptIntoDataDirectory: Creation complete after 0s [id=6b78ab95c19fdd7629736279b0f7ee08cade7945] 
							null_resource.UploadAndRunSFDeletionScriptOnDDC2: Creating... 
							null_resource.UploadAndRunSFDeletionScriptOnDDC1: Creating... 
							null_resource.UploadAndRunSFDeletionScriptOnDDC1: Provisioning with 'file'... 
							null_resource.UploadAndRunSFDeletionScriptOnDDC2: Provisioning with 'file'... 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadAndRunSFDeletionScriptOnDDC1: Provisioning with 'remote-exec'... 
							null_resource.UploadAndRunSFDeletionScriptOnDDC1 (remote-exec): Connecting to remote host via WinRM... 
							null_resource.UploadAndRunSFDeletionScriptOnDDC1 (remote-exec):   Host: 10.10.111.31 
							null_resource.UploadAndRunSFDeletionScriptOnDDC1 (remote-exec):   Port: 5985 
							null_resource.UploadAndRunSFDeletionScriptOnDDC1 (remote-exec):   User: administrator 
							null_resource.UploadAndRunSFDeletionScriptOnDDC1 (remote-exec):   Password: true 
							null_resource.UploadAndRunSFDeletionScriptOnDDC1 (remote-exec):   HTTPS: false 
							null_resource.UploadAndRunSFDeletionScriptOnDDC1 (remote-exec):   Insecure: false 
							null_resource.UploadAndRunSFDeletionScriptOnDDC1 (remote-exec):   NTLM: false 
							null_resource.UploadAndRunSFDeletionScriptOnDDC1 (remote-exec):   CACert: false 
							null_resource.UploadAndRunSFDeletionScriptOnDDC1 (remote-exec): Connected! 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadAndRunSFDeletionScriptOnDDC2: Provisioning with 'remote-exec'... 
							null_resource.UploadAndRunSFDeletionScriptOnDDC2 (remote-exec): Connecting to remote host via WinRM... 
							null_resource.UploadAndRunSFDeletionScriptOnDDC2 (remote-exec):   Host: 10.10.111.33 
							null_resource.UploadAndRunSFDeletionScriptOnDDC2 (remote-exec):   Port: 5985 
							null_resource.UploadAndRunSFDeletionScriptOnDDC2 (remote-exec):   User: administrator 
							null_resource.UploadAndRunSFDeletionScriptOnDDC2 (remote-exec):   Password: true 
							null_resource.UploadAndRunSFDeletionScriptOnDDC2 (remote-exec):   HTTPS: false 
							null_resource.UploadAndRunSFDeletionScriptOnDDC2 (remote-exec):   Insecure: false 
							null_resource.UploadAndRunSFDeletionScriptOnDDC2 (remote-exec):   NTLM: false 
							null_resource.UploadAndRunSFDeletionScriptOnDDC2 (remote-exec):   CACert: false 
							null_resource.UploadAndRunSFDeletionScriptOnDDC2 (remote-exec): Connected! 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
							null_resource.UploadAndRunSFDeletionScriptOnDDC1 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/CVAD-ClearSTF.ps1 
							 
							null_resource.UploadAndRunSFDeletionScriptOnDDC2 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/CVAD-ClearSTF.ps1 
							null_resource.UploadAndRunSFDeletionScriptOnDDC1 (remote-exec): WARNING: No deployments configured 
							null_resource.UploadAndRunSFDeletionScriptOnDDC2 (remote-exec): WARNING: No deployments configured 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadAndRunSFDeletionScriptOnDDC1: Creation complete after 3s [id=1278400339704868689] 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadAndRunSFDeletionScriptOnDDC2: Creation complete after 3s [id=3382560375518362931] 
							 
							Apply complete! Resources: 3 added, 0 changed, 0 destroyed. 
							PS C:\_TACG\_CVADOnXenServer-InitialCVADConfiguration&gt;
						 
					
				
			
		
	

	
		 
		After the deletion of existing deployments, Terraform configures a StoreFront cluster according to our needs:
	 

	
		
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}		
		
			
				
					
						
							PS C:\_TACG\_CVADOnXenServer-InitialCVADConfiguration&gt; terraform init 
							Initializing the backend... 
							Initializing provider plugins... 
							- Finding latest version of hashicorp/null... 
							- Finding latest version of xenserver/xenserver... 
							- Finding citrix/citrix versions matching "1.0.1"... 
							- Finding latest version of hashicorp/local... 
							- Installing xenserver/xenserver v0.1.1... 
							- Installed xenserver/xenserver v0.1.1 (self-signed, key ID FABD99A0D7648275) 
							- Installing citrix/citrix v1.0.1... 
							- Installed citrix/citrix v1.0.1 (self-signed, key ID BD4BD0E690CB7D88) 
							- 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) 
							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\_CVADOnXenServer-InitialCVADConfiguration&gt; terraform plan 
							 
							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_stf_authentication_service.CreateSTFAuthenticationService will be created 
							  + resource "citrix_stf_authentication_service" "CreateSTFAuthenticationService" { 
							      + claims_factory_name = "standardClaimsFactory" 
							      + friendly_name       = "Auth" 
							      + site_id             = "1" 
							      + virtual_path        = "/Citrix/Authentication" 
							    } 
							 
							  # citrix_stf_deployment.CreateSTFDeployment will be created 
							  + resource "citrix_stf_deployment" "CreateSTFDeployment" { 
							      + host_base_url = "https://storefront-xs8.the-austrian-citrix-guy.at" 
							      + site_id       = "1" 
							    } 
							 
							  # citrix_stf_roaming_beacon.CreateSTFRoamingBeacon will be created 
							  + resource "citrix_stf_roaming_beacon" "CreateSTFRoamingBeacon" { 
							      + external_ips = [ 
							          + "https://www.orf.at", 
							          + "https://www.google.com", 
							        ] 
							      + internal_ip  = "https://storefront-xs8.the-austrian-citrix-guy.at" 
							      + site_id      = 1 
							    } 
							 
							  # citrix_stf_roaming_gateway.CreateSTFRoamingGateway will be created 
							  + resource "citrix_stf_roaming_gateway" "CreateSTFRoamingGateway" { 
							      + callback_url                   = "https://ns.the-austrian-citrix-guy.at" 
							      + deployment                     = (known after apply) 
							      + edition                        = (known after apply) 
							      + gateway_url                    = "https://ns.the-austrian-citrix-guy.at" 
							      + gslb_url                       = "" 
							      + is_cloud_gateway               = false 
							      + logon_type                     = "Domain" 
							      + name                           = "NSGW TACG" 
							      + request_ticket_two_stas        = false 
							      + secure_ticket_authority_urls   = [ 
							          + { 
							              + authority_id           = (known after apply) 
							              + sta_url                = "http://TACG-XS8-DDC1.the-austrian-citrix-guy.at/scripts/ctxsta.dll" 
							              + sta_validation_enabled = false 
							            }, 
							          + { 
							              + authority_id           = (known after apply) 
							              + sta_url                = "http://TACG-XS8-DDC1.the-austrian-citrix-guy.at/scripts/ctxsta.dll" 
							              + sta_validation_enabled = false 
							            }, 
							        ] 
							      + session_reliability            = false 
							      + site_id                        = "1" 
							      + smart_card_fallback_logon_type = "None" 
							      + stas_bypass_duration           = "0.1:0:0" 
							      + stas_use_load_balancing        = false 
							      + subnet_ip_address              = "10.10.111.251" 
							      + version                        = "Version10_0_69_4" 
							    } 
							 
							  # citrix_stf_store_service.CreateSTFStoreService will be created 
							  + resource "citrix_stf_store_service" "CreateSTFStoreService" { 
							      + authentication_service_virtual_path = "/Citrix/Authentication" 
							      + farms                               = [ 
							          + { 
							              + all_failed_bypass_duration     = 0 
							              + bypass_duration                = 60 
							              + farm_name                      = "TACG-TF-XS8" 
							              + farm_type                      = "XenDesktop" 
							              + load_balance                   = true 
							              + max_failed_servers_per_request = 0 
							              + port                           = 443 
							              + rade_ticket_time_to_live       = 100 
							              + server_urls                    = [] 
							              + servers                        = [ 
							                  + "tacg-xs-ddc1.the-austrian-citrix-guy.at", 
							                  + "tacg-xs-ddc2.the-austrian-citrix-guy.at", 
							                ] 
							              + ssl_relay_port                 = 443 
							              + ticket_time_to_live            = 200 
							              + transport_type                 = "HTTPS" 
							              + xml_validation_enabled         = false 
							              + zones                          = [] 
							                # (4 unchanged attributes hidden) 
							            }, 
							        ] 
							      + friendly_name                       = "Store" 
							      + pna                                 = { 
							          + enable = true 
							        } 
							      + site_id                             = "1" 
							      + virtual_path                        = "/Citrix/Store" 
							    } 
							 
							  # citrix_stf_webreceiver_service.CreateSTFWebReceiverService will be created 
							  + resource "citrix_stf_webreceiver_service" "CreateSTFWebReceiverService" { 
							      + friendly_name      = "StoreWeb" 
							      + site_id            = "1" 
							      + store_virtual_path = "/Citrix/Store" 
							      + virtual_path       = "/Citrix/StoreWeb" 
							    } 
							 
							  # citrix_stf_xenapp_default_store.CreateSTFDefaultStore will be created 
							  + resource "citrix_stf_xenapp_default_store" "CreateSTFDefaultStore" { 
							      + store_site_id      = "1" 
							      + store_virtual_path = "/Citrix/Store" 
							    } 
							 
							  # local_file.WriteClearStoreFrontDeploymentScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteClearStoreFrontDeploymentScriptIntoDataDirectory" { 
							      + content              = &lt;&lt;-EOT 
							            Clear-STFDeployment -Confirm:$false 
							        EOT 
							      + 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             = "./data/CVAD-ClearSTF.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # null_resource.UploadAndRunSFDeletionScriptOnDDC1 will be created 
							  + resource "null_resource" "UploadAndRunSFDeletionScriptOnDDC1" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.UploadAndRunSFDeletionScriptOnDDC2 will be created 
							  + resource "null_resource" "UploadAndRunSFDeletionScriptOnDDC2" { 
							      + id = (known after apply) 
							    } 
							 
							Plan: 10 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\_CVADOnXenServer-InitialCVADConfiguration&gt; terraform validate 
							Success! The configuration is valid. 
							 
							PS C:\_TACG\_CVADOnXenServer-InitialCVADConfiguration&gt; terraform apply 
							 
							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_stf_authentication_service.CreateSTFAuthenticationService will be created 
							  + resource "citrix_stf_authentication_service" "CreateSTFAuthenticationService" { 
							      + claims_factory_name = "standardClaimsFactory" 
							      + friendly_name       = "Auth" 
							      + site_id             = "1" 
							      + virtual_path        = "/Citrix/Authentication" 
							    } 
							 
							  # citrix_stf_deployment.CreateSTFDeployment will be created 
							  + resource "citrix_stf_deployment" "CreateSTFDeployment" { 
							      + host_base_url = "https://storefront-xs8.the-austrian-citrix-guy.at" 
							      + site_id       = "1" 
							    } 
							 
							  # citrix_stf_roaming_beacon.CreateSTFRoamingBeacon will be created 
							  + resource "citrix_stf_roaming_beacon" "CreateSTFRoamingBeacon" { 
							      + external_ips = [ 
							          + "https://www.orf.at", 
							          + "https://www.google.com", 
							        ] 
							      + internal_ip  = "https://storefront-xs8.the-austrian-citrix-guy.at" 
							      + site_id      = 1 
							    } 
							 
							  # citrix_stf_roaming_gateway.CreateSTFRoamingGateway will be created 
							  + resource "citrix_stf_roaming_gateway" "CreateSTFRoamingGateway" { 
							      + callback_url                   = "https://ns.the-austrian-citrix-guy.at" 
							      + deployment                     = (known after apply) 
							      + edition                        = (known after apply) 
							      + gateway_url                    = "https://ns.the-austrian-citrix-guy.at" 
							      + gslb_url                       = "" 
							      + is_cloud_gateway               = false 
							      + logon_type                     = "Domain" 
							      + name                           = "NSGW TACG" 
							      + request_ticket_two_stas        = false 
							      + secure_ticket_authority_urls   = [ 
							          + { 
							              + authority_id           = (known after apply) 
							              + sta_url                = "http://TACG-XS8-DDC1.the-austrian-citrix-guy.at/scripts/ctxsta.dll" 
							              + sta_validation_enabled = false 
							            }, 
							          + { 
							              + authority_id           = (known after apply) 
							              + sta_url                = "http://TACG-XS8-DDC1.the-austrian-citrix-guy.at/scripts/ctxsta.dll" 
							              + sta_validation_enabled = false 
							            }, 
							        ] 
							      + session_reliability            = false 
							      + site_id                        = "1" 
							      + smart_card_fallback_logon_type = "None" 
							      + stas_bypass_duration           = "0.1:0:0" 
							      + stas_use_load_balancing        = false 
							      + subnet_ip_address              = "10.10.111.251" 
							      + version                        = "Version10_0_69_4" 
							    } 
							 
							  # citrix_stf_store_service.CreateSTFStoreService will be created 
							  + resource "citrix_stf_store_service" "CreateSTFStoreService" { 
							      + authentication_service_virtual_path = "/Citrix/Authentication" 
							      + farms                               = [ 
							          + { 
							              + all_failed_bypass_duration     = 0 
							              + bypass_duration                = 60 
							              + farm_name                      = "TACG-TF-XS8" 
							              + farm_type                      = "XenDesktop" 
							              + load_balance                   = true 
							              + max_failed_servers_per_request = 0 
							              + port                           = 443 
							              + rade_ticket_time_to_live       = 100 
							              + server_urls                    = [] 
							              + servers                        = [ 
							                  + "tacg-xs-ddc1.the-austrian-citrix-guy.at", 
							                  + "tacg-xs-ddc2.the-austrian-citrix-guy.at", 
							                ] 
							              + ssl_relay_port                 = 443 
							              + ticket_time_to_live            = 200 
							              + transport_type                 = "HTTPS" 
							              + xml_validation_enabled         = false 
							              + zones                          = [] 
							                # (4 unchanged attributes hidden) 
							            }, 
							        ] 
							      + friendly_name                       = "Store" 
							      + pna                                 = { 
							          + enable = true 
							        } 
							      + site_id                             = "1" 
							      + virtual_path                        = "/Citrix/Store" 
							    } 
							 
							  # citrix_stf_webreceiver_service.CreateSTFWebReceiverService will be created 
							  + resource "citrix_stf_webreceiver_service" "CreateSTFWebReceiverService" { 
							      + friendly_name      = "StoreWeb" 
							      + site_id            = "1" 
							      + store_virtual_path = "/Citrix/Store" 
							      + virtual_path       = "/Citrix/StoreWeb" 
							    } 
							 
							  # citrix_stf_xenapp_default_store.CreateSTFDefaultStore will be created 
							  + resource "citrix_stf_xenapp_default_store" "CreateSTFDefaultStore" { 
							      + store_site_id      = "1" 
							      + store_virtual_path = "/Citrix/Store" 
							    } 
							 
							  # local_file.WriteClearStoreFrontDeploymentScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteClearStoreFrontDeploymentScriptIntoDataDirectory" { 
							      + content              = &lt;&lt;-EOT 
							            Clear-STFDeployment -Confirm:$false 
							        EOT 
							      + 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             = "./data/CVAD-ClearSTF.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # null_resource.UploadAndRunSFDeletionScriptOnDDC1 will be created 
							  + resource "null_resource" "UploadAndRunSFDeletionScriptOnDDC1" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.UploadAndRunSFDeletionScriptOnDDC2 will be created 
							  + resource "null_resource" "UploadAndRunSFDeletionScriptOnDDC2" { 
							      + id = (known after apply) 
							    } 
							 
							Plan: 10 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 
							 
							local_file.WriteClearStoreFrontDeploymentScriptIntoDataDirectory: Creating... 
							local_file.WriteClearStoreFrontDeploymentScriptIntoDataDirectory: Creation complete after 0s [id=6b78ab95c19fdd7629736279b0f7ee08cade7945] 
							null_resource.UploadAndRunSFDeletionScriptOnDDC1: Creating... 
							null_resource.UploadAndRunSFDeletionScriptOnDDC2: Creating... 
							null_resource.UploadAndRunSFDeletionScriptOnDDC2: Provisioning with 'file'... 
							null_resource.UploadAndRunSFDeletionScriptOnDDC1: Provisioning with 'file'... 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadAndRunSFDeletionScriptOnDDC2: Provisioning with 'remote-exec'...                                                                                                                                                                  null_resource.UploadAndRunSFDeletionScriptOnDDC2 (remote-exec): Connecting to remote host via WinRM...                                                                                                           null_resource.UploadAndRunSFDeletionScriptOnDDC2 (remote-exec):   Host: 10.10.111.33                                                                                                                             null_resource.UploadAndRunSFDeletionScriptOnDDC2 (remote-exec):   Port: 5985                                                                                                                                     null_resource.UploadAndRunSFDeletionScriptOnDDC2 (remote-exec):   NTLM: false 
							null_resource.UploadAndRunSFDeletionScriptOnDDC2 (remote-exec):   CACert: false 
							null_resource.UploadAndRunSFDeletionScriptOnDDC2 (remote-exec): Connected! 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadAndRunSFDeletionScriptOnDDC1: Provisioning with 'remote-exec'... 
							null_resource.UploadAndRunSFDeletionScriptOnDDC1 (remote-exec): Connecting to remote host via WinRM... 
							null_resource.UploadAndRunSFDeletionScriptOnDDC1 (remote-exec):   Host: 10.10.111.31 
							null_resource.UploadAndRunSFDeletionScriptOnDDC1 (remote-exec):   Port: 5985 
							null_resource.UploadAndRunSFDeletionScriptOnDDC1 (remote-exec):   User: administrator 
							null_resource.UploadAndRunSFDeletionScriptOnDDC1 (remote-exec):   Password: true 
							null_resource.UploadAndRunSFDeletionScriptOnDDC1 (remote-exec):   HTTPS: false 
							null_resource.UploadAndRunSFDeletionScriptOnDDC1 (remote-exec):   Insecure: false 
							null_resource.UploadAndRunSFDeletionScriptOnDDC1 (remote-exec):   NTLM: false 
							null_resource.UploadAndRunSFDeletionScriptOnDDC1 (remote-exec):   CACert: false 
							null_resource.UploadAndRunSFDeletionScriptOnDDC1 (remote-exec): Connected! 
							citrix_stf_deployment.CreateSTFDeployment: Creating... 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
							null_resource.UploadAndRunSFDeletionScriptOnDDC2 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/CVAD-ClearSTF.ps1 
							null_resource.UploadAndRunSFDeletionScriptOnDDC2 (remote-exec): WARNING: No deployments configured 
							 
							null_resource.UploadAndRunSFDeletionScriptOnDDC1 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/CVAD-ClearSTF.ps1 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadAndRunSFDeletionScriptOnDDC2: Creation complete after 2s [id=7097935231284650493] 
							null_resource.UploadAndRunSFDeletionScriptOnDDC1 (remote-exec): WARNING: No deployments configured 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadAndRunSFDeletionScriptOnDDC1: Creation complete after 4s [id=4288398068901764598] 
							citrix_stf_deployment.CreateSTFDeployment: Still creating... [10s elapsed] 
							citrix_stf_deployment.CreateSTFDeployment: Still creating... [20s elapsed] 
							citrix_stf_deployment.CreateSTFDeployment: Still creating... [30s elapsed] 
							citrix_stf_deployment.CreateSTFDeployment: Still creating... [40s elapsed] 
							citrix_stf_deployment.CreateSTFDeployment: Creation complete after 43s 
							citrix_stf_authentication_service.CreateSTFAuthenticationService: Creating... 
							citrix_stf_roaming_gateway.CreateSTFRoamingGateway: Creating... 
							citrix_stf_authentication_service.CreateSTFAuthenticationService: Creation complete after 6s 
							citrix_stf_store_service.CreateSTFStoreService: Creating... 
							citrix_stf_store_service.CreateSTFStoreService: Still creating... [10s elapsed] 
							citrix_stf_store_service.CreateSTFStoreService: Creation complete after 13s 
							citrix_stf_xenapp_default_store.CreateSTFDefaultStore: Creating... 
							citrix_stf_webreceiver_service.CreateSTFWebReceiverService: Creating... 
							citrix_stf_roaming_beacon.CreateSTFRoamingBeacon: Creating... 
							citrix_stf_xenapp_default_store.CreateSTFDefaultStore: Creation complete after 4s 
							citrix_stf_webreceiver_service.CreateSTFWebReceiverService: Creation complete after 8s 
							citrix_stf_roaming_beacon.CreateSTFRoamingBeacon: Creation complete after 6s
						 

						
							Apply complete! Resources: 10 added, 0 changed, 0 destroyed. 
							PS C:\_TACG\_CVADOnXenServer-InitialCVADConfiguration&gt;
						 

						
							 
						 
					
				
			
		
	

	
		 
	 

	
		 
	 

	
		Caution: 
		 
		During tests of the Terraform scripts, exceptions occurred while Terraform created a new StoreFront deployment. 
		The Event viewer showed three consecutive errors, and one of the error messages contained the following: 
		System.InvalidOperationException: The installation failed, and the rollback has been performed. ---&gt; System.InvalidOperationException: Cannot load Counter Name data because an invalid index '' was read from the registry. 
		 
		There is a solution for a similar issue available in Citrix KnowledgeBase, which solved the problem each time, and Terraform was able to create the StoreFront deployment: 
		https://support.citrix.com/s/article/CTX559445-session-recording-agent-installaltion-f[…]e-due-to-error-cannot-load-counter-name-data?language=en_US 
		 
	 

	
		 
	 

	
		
	 

	
		 
		After solving this problem, Terraform successfully deployed the needed Software components: 
		
	 

	
		 
		Terraform successfully deployed and configured the StoreFront cluster: 
		
	 

	
		
	 

	
		Terraform has successfully created the databases: 
		
	 

	
		Terraform has successfully created the Citrix Virtual Apps and Desktops site: 
		
	 

	
		The Terraform flow was completed successfully, and the next module can begin. 
		 
	 

	
		Module 4:  Configure prerequisites for deploying Citrix Virtual Apps and Desktop entities
	

	
		This module is split into the following configuration parts:
	 

	
		
			Changing the SSL certificate on the Delivery Controllers/StoreFront servers to a publicly signed certificate to avoid certificate issues
		
		
			Uploading a valid license file to a License Server locally installed on the Primary Delivery Controller (optional)
		
		
			Setting the License Configuration of the site to point to a central License Server (optional)
		
	

	
		After creating and configuring the StoreFront cluster, some final configuration steps are needed before we can deploy the Citrix Virtual Apps and Desktop-related entities in Module 5. 
		Prior to running the Terraform scripts of Module 4, IIS has a self-signed certificate installed, and the Citrix Virtual Apps and Desktops-site has no valid Licenses installed/no License Server connected to:
	 

	
		 
		 
	 

	
		
	 

	
		
	 
	 

	
		The certificate is needed in .pfx format, and the correct License file must reside in this module's DATA directory if a local License Server should be used. 
		Otherwise, a central License Server will be configured. Please adjust the Terraform file accordingly. 
		 
		The corresponding Terraform file containing all needed variables point to the DATA directory.
	 

	
		Important:  
		 
		This module can only be run on the previously created Administrative VM, as the following modules rely heavily on WinRM.  
		Please install Terraform on the Administrative VM before trying to run the scripts. 
		 
	 

	
		
			 
		
	

	
		Important: 
		 
		Before starting the Terraform workflow, please ensure you have configured the variables according to your needs using the corresponding .auto.tfvars.json and/or terraform.tfvars file. 
		 
	 

	
		The configuration can be started by following the standard Terraform workflow:
	 

	
		terraform init, 
		terraform plan 
		 
	 

	
		and if no errors occur
	 

	
		terraform apply
	 

	
		
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}		
	

	
		 
	 

	
		
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}		
		
			
				
					
						
							PS C:\_TACG\_CVADOnXenServer-Configuration-AfterStoreFront&gt; terraform init 
							Initializing the backend... 
							Initializing provider plugins... 
							- Finding citrix/citrix versions matching "1.0.1"... 
							- Finding latest version of hashicorp/local... 
							- Finding latest version of hashicorp/null... 
							- Installing citrix/citrix v1.0.1... 
							- Installed citrix/citrix v1.0.1 (self-signed, key ID BD4BD0E690CB7D88) 
							- 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) 
							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\_CVADOnXenServer-Configuration-AfterStoreFront&gt; terraform plan 
							 
							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.WriteIISSSLScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteIISSSLScriptIntoDataDirectory" { 
							      + 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             = "./data/CVAD-IIS.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WriteLicScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteLicScriptIntoDataDirectory" { 
							      + 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             = "./data/CVAD-Lic.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WritePFXScriptIntoDataDirectory will be created 
							  + resource "local_file" "WritePFXScriptIntoDataDirectory" { 
							      + 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             = "./data/CVAD-PFX.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # null_resource.ChangeSSLOnDDC1 will be created 
							  + resource "null_resource" "ChangeSSLOnDDC1" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.ChangeSSLOnDDC2 will be created 
							  + resource "null_resource" "ChangeSSLOnDDC2" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.CreateCVADLicPFXOnDDC1 will be created 
							  + resource "null_resource" "CreateCVADLicPFXOnDDC1" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.CreateCVADPFXOnDDC2 will be created 
							  + resource "null_resource" "CreateCVADPFXOnDDC2" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.RebootDDC1-2 will be created 
							  + resource "null_resource" "RebootDDC1-2" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.RebootDDC2-2 will be created 
							  + resource "null_resource" "RebootDDC2-2" { 
							      + id = (known after apply) 
							    } 
							 
							Plan: 9 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\_CVADOnXenServer-Configuration-AfterStoreFront&gt; terraform validate 
							Success! The configuration is valid. 
							 
							PS C:\_TACG\_CVADOnXenServer-Configuration-AfterStoreFront&gt; terraform apply 
							 
							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.WriteIISSSLScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteIISSSLScriptIntoDataDirectory" { 
							      + 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             = "./data/CVAD-IIS.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WriteLicScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteLicScriptIntoDataDirectory" { 
							      + 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             = "./data/CVAD-Lic.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WritePFXScriptIntoDataDirectory will be created 
							  + resource "local_file" "WritePFXScriptIntoDataDirectory" { 
							      + 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             = "./data/CVAD-PFX.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # null_resource.ChangeSSLOnDDC1 will be created 
							  + resource "null_resource" "ChangeSSLOnDDC1" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.ChangeSSLOnDDC2 will be created 
							  + resource "null_resource" "ChangeSSLOnDDC2" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.CreateCVADLicPFXOnDDC1 will be created 
							  + resource "null_resource" "CreateCVADLicPFXOnDDC1" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.CreateCVADPFXOnDDC2 will be created 
							  + resource "null_resource" "CreateCVADPFXOnDDC2" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.RebootDDC1-2 will be created 
							  + resource "null_resource" "RebootDDC1-2" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.RebootDDC2-2 will be created 
							  + resource "null_resource" "RebootDDC2-2" { 
							      + id = (known after apply) 
							    } 
							 
							Plan: 9 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 
							 
							local_file.WriteIISSSLScriptIntoDataDirectory: Creating... 
							local_file.WriteLicScriptIntoDataDirectory: Creating... 
							local_file.WriteIISSSLScriptIntoDataDirectory: Creation complete after 0s [id=86bab8825d5d9717e524218d8d52d3eab2c70796] 
							local_file.WriteLicScriptIntoDataDirectory: Creation complete after 0s [id=4420aae49d9a3f1f3cc5e92f9628791fb7c5cc7d] 
							local_file.WritePFXScriptIntoDataDirectory: Creating... 
							local_file.WritePFXScriptIntoDataDirectory: Creation complete after 0s [id=63683eea6a0c276da17943ff455714f492a090d3] 
							null_resource.CreateCVADPFXOnDDC2: Creating... 
							null_resource.CreateCVADLicPFXOnDDC1: Creating... 
							null_resource.CreateCVADLicPFXOnDDC1: Provisioning with 'file'... 
							null_resource.CreateCVADPFXOnDDC2: Provisioning with 'file'... 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.CreateCVADPFXOnDDC2: Provisioning with 'file'... 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.CreateCVADLicPFXOnDDC1: Provisioning with 'file'... 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.CreateCVADLicPFXOnDDC1: Provisioning with 'file'... 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.CreateCVADPFXOnDDC2: Provisioning with 'file'... 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.CreateCVADLicPFXOnDDC1: Provisioning with 'file'... 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.CreateCVADPFXOnDDC2: Provisioning with 'remote-exec'... 
							null_resource.CreateCVADPFXOnDDC2 (remote-exec): Connecting to remote host via WinRM... 
							null_resource.CreateCVADPFXOnDDC2 (remote-exec):   Host: 10.10.111.33 
							null_resource.CreateCVADPFXOnDDC2 (remote-exec):   Port: 5985 
							null_resource.CreateCVADPFXOnDDC2 (remote-exec):   User: administrator 
							null_resource.CreateCVADPFXOnDDC2 (remote-exec):   Password: true 
							null_resource.CreateCVADPFXOnDDC2 (remote-exec):   HTTPS: false 
							null_resource.CreateCVADPFXOnDDC2 (remote-exec):   Insecure: false 
							null_resource.CreateCVADPFXOnDDC2 (remote-exec):   NTLM: false 
							null_resource.CreateCVADPFXOnDDC2 (remote-exec):   CACert: false 
							null_resource.CreateCVADPFXOnDDC2 (remote-exec): Connected! 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.CreateCVADLicPFXOnDDC1: Provisioning with 'remote-exec'... 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec): Connecting to remote host via WinRM... 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   Host: 10.10.111.31 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   Port: 5985 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   User: administrator 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   Password: true 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   HTTPS: false 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   Insecure: false 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   NTLM: false 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   CACert: false 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.CreateCVADLicPFXOnDDC1 (remote-exec): Connected! 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
							null_resource.CreateCVADPFXOnDDC2 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/CVAD-PFX.ps1 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/CVAD-Lic.ps1 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec): Invalid LicenseServerUri parameter. 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):     + CategoryInfo          : InvalidArgument: (:) [Set-ConfigSite], InvalidOperationException 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):     + FullyQualifiedErrorId : Citrix.XDPowerShell.Status.InvalidParameterCombination,Citrix.Configuration.Sdk.FeatureC 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):    hecks.Commands.SetConfigSiteCommand 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):     + PSComputerName        : localhost 
							 
							 
							 
							null_resource.CreateCVADPFXOnDDC2 (remote-exec):    PSParentPath: Microsoft.PowerShell.Security\Certificate::LocalMachine\Webhosting 
							 
							null_resource.CreateCVADPFXOnDDC2 (remote-exec): Thumbprint                                Subject                                PSComputerName 
							null_resource.CreateCVADPFXOnDDC2 (remote-exec): ----------                                -------                                -------------- 
							null_resource.CreateCVADPFXOnDDC2 (remote-exec): AB4CA8AC77068368F4B41AD650858D07679DD709  CN=*.the-austrian-citrix-guy.at        localhost 
							 
							 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.CreateCVADLicPFXOnDDC1: Provisioning with 'remote-exec'... 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec): Connecting to remote host via WinRM... 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   Host: 10.10.111.31 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   Port: 5985 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   User: administrator 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   Password: true 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   HTTPS: false 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   Insecure: false 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   NTLM: false 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   CACert: false 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec): Connected! 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.CreateCVADPFXOnDDC2: Creation complete after 6s [id=1494770023356641697] 
							null_resource.ChangeSSLOnDDC2: Creating... 
							null_resource.ChangeSSLOnDDC2: Provisioning with 'remote-exec'... 
							null_resource.ChangeSSLOnDDC2 (remote-exec): Connecting to remote host via WinRM... 
							null_resource.ChangeSSLOnDDC2 (remote-exec):   Host: 10.10.111.33 
							null_resource.ChangeSSLOnDDC2 (remote-exec):   Port: 5985 
							null_resource.ChangeSSLOnDDC2 (remote-exec):   User: administrator 
							null_resource.ChangeSSLOnDDC2 (remote-exec):   Password: true 
							null_resource.ChangeSSLOnDDC2 (remote-exec):   HTTPS: false 
							null_resource.ChangeSSLOnDDC2 (remote-exec):   Insecure: false 
							null_resource.ChangeSSLOnDDC2 (remote-exec):   NTLM: false 
							null_resource.ChangeSSLOnDDC2 (remote-exec):   CACert: false 
							null_resource.ChangeSSLOnDDC2 (remote-exec): Connected! 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/CVAD-PFX.ps1 
							 
							null_resource.ChangeSSLOnDDC2 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/CVAD-IIS.ps1 
							 
							 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):    PSParentPath: Microsoft.PowerShell.Security\Certificate::LocalMachine\Webhosting 
							 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec): Thumbprint                                Subject                                PSComputerName 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec): ----------                                -------                                -------------- 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec): AB4CA8AC77068368F4B41AD650858D07679DD709  CN=*.the-austrian-citrix-guy.at        localhost 
							 
							 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.CreateCVADLicPFXOnDDC1: Creation complete after 8s [id=9076358615069953341] 
							null_resource.ChangeSSLOnDDC1: Creating... 
							null_resource.ChangeSSLOnDDC1: Provisioning with 'remote-exec'... 
							null_resource.ChangeSSLOnDDC1 (remote-exec): Connecting to remote host via WinRM... 
							null_resource.ChangeSSLOnDDC1 (remote-exec):   Host: 10.10.111.31 
							null_resource.ChangeSSLOnDDC1 (remote-exec):   Port: 5985 
							null_resource.ChangeSSLOnDDC1 (remote-exec):   User: administrator 
							null_resource.ChangeSSLOnDDC1 (remote-exec):   Password: true 
							null_resource.ChangeSSLOnDDC1 (remote-exec):   HTTPS: false 
							null_resource.ChangeSSLOnDDC1 (remote-exec):   Insecure: false 
							null_resource.ChangeSSLOnDDC1 (remote-exec):   NTLM: false 
							null_resource.ChangeSSLOnDDC1 (remote-exec):   CACert: false 
							null_resource.ChangeSSLOnDDC1 (remote-exec): Connected! 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
							null_resource.ChangeSSLOnDDC1 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/CVAD-IIS.ps1 
							null_resource.ChangeSSLOnDDC2: Still creating... [10s elapsed] 
							null_resource.ChangeSSLOnDDC1: Still creating... [10s elapsed] 
							 
							null_resource.ChangeSSLOnDDC2 (remote-exec): Attempting stop... 
							 
							null_resource.ChangeSSLOnDDC2 (remote-exec): Internet services successfully stopped 
							 
							null_resource.ChangeSSLOnDDC2 (remote-exec): Attempting start... 
							 
							null_resource.ChangeSSLOnDDC2 (remote-exec): Internet services successfully restarted 
							 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.ChangeSSLOnDDC2: Creation complete after 13s [id=3342403750279377103] 
							null_resource.RebootDDC2-2: Creating... 
							null_resource.RebootDDC2-2: Provisioning with 'remote-exec'... 
							null_resource.RebootDDC2-2 (remote-exec): Connecting to remote host via WinRM... 
							null_resource.RebootDDC2-2 (remote-exec):   Host: 10.10.111.33 
							null_resource.RebootDDC2-2 (remote-exec):   Port: 5985 
							null_resource.RebootDDC2-2 (remote-exec):   User: administrator 
							null_resource.RebootDDC2-2 (remote-exec):   Password: true 
							null_resource.RebootDDC2-2 (remote-exec):   HTTPS: false 
							null_resource.RebootDDC2-2 (remote-exec):   Insecure: false 
							null_resource.RebootDDC2-2 (remote-exec):   NTLM: false 
							null_resource.RebootDDC2-2 (remote-exec):   CACert: false 
							null_resource.RebootDDC2-2 (remote-exec): Connected! 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
							null_resource.RebootDDC2-2 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/Reboot.ps1 
							 
							null_resource.ChangeSSLOnDDC1 (remote-exec): Attempting stop... 
							 
							null_resource.ChangeSSLOnDDC1 (remote-exec): Internet services successfully stopped 
							 
							null_resource.ChangeSSLOnDDC1 (remote-exec): Attempting start... 
							 
							null_resource.ChangeSSLOnDDC1 (remote-exec): Internet services successfully restarted 
							 
							null_resource.RebootDDC2-2: Creation complete after 1s [id=654125571936143453] 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.ChangeSSLOnDDC1: Creation complete after 13s [id=5588651384891073291] 
							null_resource.RebootDDC1-2: Creating... 
							null_resource.RebootDDC1-2: Provisioning with 'remote-exec'... 
							null_resource.RebootDDC1-2 (remote-exec): Connecting to remote host via WinRM... 
							null_resource.RebootDDC1-2 (remote-exec):   Host: 10.10.111.31 
							null_resource.RebootDDC1-2 (remote-exec):   Port: 5985 
							null_resource.RebootDDC1-2 (remote-exec):   User: administrator 
							null_resource.RebootDDC1-2 (remote-exec):   Password: true 
							null_resource.RebootDDC1-2 (remote-exec):   HTTPS: false 
							null_resource.RebootDDC1-2 (remote-exec):   Insecure: false 
							null_resource.RebootDDC1-2 (remote-exec):   NTLM: false 
							null_resource.RebootDDC1-2 (remote-exec):   CACert: false 
							null_resource.RebootDDC1-2 (remote-exec): Connected! 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
							null_resource.RebootDDC1-2 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/Reboot.ps1 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.RebootDDC1-2: Creation complete after 2s [id=5798775506946784337] 
							 
							Apply complete! Resources: 9 added, 0 changed, 0 destroyed. 
							PS C:\_TACG\_CVADOnXenServer-Configuration-AfterStoreFront&gt;
						 
					
				
			
		
	

	
		
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}		
	
	 

	
		Terraform has successfully uploaded the public-signed SSL certificate and bound it to IIS:
	 

	
		
	 

	
		
	 

	
		 
		Terraform has successfully configured a central Citrix License Server for the site: 
		
	 

	
		The Terraform flow was completed successfully, and the next module can begin.
	 

	
		 
	 

	
		Module 5: Creating all needed CVAD-related Entities
	

	
		 
		In this module, Terraform finally creates all the entities needed:
	 

	
		
			Creating a dedicated Hypervisor Connection to XenServer 8
		
		
			Creating a dedicated Hypervisor Resource Pool
		
		
			Creating a Machine Catalog based on the referenced Master Image
		
		
			Creating a Delivery Group
		
		
			Creating AutoScale settings and applying these to the newly created Delivery Group 
		
	

	
		Important:  
		 
		This module does not cover the creation of other entities like Delegated Administration and Policies. 
		You can find more information about creating these in our Deployment Guide: Using Terraform to deploy Citrix Policies and Delegated Administration on a Citrix Virtual Apps and Desktops 2402 LTSR CU1 Site 
		 
	 

	
		
			The Terraform configuration contains some idle time slots to ensure that background operations can be completed before the following configuration steps occur. 
			We have seen different elapsed configuration times related to varying loads on the XenServer cluster. 
		
	

	
		Before running Terraform, no Terraform-related entities are available: 
		
	 

	
		 
		
	 

	
		 
		
	 

	
		 
	 

	
		 
	 

	
		Important:  
		 
		This module can only be run on the previously created Administrative VM, as the following modules rely heavily on WinRM.  
		Please install Terraform on the Administrative VM before trying to run the scripts. 
		 
	 

	
		
			 
		
	

	
		Important: 
		 
		Before starting the Terraform workflow, please ensure you have configured the variables according to your needs using the corresponding .auto.tfvars.json and/or terraform.tfvars file. 
		 
	 

	
		The configuration can be started by following the standard Terraform workflow:
	 

	
		terraform init, 
		terraform plan 
		 
	 

	
		and if no errors occur
	 

	
		terraform apply
	 

	
		
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}		
	

	
		 
	 

	
		
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}		
		
			
				
					
						
							PS C:\_TACG\_CVADOnXenServer-CVAD-Entities&gt; terraform init 
							Initializing the backend... 
							Initializing provider plugins... 
							- Finding citrix/citrix versions matching "1.0.1"... 
							- Installing citrix/citrix v1.0.1... 
							- Installed citrix/citrix v1.0.1 (self-signed, key ID BD4BD0E690CB7D88) 
							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\_CVADOnXenServer-CVAD-Entities&gt; terraform plan 
							 
							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_xenserver_hypervisor.CreateHypervisorConnection will be created 
							  + resource "citrix_xenserver_hypervisor" "CreateHypervisorConnection" { 
							      + addresses                                = [ 
							          + "http://10.10.111.11", 
							        ] 
							      + id                                       = (known after apply) 
							      + max_absolute_active_actions              = 40 
							      + max_absolute_new_actions_per_minute      = 10 
							      + max_power_actions_percentage_of_machines = 20 
							      + name                                     = "TACG-TF-CVAD-XS8-HypConn TT" 
							      + password                                 = (sensitive value) 
							      + password_format                          = "PlainText" 
							      + scopes                                   = [] 
							      + username                                 = "root" 
							      + zone                                     = (known after apply) 
							    } 
							 
							  # citrix_zone.TACG-TF-HYP-Zone will be created 
							  + resource "citrix_zone" "TACG-TF-HYP-Zone" { 
							      + description          = (known after apply) 
							      + id                   = (known after apply) 
							      + name                 = "TACG-TF-CVAD-XS8-ZONE TT" 
							      + resource_location_id = (known after apply) 
							    }
						 

						
							# 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                            = "Suspend" 
							          + off_peak_disconnect_timeout_minutes                   = 0 
							          + off_peak_extended_disconnect_action                   = "Nothing" 
							          + off_peak_extended_disconnect_timeout_minutes          = 0 
							          + off_peak_log_off_action                               = "Suspend" 
							          + off_peak_log_off_timeout_minutes                      = 0 
							          + peak_autoscale_assigned_power_on_idle_action          = "Nothing" 
							          + peak_autoscale_assigned_power_on_idle_timeout_minutes = 0 
							          + peak_buffer_size_percent                              = 0 
							          + peak_disconnect_action                                = "Suspend" 
							          + peak_disconnect_timeout_minutes                       = 0 
							          + peak_extended_disconnect_action                       = "Nothing" 
							          + peak_extended_disconnect_timeout_minutes              = 0 
							          + peak_log_off_action                                   = "Suspend" 
							          + peak_log_off_timeout_minutes                          = 0 
							          + power_off_delay_minutes                               = 30 
							          + power_time_schemes                                    = [ 
							              + { 
							                  + days_of_week          = [ 
							                      + "Friday", 
							                      + "Monday", 
							                      + "Thursday", 
							                      + "Tuesday", 
							                      + "Wednesday", 
							                    ] 
							                  + display_name          = "TACG-TF-CVAD-XS8-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 
							                }, 
							            ] 
							        } 
							      + description                 = "" 
							      + desktops                    = [ 
							          + { 
							              + description             = "Terraform-based Delivery Group running on XS8" 
							              + enable_session_roaming  = true 
							              + enabled                 = true 
							              + published_name          = "DG-TACG-TF-CVAD-XS8" 
							              + restricted_access_users = { 
							                  + allow_list = [ 
							                      + "TACG\\vdaallowed", 
							                    ] 
							                } 
							            }, 
							        ] 
							      + id                          = (known after apply) 
							      + minimum_functional_level    = "L7_20" 
							      + name                        = "DG-TACG-TF-CVAD-XS8" 
							      + reboot_schedules            = [ 
							          + { 
							              + days_in_week            = [ 
							                  + "Sunday", 
							                ] 
							              + frequency               = "Weekly" 
							              + frequency_factor        = 1 
							              + ignore_maintenance_mode = true 
							              + name                    = "TACG-XS8-Reboot Schedule" 
							              + natural_reboot_schedule = false 
							              + reboot_duration_minutes = 0 
							              + reboot_schedule_enabled = true 
							              + start_date              = "2024-01-01" 
							              + start_time              = "02:00" 
							                # (1 unchanged attribute hidden) 
							            }, 
							        ] 
							      + restricted_access_users     = { 
							          + allow_list = [ 
							              + "TACG\\vdaallowed", 
							            ] 
							        } 
							      + scopes                      = [] 
							      + total_machines              = (known after apply) 
							    } 
							 
							  # citrix_machine_catalog.CreateXS8MCSCatalog will be created 
							  + resource "citrix_machine_catalog" "CreateXS8MCSCatalog" { 
							      + allocation_type          = "Random" 
							      + description              = "Terraform-based Machine Catalog" 
							      + id                       = (known after apply) 
							      + minimum_functional_level = "L7_20" 
							      + name                     = "MC-TACG-TF-CVAD-XS8" 
							      + provisioning_scheme      = { 
							          + hypervisor                     = "226ca37a-7e3b-4e56-bb84-6b6e577ec869" 
							          + hypervisor_resource_pool       = (known after apply) 
							          + identity_type                  = "ActiveDirectory" 
							          + machine_account_creation_rules = { 
							              + naming_scheme      = "TACG-TF-XS8-C#" 
							              + naming_scheme_type = "Numeric" 
							            } 
							          + machine_domain_identity        = { 
							              + domain                   = "the-austrian-citrix-guy.at" 
							              + domain_ou                = "CN=Computers,DC=the-austrian-citrix-guy,DC=at" 
							              + service_account          = "Administrator" 
							              + service_account_password = (sensitive value) 
							            } 
							          + number_of_total_machines       = 1 
							          + xenserver_machine_config       = { 
							              + cpu_count         = 2 
							              + image_snapshot    = (known after apply) 
							              + master_image_note = "" 
							              + master_image_vm   = "TACG-W11-VDA-M" 
							              + memory_mb         = 4096 
							              + writeback_cache   = { 
							                  + writeback_cache_disk_size_gb   = 4 
							                  + writeback_cache_memory_size_mb = 1024 
							                } 
							            } 
							        } 
							      + provisioning_type        = "MCS" 
							      + scopes                   = [] 
							      + session_support          = "SingleSession" 
							      + zone                     = "dde49d85-9385-4f1e-986c-6a0b49da3c0d" 
							    } 
							 
							  # citrix_xenserver_hypervisor_resource_pool.CreateHypervisorPool will be created 
							  + resource "citrix_xenserver_hypervisor_resource_pool" "CreateHypervisorPool" { 
							      + hypervisor                = "226ca37a-7e3b-4e56-bb84-6b6e577ec869" 
							      + id                        = (known after apply) 
							      + name                      = "TACG-TF-CVAD-XS8-HypConnPool" 
							      + networks                  = [ 
							          + "Network 0", 
							        ] 
							      + storage                   = [ 
							          + { 
							              + storage_name = "Local storage on tacg-xs" 
							              + superseded   = false 
							            }, 
							        ] 
							      + temporary_storage         = [ 
							          + { 
							              + storage_name = "Local storage on tacg-xs" 
							              + superseded   = false 
							            }, 
							        ] 
							      + use_local_storage_caching = false 
							    } 
							 
						 

						
							  # time_sleep.wait_30_seconds will be created 
							  + resource "time_sleep" "wait_30_seconds1" { 
							      + create_duration = "30s" 
							      + id              = (known after apply) 
							    } 
							  
							  # time_sleep.wait_30_seconds1 will be created 
							  + resource "time_sleep" "wait_30_seconds1" { 
							      + create_duration = "30s" 
							      + id              = (known after apply) 
							    } 
							 
							  # time_sleep.wait_30_seconds_2 will be created 
							  + resource "time_sleep" "wait_30_seconds_2" { 
							      + create_duration = "30s" 
							      + 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\_CVADOnXenServer-CVAD-Entities&gt; terraform validate 
							Success! The configuration is valid. 
							 
							PS C:\_TACG\_CVADOnXenServer-CVAD-Entities&gt; terraform apply 
							 
							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_xenserver_hypervisor.CreateHypervisorConnection will be created 
							  + resource "citrix_xenserver_hypervisor" "CreateHypervisorConnection" { 
							      + addresses                                = [ 
							          + "http://10.10.111.11", 
							        ] 
							      + id                                       = (known after apply) 
							      + max_absolute_active_actions              = 40 
							      + max_absolute_new_actions_per_minute      = 10 
							      + max_power_actions_percentage_of_machines = 20 
							      + name                                     = "TACG-TF-CVAD-XS8-HypConn TT" 
							      + password                                 = (sensitive value) 
							      + password_format                          = "PlainText" 
							      + scopes                                   = [] 
							      + username                                 = "root" 
							      + zone                                     = (known after apply) 
							    } 
							 
							  # citrix_zone.TACG-TF-HYP-Zone will be created 
							  + resource "citrix_zone" "TACG-TF-HYP-Zone" { 
							      + description          = (known after apply) 
							      + id                   = (known after apply) 
							      + name                 = "TACG-TF-CVAD-XS8-ZONE TT" 
							      + resource_location_id = (known after apply) 
							    } 
							 
							# 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                            = "Suspend" 
							          + off_peak_disconnect_timeout_minutes                   = 0 
							          + off_peak_extended_disconnect_action                   = "Nothing" 
							          + off_peak_extended_disconnect_timeout_minutes          = 0 
							          + off_peak_log_off_action                               = "Suspend" 
							          + off_peak_log_off_timeout_minutes                      = 0 
							          + peak_autoscale_assigned_power_on_idle_action          = "Nothing" 
							          + peak_autoscale_assigned_power_on_idle_timeout_minutes = 0 
							          + peak_buffer_size_percent                              = 0 
							          + peak_disconnect_action                                = "Suspend" 
							          + peak_disconnect_timeout_minutes                       = 0 
							          + peak_extended_disconnect_action                       = "Nothing" 
							          + peak_extended_disconnect_timeout_minutes              = 0 
							          + peak_log_off_action                                   = "Suspend" 
							          + peak_log_off_timeout_minutes                          = 0 
							          + power_off_delay_minutes                               = 30 
							          + power_time_schemes                                    = [ 
							              + { 
							                  + days_of_week          = [ 
							                      + "Friday", 
							                      + "Monday", 
							                      + "Thursday", 
							                      + "Tuesday", 
							                      + "Wednesday", 
							                    ] 
							                  + display_name          = "TACG-TF-CVAD-XS8-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 
							                }, 
							            ] 
							        } 
							      + description                 = "" 
							      + desktops                    = [ 
							          + { 
							              + description             = "Terraform-based Delivery Group running on XS8" 
							              + enable_session_roaming  = true 
							              + enabled                 = true 
							              + published_name          = "DG-TACG-TF-CVAD-XS8" 
							              + restricted_access_users = { 
							                  + allow_list = [ 
							                      + "TACG\\vdaallowed", 
							                    ] 
							                } 
							            }, 
							        ] 
							      + id                          = (known after apply) 
							      + minimum_functional_level    = "L7_20" 
							      + name                        = "DG-TACG-TF-CVAD-XS8" 
							      + reboot_schedules            = [ 
							          + { 
							              + days_in_week            = [ 
							                  + "Sunday", 
							                ] 
							              + frequency               = "Weekly" 
							              + frequency_factor        = 1 
							              + ignore_maintenance_mode = true 
							              + name                    = "TACG-XS8-Reboot Schedule" 
							              + natural_reboot_schedule = false 
							              + reboot_duration_minutes = 0 
							              + reboot_schedule_enabled = true 
							              + start_date              = "2024-01-01" 
							              + start_time              = "02:00" 
							                # (1 unchanged attribute hidden) 
							            }, 
							        ] 
							      + restricted_access_users     = { 
							          + allow_list = [ 
							              + "TACG\\vdaallowed", 
							            ] 
							        } 
							      + scopes                      = [] 
							      + total_machines              = (known after apply) 
							    } 
							 
							  # citrix_machine_catalog.CreateXS8MCSCatalog will be created 
							  + resource "citrix_machine_catalog" "CreateXS8MCSCatalog" { 
							      + allocation_type          = "Random" 
							      + description              = "Terraform-based Machine Catalog" 
							      + id                       = (known after apply) 
							      + minimum_functional_level = "L7_20" 
							      + name                     = "MC-TACG-TF-CVAD-XS8" 
							      + provisioning_scheme      = { 
							          + hypervisor                     = "226ca37a-7e3b-4e56-bb84-6b6e577ec869" 
							          + hypervisor_resource_pool       = (known after apply) 
							          + identity_type                  = "ActiveDirectory" 
							          + machine_account_creation_rules = { 
							              + naming_scheme      = "TACG-TF-XS8-C#" 
							              + naming_scheme_type = "Numeric" 
							            } 
							          + machine_domain_identity        = { 
							              + domain                   = "the-austrian-citrix-guy.at" 
							              + domain_ou                = "CN=Computers,DC=the-austrian-citrix-guy,DC=at" 
							              + service_account          = "Administrator" 
							              + service_account_password = (sensitive value) 
							            } 
							          + number_of_total_machines       = 1 
							          + xenserver_machine_config       = { 
							              + cpu_count         = 2 
							              + image_snapshot    = (known after apply) 
							              + master_image_note = "" 
							              + master_image_vm   = "TACG-W11-VDA-M" 
							              + memory_mb         = 4096 
							              + writeback_cache   = { 
							                  + writeback_cache_disk_size_gb   = 4 
							                  + writeback_cache_memory_size_mb = 1024 
							                } 
							            } 
							        } 
							      + provisioning_type        = "MCS" 
							      + scopes                   = [] 
							      + session_support          = "SingleSession" 
							      + zone                     = "dde49d85-9385-4f1e-986c-6a0b49da3c0d" 
							    } 
							 
							  # citrix_xenserver_hypervisor_resource_pool.CreateHypervisorPool will be created 
							  + resource "citrix_xenserver_hypervisor_resource_pool" "CreateHypervisorPool" { 
							      + hypervisor                = "226ca37a-7e3b-4e56-bb84-6b6e577ec869" 
							      + id                        = (known after apply) 
							      + name                      = "TACG-TF-CVAD-XS8-HypConnPool" 
							      + networks                  = [ 
							          + "Network 0", 
							        ] 
							      + storage                   = [ 
							          + { 
							              + storage_name = "Local storage on tacg-xs" 
							              + superseded   = false 
							            }, 
							        ] 
							      + temporary_storage         = [ 
							          + { 
							              + storage_name = "Local storage on tacg-xs" 
							              + superseded   = false 
							            }, 
							        ] 
							      + use_local_storage_caching = false 
							    } 
							 
							# time_sleep.wait_30_seconds will be created 
							  + resource "time_sleep" "wait_30_seconds1" { 
							      + create_duration = "30s" 
							      + id              = (known after apply) 
							    }  
						 

						
							# time_sleep.wait_30_seconds1 will be created 
							  + resource "time_sleep" "wait_30_seconds1" { 
							      + create_duration = "30s" 
							      + id              = (known after apply) 
							    } 
							 
							  # time_sleep.wait_30_seconds_2 will be created 
							  + resource "time_sleep" "wait_30_seconds_2" { 
							      + create_duration = "30s" 
							      + 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 
							 
							citrix_zone.TACG-TF-HYP-Zone: Creating... 
							citrix_zone.TACG-TF-HYP-Zone: Creation complete after 8s [id=334adf89-4542-bcd1-33ef-3890df7a7bc1]
						 

						
							citrix_xenserver_hypervisor.CreateHypervisorConnection: Creating... 
							citrix_xenserver_hypervisor.CreateHypervisorConnection: Still creating... [10s elapsed] 
							citrix_xenserver_hypervisor.CreateHypervisorConnection: Still creating... [20s elapsed] 
							citrix_xenserver_hypervisor.CreateHypervisorConnection: Still creating... [30s elapsed] 
							citrix_xenserver_hypervisor.CreateHypervisorConnection: Creation complete after 31s [id=3901abe2-eefd-3d09-2889-340ab8327df8] 
							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-09-11T12:53:34Z] 
							citrix_xenserver_hypervisor_resource_pool.CreateHypervisorPool: Creating... 
							citrix_xenserver_hypervisor_resource_pool.CreateHypervisorPool: Still creating... [10s elapsed] 
							citrix_xenserver_hypervisor_resource_pool.CreateHypervisorPool: Creation complete after 11s [id=ec397dba-e416-4a67-8462-a589486e41fa] 
							time_sleep.wait_30_seconds1: Creating... 
							time_sleep.wait_30_seconds1: Still creating... [10s elapsed] 
							time_sleep.wait_30_seconds1: Still creating... [20s elapsed] 
							time_sleep.wait_30_seconds1: Creation complete after 30s [id=2024-09-11T12:54:16Z] 
							citrix_machine_catalog.CreateXS8MCSCatalog: Creating... 
							citrix_machine_catalog.CreateXS8MCSCatalog: Still creating... [10s elapsed] 
							citrix_machine_catalog.CreateXS8MCSCatalog: Still creating... [20s elapsed] 
							citrix_machine_catalog.CreateXS8MCSCatalog: Still creating... [30s elapsed] 
							citrix_machine_catalog.CreateXS8MCSCatalog: Still creating... [40s elapsed] 
							citrix_machine_catalog.CreateXS8MCSCatalog: Still creating... [50s elapsed] 
							citrix_machine_catalog.CreateXS8MCSCatalog: Still creating... [1m0s elapsed] 
							citrix_machine_catalog.CreateXS8MCSCatalog: Still creating... [1m10s elapsed] 
							citrix_machine_catalog.CreateXS8MCSCatalog: Still creating... [1m20s elapsed] 
							citrix_machine_catalog.CreateXS8MCSCatalog: Still creating... [1m30s elapsed] 
							citrix_machine_catalog.CreateXS8MCSCatalog: Still creating... [1m40s elapsed] 
							citrix_machine_catalog.CreateXS8MCSCatalog: Still creating... [1m50s elapsed] 
							citrix_machine_catalog.CreateXS8MCSCatalog: Still creating... [2m0s elapsed] 
							citrix_machine_catalog.CreateXS8MCSCatalog: Still creating... [2m10s elapsed] 
							citrix_machine_catalog.CreateXS8MCSCatalog: Still creating... [2m20s elapsed] 
							citrix_machine_catalog.CreateXS8MCSCatalog: Still creating... [2m30s elapsed] 
							citrix_machine_catalog.CreateXS8MCSCatalog: Still creating... [2m40s elapsed] 
							citrix_machine_catalog.CreateXS8MCSCatalog: Still creating... [2m50s elapsed] 
							citrix_machine_catalog.CreateXS8MCSCatalog: Still creating... [3m0s elapsed] 
							citrix_machine_catalog.CreateXS8MCSCatalog: Still creating... [3m10s elapsed] 
							citrix_machine_catalog.CreateXS8MCSCatalog: Still creating... [3m20s elapsed] 
							citrix_machine_catalog.CreateXS8MCSCatalog: Still creating... [3m30s elapsed] 
							citrix_machine_catalog.CreateXS8MCSCatalog: Creation complete after 3m31s [id=502012d7-37dc-4816-94d3-d7cd66981c14] 
							time_sleep.wait_30_seconds_2: Creating... 
							time_sleep.wait_30_seconds_2: Still creating... [10s elapsed] 
							time_sleep.wait_30_seconds_2: Still creating... [20s elapsed] 
							time_sleep.wait_30_seconds_2: Still creating... [30s elapsed] 
							time_sleep.wait_30_seconds_2: Creation complete after 30s [id=2024-09-11T12:58:17Z] 
							citrix_delivery_group.CreateDG: Creating... 
							citrix_delivery_group.CreateDG: Creation complete after 3s [id=3a1a12ff-f52a-4c38-a0cc-4dbb9dc28a72] 
							 
							Apply complete! Resources: 8 added, 0 changed, 0 destroyed. 
							 
							PS C:\_TACG\_CVADOnXenServer-CVAD-Entities&gt;
						 
					
				
			
		
	

	
		Terraform successfully created the Hosting Connection and a Hosting Connection Pool associated with the Hosting Connection: 
		
	 

	
		
	 

	
		 
		Terraform successfully created the Machine Catalog: 
		
	 

	
		
	 

	
		
	 

	
		 
		Terraform successfully created the Delivery Group: 
		
	 

	
		Terraform successfully created the AutoScale settings: 
		
	 

	
		The Worker VM is shown on StoreFront and ready to start: 
		
	 

	
		 
		The HDX connection is successfully established: 
		
	 

	
		That concludes the guide "Using Terraform to deploy a Citrix Virtual Apps and Desktops 2402 LTSR CU1 site on XenServer 8”.
	 
	 

	
		Appendix 
		 
	

	
		
			Disclaimer
		 

		
			EXCEPT WHERE EXPRESSLY PROVIDED OTHERWISE BY CLOUD SOFTWARE GROUP, THE CONTENT IN THIS DOCUMENT IS PROVIDED “AS IS” AND CLOUD SOFTWARE GROUP HEREBY DISCLAIMS ALL EXPRESS OR IMPLIED REPRESENTATIONS, WARRANTIES, GUARANTIES, AND CONDITIONS, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES OR CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD TO BE LEGALLY INVALID. CLOUD SOFTWARE GROUP MAKES NO REPRESENTATIONS, WARRANTIES, GUARANTIES, OR CONDITIONS AS TO THE QUALITY, SUITABILITY, TRUTH, ACCURACY, OR COMPLETENESS OF ANY OF THE CONTENT CONTAINED ON THE WEBSITE.
		 

		
			The above disclaimer applies to any damages, liability, or injuries caused by any failure of performance, error, omission, interruption, deletion, defect, delay in operation or transmission, computer virus, communication line failure, theft or destruction of or unauthorized access to, alteration of, or use, whether for breach of contract, tort, negligence or any other cause of action. 
			 
		 
	

	
		Examples of the Terraform scripts
	 

	
		Module 1: _CVADOnXenServer-Creation
	

	
		These are the Terraform configuration files for Module 1 (excerpts):
	 

	
		CVADOnXenServer-Create-Provider.tf
	

	
		
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}		
		
			
				
					
						
							
								
									# Terraform deployment of Citrix Virtual Apps and Desktops 2402 LTSR CU1 on XenServer 8
								

								
									## Definition of all required Terraform providers
								
								 

								
									terraform {
								

								
									    required_version = "&gt;= 1.9.0"
								
								 

								
									  required_providers {
								

								
									    xenserver = {
								

								
									      source = "xenserver/xenserver"
								

								
									    }
								

								
									  }
								

								
									}
								
								 

								
									provider "xenserver" {
								

								
									  username     = var.XenServer_Provider-Username
								

								
									  password     = var.XenServer_Provider-Password
								

								
									  host         = var.XenServer_Provider-Host
								

								
									}
								
							
						
					
				
			
		
	

	
		CVADOnXenServer-Create.tf
	

	
		
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}		
		
			
				
					
						
							
								
									# Terraform deployment of Citrix Virtual Apps and Desktops 2402 LTSR CU1 on XenServer 8
								

								
									## Definition of all required local variables
								

								
									### Get XenServer-related Data
								

								
									#### Get the SR object
								

								
									data "xenserver_sr" "sr" {
								

								
									  name_label = "Local storage"
								

								
									}
								
								 

								
									#### Get the Network object
								

								
									data "xenserver_network" "network" {
								

								
									}
								
								 

								
									#### Create a local ISO repository
								

								
									resource "xenserver_sr" "local" {
								

								
									  name_label       = "ISO on TACG-DCN"
								

								
									  name_description = "ISO repository on TACG-DC"
								

								
									  type             = "iso"
								

								
									  content_type     = "ISOs"
								

								
									  shared           = true
								

								
									  device_config = {
								

								
									    username = "smb@the-austrian-citrix-guy.at"
								

								
									    cifspassword = "********"
								

								
									    location = "\\\\tacg-dc.the-austrian-citrix-guy.at\\_sw"
								

								
									    type = "cifs"
								

								
									    vers = "1.0" 
								

								
									  }
								

								
									  sm_config = {
								

								
									    "iso_type" = "cifs"
								

								
									  } 
								

								
									} 
								
								 

								
									#### Set local variables for creating multiple VMs
								

								
									locals {
								

								
									  virtual_machines = {
								

								
									    "windows-vm" = {
								

								
									      name_label       = var.DDC1-VM-Name
								

								
									      description      = var.DDC1-VM-Description
								

								
									      template_name    = var.XS-TemplateName
								

								
									      static_mem_max   = var.DDC1-VM-RAM * 1024 * 1024
								

								
									      vcpus            = var.DDC1-VM-CPU
								

								
									      cores_per_socket = 2
								

								
									      cdrom            = var.XS-Image-ISO
								

								
									      network_interface = [
								

								
									        {
								

								
									          network_uuid = data.xenserver_network.network.data_items[0].uuid,
								

								
									          device       = "0"
								

								
									        },
								

								
									      ]
								

								
									    }
								

								
									    "windows-vm1" = {
								

								
									      name_label       = var.DDC2-VM-Name
								

								
									      description      = var.DDC2-VM-Description
								

								
									      template_name    = var.XS-TemplateName
								

								
									      static_mem_max   = var.DDC2-VM-RAM * 1024 * 1024
								

								
									      vcpus            = var.DDC2-VM-CPU
								

								
									      cores_per_socket = 2
								

								
									      cdrom            = var.XS-Image-ISO
								

								
									      network_interface = [
								

								
									        {
								

								
									          network_uuid = data.xenserver_network.network.data_items[0].uuid,
								

								
									          device       = "0"
								
								 

								
									        },
								

								
									      ]
								

								
									    }
								

								
									    "windows-vm2" = {
								

								
									      name_label       = var.AVM-VM-Name
								

								
									      description      = var.AVM-VM-Description
								

								
									      template_name    = var.XS-TemplateName
								

								
									      static_mem_max   = var.AVM-VM-RAM * 1024 * 1024
								

								
									      vcpus            = var.AVM-VM-CPU
								

								
									      cores_per_socket = 2
								

								
									      cdrom            = var.XS-Image-ISO
								

								
									      network_interface = [
								

								
									        {
								

								
									          network_uuid = data.xenserver_network.network.data_items[0].uuid,
								

								
									          device       = "0"
								

								
									        },
								

								
									      ]
								

								
									    }
								

								
									  }
								

								
									}
								
								 

								
									resource "xenserver_vm" "vm" {
								

								
									   # depends_on = [ xenserver_sr.local ]
								

								
									  for_each          = local.virtual_machines
								

								
									  name_label        = each.value.name_label
								

								
									  template_name     = each.value.template_name 
								

								
									  static_mem_max    = each.value.static_mem_max
								

								
									  vcpus             = each.value.vcpus
								

								
									  cores_per_socket  = 2
								

								
									  cdrom              = var.XS-Image-ISO
								

								
									  network_interface = each.value.network_interface
								

								
									}
								
								 

								
									output "vm_out" {
								

								
									  value = {
								

								
									    for vm in xenserver_vm.vm : vm.name_label =&gt; vm
								

								
									  }
								

								
									}
								
								 

								
									################################################################################################
								

								
									###  All VMs for creating a CVAD 2402 LTSR CU1 site on XenServer 8 should have been created  ###
								

								
									################################################################################################  
								
							
						
					
				
			
		
	

	
		 
		Module 2: _CVADOnXenServer-InitialConfiguration
	

	
		These are the Terraform configuration files for Module 2 (excerpts):
	 

	
		CVADOnXenServer-InitialConfiguration.tf
	

	
		
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}		
		
			
				
					
						
							
								
									# Terraform deployment of Citrix Virtual Apps and Desktops 2402 LTSR CU1 on XenServer 8
								

								
									## Definition of all required local variables
								

								
									### Get XenServer-related Data
								

								
									#### Get the SR object
								

								
									data "xenserver_sr" "sr" {
								

								
									  name_label = "Local storage"
								

								
									}
								
								 

								
									#### Get the Network object
								

								
									data "xenserver_network" "network" {
								

								
									}
								
								 

								
									#### Get the VM objects
								

								
									data "xenserver_vm" "vm_data" {
								

								
									}
								
								 

								
									locals {
								

								
									#### Create further Configuration scripts
								

								
									##### Create Installation directory
								

								
									InstallLogPath     = &lt;&lt;-EOT
								

								
									New-Item -Path ${var.Install_LogPath} -ItemType Directory
								

								
									EOT
								
								 

								
									##### Create IP-Setting script for DDC1
								

								
									SetIPForDDC1     = &lt;&lt;-EOT
								

								
									$Adapter = Get-NetAdapter | ? {$_.Status -eq "up"}
								

								
									$IFIndx = $Adapter.InterfaceIndex
								

								
									$IpAddress = '${var.DDC1-VM-IP-Final}'
								

								
									$Prefix = '${var.VM-IP-Prefix}'
								

								
									$GW = '${var.VM-IP-GW}'
								

								
									$DNS = '${var.VM-IP-DNS}'
								

								
									$IPType = '${var.VM-IP-Type}'
								
								 

								
									$adapter | New-NetIPAddress -AddressFamily $IPType -IPAddress $IpAddress -PrefixLength $Prefix -DefaultGateway $GW
								

								
									$adapter | Set-DnsClientServerAddress -ServerAddresses $DNS
								
								 

								
									Start-Sleep -Seconds 10
								
								 

								
									$ADN = '${var.DomainName}'
								

								
									$APSUsername = '${var.DomainAdmin-UN}'
								

								
									$APSPassword = '${var.DomainAdmin-PW}'
								

								
									$APSSecurePassword = ConvertTo-SecureString $APSPassword -AsPlainText -Force
								

								
									$APSCredential = New-Object System.Management.Automation.PSCredential ($APSUsername, $APSSecurePassword)
								

								
									Add-Computer -DomainName $ADN -Credential $APSCredential -Restart -Force
								

								
									EOT 
								
								 

								
									##### Create IP-Setting script for DDC2
								

								
									SetIPForDDC2     = &lt;&lt;-EOT
								

								
									$Adapter = Get-NetAdapter | ? {$_.Status -eq "up"}
								

								
									$IFIndx = $Adapter.InterfaceIndex
								

								
									$IpAddress = '${var.DDC2-VM-IP-Final}'
								

								
									$Prefix = '${var.VM-IP-Prefix}'
								

								
									$GW = '${var.VM-IP-GW}'
								

								
									$DNS = '${var.VM-IP-DNS}'
								

								
									$IPType = '${var.VM-IP-Type}'
								
								 

								
									$adapter | New-NetIPAddress -AddressFamily $IPType -IPAddress $IpAddress -PrefixLength $Prefix -DefaultGateway $GW
								

								
									$adapter | Set-DnsClientServerAddress -ServerAddresses $DNS
								
								 

								
									Start-Sleep -Seconds 10
								
								 

								
									$ADN = '${var.DomainName}'
								

								
									$APSUsername = '${var.DomainAdmin-UN}'
								

								
									$APSPassword = '${var.DomainAdmin-PW}'
								

								
									$APSSecurePassword = ConvertTo-SecureString $APSPassword -AsPlainText -Force
								

								
									$APSCredential = New-Object System.Management.Automation.PSCredential ($APSUsername, $APSSecurePassword)
								

								
									Add-Computer -DomainName $ADN -Credential $APSCredential -Restart -Force
								

								
									EOT 
								
								 

								
									##### Create IP-Setting script for CVADAVM
								

								
									SetIPForCVADAVM     = &lt;&lt;-EOT
								

								
									$Adapter = Get-NetAdapter | ? {$_.Status -eq "up"}
								

								
									$IFIndx = $Adapter.InterfaceIndex
								

								
									$IpAddress = '${var.CVADAVM-VM-IP-Final}'
								

								
									$Prefix = '${var.VM-IP-Prefix}'
								

								
									$GW = '${var.VM-IP-GW}'
								

								
									$DNS = '${var.VM-IP-DNS}'
								

								
									$IPType = '${var.VM-IP-Type}'
								
								 

								
									$adapter | New-NetIPAddress -AddressFamily $IPType -IPAddress $IpAddress -PrefixLength $Prefix -DefaultGateway $GW
								

								
									$adapter | Set-DnsClientServerAddress -ServerAddresses $DNS
								
								 

								
									Start-Sleep -Seconds 10
								
								 

								
									$ADN = '${var.DomainName}'
								

								
									$APSUsername = '${var.DomainAdmin-UN}'
								

								
									$APSPassword = '${var.DomainAdmin-PW}'
								

								
									$APSSecurePassword = ConvertTo-SecureString $APSPassword -AsPlainText -Force
								

								
									$APSCredential = New-Object System.Management.Automation.PSCredential ($APSUsername, $APSSecurePassword)
								

								
									Add-Computer -DomainName $ADN -Credential $APSCredential -Restart -Force
								

								
									EOT 
								
								 

								
									##### Create generic Reboot script
								

								
									RebootScript     = &lt;&lt;-EOT
								

								
									Restart-Computer -Force
								

								
									EOT
								
								 

								
									##### Create RenameComputer script for DDC1
								

								
									RenameDDC1     = &lt;&lt;-EOT
								

								
									$PSUsername = '${var.Provisioner_Admin-Username}'
								

								
									$PSPassword = '${var.Provisioner_Admin-Password}'
								

								
									$CN = '${var.DDC1-VM-ADName}'
								

								
									$PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
								

								
									$PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
								

								
									Rename-Computer -NewName $CN -LocalCredential $PSCredential -Restart -Force
								

								
									EOT
								
								 

								
									##### Create RenameComputer script for DDC2
								

								
									RenameDDC2     = &lt;&lt;-EOT
								

								
									$PSUsername = '${var.Provisioner_Admin-Username}'
								

								
									$PSPassword = '${var.Provisioner_Admin-Password}'
								

								
									$CN = '${var.DDC2-VM-ADName}'
								

								
									$PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
								

								
									$PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
								

								
									Rename-Computer -NewName $CN -LocalCredential $PSCredential -Restart -Force
								

								
									EOT
								
								 

								
									##### Create RenameComputer script for CVADAVM
								

								
									RenameCVADAVM     = &lt;&lt;-EOT
								

								
									$PSUsername = '${var.Provisioner_Admin-Username}'
								

								
									$PSPassword = '${var.Provisioner_Admin-Password}'
								

								
									$CN = '${var.CVADAVM-VM-ADName}'
								

								
									$PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
								

								
									$PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
								

								
									Rename-Computer -NewName $CN -LocalCredential $PSCredential -Restart -Force
								

								
									EOT
								
								 

								
									##### Create generic AddToDomain script
								

								
									AddToDomain     = &lt;&lt;-EOT
								

								
									$DN = '${var.DomainName}'
								

								
									$PSUsername = '${var.DomainAdmin-UN}'
								

								
									$PSPassword = '${var.DomainAdmin-PW}'
								

								
									$PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
								

								
									$PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
								

								
									Add-Computer -DomainName $DN -Credential $PSCredential -Restart -Force
								

								
									EOT
								

								
									}
								
								 

								
									#### Write DDC1-IP-Script into local data-directory
								

								
									resource "local_file" "WriteDDC1IPScriptIntoDataDirectory" {
								

								
									  filename = "${path.module}/data/DDC1-IP-Script.ps1"
								

								
									  content  = local.SetIPForDDC1
								

								
									}
								
								 

								
									#### Write DDC2-IP-Script into local data-directory
								

								
									resource "local_file" "WriteDDC2IPScriptIntoDataDirectory" {
								

								
									  filename = "${path.module}/data/DDC2-IP-Script.ps1"
								

								
									  content  = local.SetIPForDDC2
								

								
									}
								
								 

								
									#### Write CVADAVM-IP-Script into local data-directory
								

								
									resource "local_file" "WriteCVADAVMIPScriptIntoDataDirectory" {
								

								
									  filename = "${path.module}/data/CVADAVM-IP-Script.ps1"
								

								
									  content  = local.SetIPForCVADAVM
								

								
									}
								
								 

								
									#### Write Reboot-Script into local data-directory
								

								
									resource "local_file" "WriteRebootScriptIntoDataDirectory" {
								

								
									  filename = "${path.module}/data/Reboot-Script.ps1"
								

								
									  content  = local.RebootScript
								

								
									}
								
								 

								
									#### Write RenameComputer-DDC1-Script into local data-directory
								

								
									resource "local_file" "WriteRenameComputerDDC1ScriptIntoDataDirectory" {
								

								
									  filename = "${path.module}/data/RenameComputer-DDC1-Script.ps1"
								

								
									  content  = local.RenameDDC1
								

								
									}
								
								 

								
									#### Write RenameComputer-DDC2-Script into local data-directory
								

								
									resource "local_file" "WriteRenameComputerDDC2ScriptIntoDataDirectory" {
								

								
									  filename = "${path.module}/data/RenameComputer-DDC2-Script.ps1"
								

								
									  content  = local.RenameDDC2
								

								
									}
								
								 

								
									#### Write RenameComputer-CVADAVM-Script into local data-directory
								

								
									resource "local_file" "WriteRenameComputerCVADAVMScriptIntoDataDirectory" {
								

								
									  filename = "${path.module}/data/RenameComputer-CVADAVM-Script.ps1"
								

								
									  content  = local.RenameCVADAVM
								

								
									}
								
								 

								
									#### Write generic Domain-Script into local data-directory
								

								
									resource "local_file" "WriteAddToDomainScriptIntoDataDirectory" {
								

								
									  filename = "${path.module}/data/AddToDomain-Script.ps1"
								

								
									  content  = local.AddToDomain
								

								
									}
								
								 

								
									#### Write InstallationPath-Script into local data-directory
								

								
									resource "local_file" "WriteInstallationScriptIntoDataDirectory" {
								

								
									  filename = "${path.module}/data/InstallationLogPath-Script.ps1"
								

								
									  content  = local.InstallLogPath
								

								
									}
								
								 

								
									#### Upload Initial Configuration scripts to DDC1 and execute them
								

								
									###### Set the Provisioner-Connection
								

								
									resource "null_resource" "UploadAndExecuteScripts-DDC1" {
								

								
									  depends_on = [ local_file.WriteDDC1IPScriptIntoDataDirectory,  local_file.WriteRebootScriptIntoDataDirectory,  local_file.WriteAddToDomainScriptIntoDataDirectory, local_file.WriteInstallationScriptIntoDataDirectory, local_file.WriteRenameComputerDDC1ScriptIntoDataDirectory ]
								

								
									  connection {
								

								
									    type            = var.Provisioner_Type
								

								
									    user            = var.Provisioner_Admin-Username
								

								
									    password        = var.Provisioner_Admin-Password
								

								
									    host            = var.DDC1-VM-IP-PreConfig
								

								
									    timeout         = var.Provisioner_Timeout
								
								 

								
									  }
								
								 

								
									###### Upload Installation-Path script to DDC1
								

								
									  provisioner "file" {
								

								
									    source      = "${path.module}/data/InstallationLogPath-Script.ps1"
								

								
									    destination = "C:/temp/InstallationLogPath-Script.ps1"
								

								
									    
								

								
									  }
								
								 

								
									###### Execute the Installation-Path script
								

								
									 provisioner "remote-exec" {
								

								
									    inline = [
								

								
									      "powershell -File C:/temp/InstallationLogPath-Script.ps1"
								

								
									    ]
								

								
									  }
								

								
									}
								
								 

								
									#### Upload Initial Configuration scripts to DDC2 and execute them
								

								
									##### Set the Provisioner-Connection
								

								
									resource "null_resource" "UploadAndExecuteScripts-DDC2" {
								

								
									 depends_on = [ local_file.WriteDDC2IPScriptIntoDataDirectory,  local_file.WriteRebootScriptIntoDataDirectory,  local_file.WriteAddToDomainScriptIntoDataDirectory, local_file.WriteInstallationScriptIntoDataDirectory, local_file.WriteRenameComputerDDC2ScriptIntoDataDirectory ]
								

								
									  connection {
								

								
									    type            = var.Provisioner_Type
								

								
									    user            = var.Provisioner_Admin-Username
								

								
									    password        = var.Provisioner_Admin-Password
								

								
									    host            = var.DDC2-VM-IP-PreConfig
								

								
									    timeout         = var.Provisioner_Timeout
								
								 

								
									  }
								
								 

								
									##### Upload Installation-Path script to DDC2
								

								
									  provisioner "file" {
								

								
									    source      = "${path.module}/data/InstallationLogPath-Script.ps1"
								

								
									    destination = "C:/temp/InstallationLogPath-Script.ps1"
								

								
									    
								

								
									  }
								
								 

								
									##### Execute the Installation-Path script
								

								
									 provisioner "remote-exec" {
								

								
									    inline = [
								

								
									      "powershell -File C:/temp/InstallationLogPath-Script.ps1"
								

								
									    ]
								

								
									  }
								

								
									}
								
								 

								
									#### Upload Initial Configuration scripts to CVADAVM and execute them
								

								
									###### Set the Provisioner-Connection
								

								
									resource "null_resource" "UploadAndExecuteScripts-CVADAVM" {
								

								
									 depends_on = [ local_file.WriteCVADAVMIPScriptIntoDataDirectory,  local_file.WriteRebootScriptIntoDataDirectory,  local_file.WriteAddToDomainScriptIntoDataDirectory, local_file.WriteInstallationScriptIntoDataDirectory, local_file.WriteRenameComputerCVADAVMScriptIntoDataDirectory ]
								

								
									  connection {
								

								
									    type            = var.Provisioner_Type
								

								
									    user            = var.Provisioner_Admin-Username
								

								
									    password        = var.Provisioner_Admin-Password
								

								
									    host            = var.CVADAVM-VM-IP-PreConfig
								

								
									    timeout         = var.Provisioner_Timeout
								
								 

								
									  }
								
								 

								
									###### Upload Installation-Path script to CVADAVM
								

								
									  provisioner "file" {
								

								
									    source      = "${path.module}/data/InstallationLogPath-Script.ps1"
								

								
									    destination = "C:/temp/InstallationLogPath-Script.ps1"
								

								
									    
								

								
									  }
								
								 

								
									###### Execute the Installation-Path script
								

								
									 provisioner "remote-exec" {
								

								
									    inline = [
								

								
									      "powershell -File C:/temp/InstallationLogPath-Script.ps1"
								

								
									    ]
								

								
									  }
								

								
									}
								
								 

								
									#### Upload RenameComputer script to DDC1 and execute them
								

								
									###### Set the Provisioner-Connection
								

								
									resource "null_resource" "UploadAndExecuteRenameScripts-DDC1" {
								

								
									  depends_on = [ null_resource.UploadAndExecuteScripts-DDC1  ]
								

								
									  connection {
								

								
									    type            = var.Provisioner_Type
								

								
									    user            = var.Provisioner_Admin-Username
								

								
									    password        = var.Provisioner_Admin-Password
								

								
									    host            = var.DDC1-VM-IP-PreConfig
								

								
									    timeout         = var.Provisioner_Timeout
								
								 

								
									  }
								
								 

								
									###### Upload RenameComputer script to DDC1
								

								
									  provisioner "file" {
								

								
									    source      = "${path.module}/data/RenameComputer-DDC1-Script.ps1"
								

								
									    destination = "${var.Install_LogPath}/RenameComputer-DDC1-Script.ps1"
								

								
									    
								

								
									  }
								
								 

								
									###### Execute the RenameComputer script
								

								
									 provisioner "remote-exec" {
								

								
									    inline = [
								

								
									      "powershell -File ${var.Install_LogPath}/RenameComputer-DDC1-Script.ps1"
								

								
									    ]
								

								
									  }
								

								
									}
								
								 

								
									#### Upload RenameComputer script to DDC2 and execute them
								

								
									###### Set the Provisioner-Connection
								

								
									resource "null_resource" "UploadAndExecuteRenameScripts-DDC2" {
								

								
									  depends_on = [ null_resource.UploadAndExecuteScripts-DDC2  ]
								

								
									  connection {
								

								
									    type            = var.Provisioner_Type
								

								
									    user            = var.Provisioner_Admin-Username
								

								
									    password        = var.Provisioner_Admin-Password
								

								
									    host            = var.DDC2-VM-IP-PreConfig
								

								
									    timeout         = var.Provisioner_Timeout
								
								 

								
									  }
								
								 

								
									###### Upload RenameComputer script to DDC2
								

								
									  provisioner "file" {
								

								
									    source      = "${path.module}/data/RenameComputer-DDC2-Script.ps1"
								

								
									    destination = "${var.Install_LogPath}/RenameComputer-DDC2-Script.ps1"
								

								
									    
								

								
									  }
								
								 

								
									###### Execute the RenameComputer script
								

								
									 provisioner "remote-exec" {
								

								
									    inline = [
								

								
									      "powershell -File ${var.Install_LogPath}/RenameComputer-DDC2-Script.ps1"
								

								
									    ]
								

								
									  }
								

								
									}
								
								 

								
									#### Upload RenameComputer script to CVADAVM and execute them
								

								
									###### Set the Provisioner-Connection
								

								
									resource "null_resource" "UploadAndExecuteRenameScripts-CVADAVM" {
								

								
									  depends_on = [ null_resource.UploadAndExecuteScripts-CVADAVM  ]
								

								
									  connection {
								

								
									    type            = var.Provisioner_Type
								

								
									    user            = var.Provisioner_Admin-Username
								

								
									    password        = var.Provisioner_Admin-Password
								

								
									    host            = var.CVADAVM-VM-IP-PreConfig
								

								
									    timeout         = var.Provisioner_Timeout
								
								 

								
									  }
								
								 

								
									###### Upload RenameComputer script to CVADAVM
								

								
									  provisioner "file" {
								

								
									    source      = "${path.module}/data/RenameComputer-CVADAVM-Script.ps1"
								

								
									    destination = "${var.Install_LogPath}/RenameComputer-CVADAVM-Script.ps1"
								

								
									    
								

								
									  }
								
								 

								
									###### Execute the RenameComputer script
								

								
									 provisioner "remote-exec" {
								

								
									    inline = [
								

								
									      "powershell -File ${var.Install_LogPath}/RenameComputer-CVADAVM-Script.ps1"
								

								
									    ]
								

								
									  }
								

								
									}
								
								 

								
									#### Wait for 1 minute to complete reboot and settling of all rename tasks on VMs
								

								
									resource "time_sleep" "WaitToRebootDDC1AfterRename" {
								

								
									  depends_on = [ null_resource.UploadAndExecuteScripts-DDC1 ]
								

								
									  create_duration = "60s"
								

								
									} 
								
								 

								
									resource "time_sleep" "WaitToRebootDDC2AfterRename" {
								

								
									  depends_on = [ null_resource.UploadAndExecuteScripts-DDC2 ]
								

								
									  create_duration = "60s"
								

								
									} 
								
								 

								
									resource "time_sleep" "WaitToRebootCVADAVMAfterRename" {
								

								
									  depends_on = [ null_resource.UploadAndExecuteRenameScripts-CVADAVM ]
								

								
									  create_duration = "60s"
								

								
									} 
								
								 

								
									#### Upload Change-IP-Script to DDC1 and execute them
								

								
									###### Set the Provisioner-Connection
								

								
									resource "null_resource" "UploadAndExecuteChangeIPScript-DDC1" {
								

								
									  depends_on = [ time_sleep.WaitToRebootDDC1AfterRename  ]
								

								
									  connection {
								

								
									    type            = var.Provisioner_Type
								

								
									    user            = var.Provisioner_Admin-Username
								

								
									    password        = var.Provisioner_Admin-Password
								

								
									    host            = var.DDC1-VM-IP-PreConfig
								

								
									    timeout         = var.Provisioner_Timeout
								
								 

								
									  }
								
								 

								
									###### Upload Change-IP-Script script to DDC1
								

								
									  provisioner "file" {
								

								
									    source      = "${path.module}/data/DDC1-IP-Script.ps1"
								

								
									    destination = "${var.Install_LogPath}/DDC1-IP-Script.ps1"
								

								
									    
								

								
									  }
								
								 

								
									###### Execute the Change-IP-Script script
								

								
									 provisioner "remote-exec" {
								

								
									    inline = [
								

								
									      "powershell -File ${var.Install_LogPath}/DDC1-IP-Script.ps1"
								

								
									    ]
								

								
									  }
								

								
									}
								
								 

								
									#### Upload Change-IP-Script to DDC2 and execute them
								

								
									###### Set the Provisioner-Connection
								

								
									resource "null_resource" "UploadAndExecuteChangeIPScript-DDC2" {
								

								
									  depends_on = [ time_sleep.WaitToRebootDDC2AfterRename ]
								

								
									  connection {
								

								
									    type            = var.Provisioner_Type
								

								
									    user            = var.Provisioner_Admin-Username
								

								
									    password        = var.Provisioner_Admin-Password
								

								
									    host            = var.DDC2-VM-IP-PreConfig
								

								
									    timeout         = var.Provisioner_Timeout
								
								 

								
									  }
								
								 

								
									###### Upload Change-IP-Script script to DDC2
								

								
									  provisioner "file" {
								

								
									    source      = "${path.module}/data/DDC2-IP-Script.ps1"
								

								
									    destination = "${var.Install_LogPath}/DDC2-IP-Script.ps1"
								

								
									    
								

								
									  }
								
								 

								
									###### Execute the Change-IP-Script script
								

								
									 provisioner "remote-exec" {
								

								
									    inline = [
								

								
									      "powershell -File ${var.Install_LogPath}/DDC2-IP-Script.ps1"
								

								
									    ]
								

								
									  }
								

								
									}
								
								 

								
									#### Upload Change-IP-Script to CVADAVM and execute them
								

								
									###### Set the Provisioner-Connection
								

								
									resource "null_resource" "UploadAndExecuteChangeIPScript-CVADAVM" {
								

								
									  depends_on = [ time_sleep.WaitToRebootCVADAVMAfterRename  ]
								

								
									  connection {
								

								
									    type            = var.Provisioner_Type
								

								
									    user            = var.Provisioner_Admin-Username
								

								
									    password        = var.Provisioner_Admin-Password
								

								
									    host            = var.CVADAVM-VM-IP-PreConfig
								

								
									    timeout         = var.Provisioner_Timeout
								
								 

								
									  }
								
								 

								
									###### Upload Change-IP-Script script to CVADAVM
								

								
									  provisioner "file" {
								

								
									    source      = "${path.module}/data/CVADAVM-IP-Script.ps1"
								

								
									    destination = "${var.Install_LogPath}/CVADAVM-IP-Script.ps1"
								

								
									    
								

								
									  }
								
								 

								
									###### Execute the Change-IP-Script script
								

								
									 provisioner "remote-exec" {
								

								
									    inline = [
								

								
									      "powershell -File ${var.Install_LogPath}/CVADAVM-IP-Script.ps1"
								

								
									    ]
								

								
									  }
								

								
									}
								
								 

								
									#### Wait for 1 minute to complete IP change tasks on VMs
								

								
									resource "time_sleep" "WaitToRebootDDC1AfterIPChange" {
								

								
									  depends_on = [ null_resource.UploadAndExecuteChangeIPScript-DDC1 ]
								

								
									  create_duration = "60s"
								

								
									} 
								
								 

								
									resource "time_sleep" "WaitToRebootDDC2AfterIPChange" {
								

								
									  depends_on = [ null_resource.UploadAndExecuteChangeIPScript-DDC2 ]
								

								
									  create_duration = "60s"
								

								
									} 
								
								 

								
									resource "time_sleep" "WaitToRebootCVADAVMAfterIPChange" {
								

								
									  depends_on = [ null_resource.UploadAndExecuteChangeIPScript-CVADAVM ]
								

								
									  create_duration = "60s"
								

								
									}
								
								 
								 
								 
								
									##############################################################################################
								

								
									###  All VMs for creating a CVAD 2402 LTSR CU1 site on XenServer should have been created  ###
								

								
									############################################################################################## 
								
							
						
					
				
			
		
	

	
		 
	

	
		Module 3: _CVADOnXenServer-InitialCVADConfiguration
	

	
		These are the Terraform configuration files for Module 3 (excerpts):
	 

	
		CVADOnXenServer-Provider.tf
	

	
		
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}		
		
			
				
					
						
							
								
									# Terraform deployment of Citrix Virtual Apps and Desktops on XenServer 8
								

								
									## Definition of all required Terraform providers
								
								 

								
									terraform {
								

								
									    required_version = "&gt;= 1.9.4"
								
								 

								
									  required_providers {
								

								
									    xenserver = {
								

								
									      source = "xenserver/xenserver"
								

								
									    }
								
								 

								
									    citrix = {
								

								
									      source  = "citrix/citrix"
								

								
									      version = "=1.0.1"
								

								
									    }
								

								
									  }
								

								
									}
								
								 

								
									provider "xenserver" {
								

								
									  username     = var.XenServer_Provider-Username
								

								
									  password     = var.XenServer_Provider-Password
								

								
									  host         = var.XenServer_Provider-Host
								

								
									}
								
								 

								
									provider "citrix" {
								

								
									  cvad_config = {
								

								
									     hostname                  = var.CVAD_Provider-FQDN
								

								
									     client_id                 = var.CVAD_Provider-UN
								

								
									     client_secret             = var.CVAD_Provider-PW
								

								
									     disable_ssl_verification  = true
								

								
									  }
								
								 

								
									  storefront_remote_host = {
								

								
									     computer_name              = var.CVAD_Provider-FQDN
								

								
									     ad_admin_username          = var.CVAD_Provider-UN
								

								
									     ad_admin_password          = var.CVAD_Provider-PW
								

								
									     disable_ssl_verification  = true
								

								
									  }
								
								 

								
									}
								
							
						
					
				
			
		
	

	
		CVADOnXenServer-InitialCVADConfiguration.tf
	

	
		
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}		
		
			
				
					
						
							
								
									# Terraform deployment of Citrix Virtual Apps and Desktops on XenServer 8
								

								
									## Install CVAD and SF with all chosen components and create a new site
								

								
									locals {
								

								
									  installPath       = join("/",[var.CVAD_Install_Source-Drive,var.CVAD_Install_Source-Path])
								

								
									  installSwitches   = join(" ",[var.CVAD_Install_Features-To-Install, var.CVAD_Install_CLI-Switch_NoReboot, var.CVAD_Install_CLI-Switch_ConfigureFirewall, var.CVAD_Install_CLI-Switch_QuietInstall, "/nosql","/Logpath", var.CVAD_Install_LogPath])
								

								
									  completeInstall   = join(" ",[local.installPath,local.installSwitches]) 
								

								
									  SFCompleteInstall = join("",[var.CVAD_Install_Source-Drive,var.SF_Install_Source-Path])
								
								 

								
									 ### Create a CVAD site
								

								
									 #### Create script for local installation of Citrix Studio
								

								
									  InstallStudio     = &lt;&lt;-EOT
								

								
									  ${local.installPath} /components controller,desktopstudio /noreboot /configure_firewall /quiet /nosql /Logpath C:/TEMP/XDINST
								

								
									  EOT
								
								 

								
									 #### Create CVAD-Installation script
								

								
									  CVADScriptcontent     = &lt;&lt;-EOT
								

								
									  if (!(Test-Path ${var.CVAD_Install_LogPath})){
								

								
									      New-Item -Path '${var.CVAD_Install_LogPath}' -ItemType Directory
								

								
									  }
								

								
									  ${local.completeInstall}
								

								
									  #Restart-Computer -Force
								

								
									  EOT
								
								 

								
									  #### Create SF-Installation script
								

								
									  SFScriptcontent     = &lt;&lt;-EOT
								

								
									  ${local.SFCompleteInstall}
								

								
									    EOT
								
								 

								
									  #### Create Reboot script
								

								
									  RebootScriptcontent     = &lt;&lt;-EOT
								

								
									  Restart-Computer -Force
								

								
									  EOT
								
								 

								
									  #### Create StoreFront CleanUp script
								

								
									  StoreFrontCleanUpScriptcontent     = &lt;&lt;-EOT
								

								
									  Clear-STFDeployment -Confirm:$false
								

								
									  EOT
								
								 

								
									  #### Create CVAD-Database-Creation script
								

								
									  #### Need to invoke PowerShell as Domain User as the provisioner does not allow to be run in a Domain Users-context, but CVAD SDK needs to be run as a Domain User
								

								
									  CVADDBScriptcontent     = &lt;&lt;-EOT
								

								
									  #$PSUsername = '${var.XS8_domain_admin_userwithdomain}'
								

								
									  #$PSPassword = '${var.XS8_domain_admin_pw}'
								

								
									  #$PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
								

								
									  #$PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
								

								
									  #Invoke-Command -ComputerName localhost -Credential $PSCredential -ScriptBlock {
								

								
									  Add-PSSnapin Citrix.*
								

								
									  $PSUsername = '${var.XS8_domain_admin_userwithdomain}'
								

								
									  $PSPassword = '${var.XS8_domain_admin_pw}'
								

								
									  $PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
								

								
									  $PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
								

								
									  New-XDDatabase -SiteName ${var.CVAD-SiteName} -AllDefaultDatabases -DatabaseServer ${var.CVAD-DB-ServerName} -DatabaseCredentials $PSCredential -Verbose -AdminAddress ${var.CVAD-DDC1-Name}
								

								
									  #}
								

								
									  EOT 
								
								 

								
									  #### Create CVAD-Site-Creation script
								

								
									  #### Need to invoke PowerShell as Domain User as the provisioner does not allow to be run in a Domain Users-context, but CVAD SDK needs to be run as a Domain User
								

								
									  CVADSiteScriptcontent     = &lt;&lt;-EOT
								

								
									  #$PSUsername = '${var.XS8_domain_admin_userwithdomain}'
								

								
									  #$PSPassword = '${var.XS8_domain_admin_pw}'
								

								
									  #$PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
								

								
									  #$PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
								

								
									  #Invoke-Command -ComputerName localhost -Credential $PSCredential -ScriptBlock {
								

								
									  Add-PSSnapin Citrix.*
								

								
									  $PSUsername = '${var.XS8_domain_admin_userwithdomain}'
								

								
									  $PSPassword = '${var.XS8_domain_admin_pw}'
								

								
									  $PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
								

								
									  $PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
								

								
									  New-XDSite -AdminAddress ${var.CVAD-DDC1-Name} -AllDefaultDatabases -SiteName ${var.CVAD-SiteName} -DatabaseServer ${var.CVAD-DB-ServerName} -Verbose
								

								
									  #Restart-Computer -Force
								

								
									  #}
								

								
									  EOT 
								
								 

								
									  #### Create Add-DDC2-To-CVAD-Site-Creation script
								

								
									  #### Need to invoke PowerShell as Domain User as the provisioner does not allow to be run in a Domain Users-context, but CVAD SDK needs to be run as a Domain User
								

								
									  CVADAddToSiteScriptcontent     = &lt;&lt;-EOT
								

								
									  #$PSUsername = '${var.XS8_domain_admin_userwithdomain}'
								

								
									  #$PSPassword = '${var.XS8_domain_admin_pw}'
								

								
									  #$PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
								

								
									  #$PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
								

								
									  #Invoke-Command -ComputerName localhost -Credential $PSCredential -ScriptBlock {
								

								
									  Add-PSSnapin Citrix.*
								

								
									  $PSUsername = '${var.XS8_domain_admin_userwithdomain}'
								

								
									  $PSPassword = '${var.XS8_domain_admin_pw}'
								

								
									  $PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
								

								
									  $PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
								

								
									  Add-XDController -AdminAddress ${var.CVAD-DDC2-Name} -SiteControllerAddress ${var.CVAD-DDC1-Name}
								

								
									  #}
								

								
									  EOT 
								
								 

								
									#### Create the Add-a-DNS-Host-Entry script
								

								
									#### Need to invoke PowerShell as Domain User as the provisioner does not allow to be run in a Domain Users-context, but CVAD SDK needs to be run as a Domain User
								

								
									CreateDNSARecordScriptcontent     = &lt;&lt;-EOT
								

								
									$PSUsername = '${var.XS8_domain_admin_userwithdomain}'
								

								
									$PSPassword = '${var.XS8_domain_admin_pw}'
								

								
									$PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
								

								
									$PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
								

								
									Invoke-Command -ComputerName localhost -Credential $PSCredential -ScriptBlock {
								

								
									#Create DNS-A-Record for SF-HostBaseURL-Loadbalancing-IP
								

								
									Add-DnsServerResourceRecordA -Name '${var.CVAD-DDC1-Name}' -ZoneName 'the-austrian-citrix-guy.at' -IPv4Address ${var.STF-FarmName-LBIP}
								

								
									} 
								

								
									EOT
								
								 

								
									}
								
								 

								
									#### Write CVACmdletsD Installation script into local data-directory
								

								
									resource "local_file" "WriteCmdletsScriptIntoDataDirectory" {
								

								
									  filename = "${path.module}/data/CVAD-Cmdlets.ps1"
								

								
									  content  = local.InstallStudio
								

								
									}
								
								 

								
									#### Write CVAD Installation script into local data-directory
								

								
									resource "local_file" "WriteCVADScriptIntoDataDirectory" {
								

								
									  filename = "${path.module}/data/CVAD-Install.ps1"
								

								
									  content  = local.CVADScriptcontent
								

								
									}
								
								 

								
									#### Write StoreFront Installation script into local data-directory
								

								
									resource "local_file" "WriteSFScriptIntoDataDirectory" {
								

								
									  filename = "${path.module}/data/SF-Install.ps1"
								

								
									  content  = local.SFScriptcontent
								

								
									}
								
								 

								
									#### Write Reboot script into local data-directory
								

								
									resource "local_file" "WriteRebootScriptIntoDataDirectory" {
								

								
									  filename = "${path.module}/data/Reboot.ps1"
								

								
									  content  = local.RebootScriptcontent
								

								
									}
								
								 

								
									#### Write the Add-a-DNS-Host-Entry script into local data-directory
								

								
									resource "local_file" "WriteLicScriptIntoDataDirectory" {
								

								
									  filename = "${path.module}/data/CVAD-CreateDNSARecordOnDC.ps1"
								

								
									  content  = local.CreateDNSARecordScriptcontent
								

								
									}
								
								 

								
									#### Write Database script into local data-directory
								

								
									resource "local_file" "WriteCVADDBScriptIntoDataDirectory" {
								

								
									  filename = "${path.module}/data/CVAD-DBs.ps1"
								

								
									  content  = local.CVADDBScriptcontent
								

								
									}
								
								 

								
									#### Write Create-Site script into local data-directory
								

								
									resource "local_file" "WriteCVADSiteScriptIntoDataDirectory" {
								

								
									  filename = "${path.module}/data/CVAD-Site.ps1"
								

								
									  content  = local.CVADSiteScriptcontent
								

								
									}
								
								 

								
									#### Write Add-To-Site script into local data-directory
								

								
									resource "local_file" "WriteAddDDC2ToCVADSiteScriptIntoDataDirectory" {
								

								
									  filename = "${path.module}/data/CVAD-AddToSite.ps1"
								

								
									  content  = local.CVADAddToSiteScriptcontent
								

								
									}
								
								 

								
									#### Write Add-To-Site script into local data-directory
								

								
									resource "local_file" "WriteClearStoreFrontDeploymentScriptIntoDataDirectory" {
								

								
									  filename = "${path.module}/data/CVAD-ClearSTF.ps1"
								

								
									  content  = local.StoreFrontCleanUpScriptcontent
								

								
									}
								
								 

								
									##### Execute CVAD-Installation script 
								

								
									resource "null_resource" "InstallCVAD" {
								

								
									  depends_on = [ local_file.WriteCVADScriptIntoDataDirectory ]
								
								 

								
									######Execute script
								

								
									  provisioner "local-exec" {
								

								
									    command = "${path.module}/data/CVAD-Cmdlets.ps1"
								

								
									     interpreter = ["PowerShell", "-Command"]
								

								
									  }
								

								
									} 
								
								 

								
									##### Execute CVAD-DB-Creation script
								

								
									resource "null_resource" "InstallCVADDB" {
								

								
									  depends_on = [ null_resource.InstallCVAD ]
								
								 

								
									###### Execute script
								

								
									  provisioner "local-exec" {
								

								
									    command = ".'${path.module}/data/CVAD-DBs.ps1'"
								

								
									     interpreter = ["PowerShell", "-Command"]
								

								
									  }
								

								
									}
								
								 

								
									##### Execute CVAD-Site-Creation script
								

								
									resource "null_resource" "InstallCVADSite" {
								

								
									  depends_on = [ null_resource.InstallCVADDB ]
								
								 

								
									###### Execute script
								

								
									  provisioner "local-exec" {
								

								
									    command = ".'${path.module}/data/CVAD-Site.ps1'"
								

								
									     interpreter = ["PowerShell", "-Command"]
								

								
									  }
								

								
									}
								
								 

								
									##### Execute CVAD-AddToSite script
								

								
									resource "null_resource" "AddToCVADSite" {
								

								
									  depends_on = [ null_resource.InstallCVADSite ]
								
								 

								
									###### Execute script
								

								
									  provisioner "local-exec" {
								

								
									    command = ".'${path.module}/data/CVAD-AddToSite.ps1'"
								

								
									     interpreter = ["PowerShell", "-Command"]
								

								
									  }
								

								
									}
								
								 

								
									##########################################
								
								 

								
									### Create a StoreFront deployment on DDC1
								

								
									##### Upload StoreFront deletion script to DDC1 and execute it
								

								
									###### Set the Provisioner-Connection
								

								
									resource "null_resource" "UploadAndRunSFDeletionScriptOnDDC1" {
								

								
									  depends_on = [ local_file.WriteClearStoreFrontDeploymentScriptIntoDataDirectory ]
								

								
									  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 CVAD-Installation script to DDC1
								

								
									  provisioner "file" {
								

								
									    source      = "${path.module}/data/CVAD-ClearSTF.ps1"
								

								
									    destination = "${var.CVAD_Install_LogPath}/CVAD-ClearSTF.ps1"
								

								
									    
								

								
									  }
								
								 

								
									###### Execute the CVAD Installation script
								

								
									 provisioner "remote-exec" {
								

								
									    inline = [
								

								
									      "powershell -File ${var.CVAD_Install_LogPath}/CVAD-ClearSTF.ps1"
								

								
									    ]
								

								
									  }
								

								
									}
								

								
									##### Upload StoreFront deletion script to DDC2 and execute it
								

								
									###### Set the Provisioner-Connection
								

								
									resource "null_resource" "UploadAndRunSFDeletionScriptOnDDC2" {
								

								
									  depends_on = [ local_file.WriteClearStoreFrontDeploymentScriptIntoDataDirectory ]
								

								
									  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 CVAD-Installation script to DDC2
								

								
									  provisioner "file" {
								

								
									    source      = "${path.module}/data/CVAD-ClearSTF.ps1"
								

								
									    destination = "${var.CVAD_Install_LogPath}/CVAD-ClearSTF.ps1"
								

								
									    
								

								
									  }
								
								 

								
									###### Execute the CVAD Installation script
								

								
									 provisioner "remote-exec" {
								

								
									    inline = [
								

								
									      "powershell -File ${var.CVAD_Install_LogPath}/CVAD-ClearSTF.ps1"
								

								
									    ]
								

								
									  }
								

								
									}
								
								 

								
									resource "citrix_stf_deployment" "CreateSTFDeployment" {
								

								
									  site_id                 = var.STF-SiteID
								

								
									  host_base_url           = var.STF-BaseURL
								

								
									}
								
								 

								
									resource "citrix_stf_authentication_service" "CreateSTFAuthenticationService" {
								

								
									  depends_on          = [ citrix_stf_deployment.CreateSTFDeployment ]
								

								
									  site_id                 = citrix_stf_deployment.CreateSTFDeployment.site_id
								

								
									  friendly_name           = var.STF-Auth-FriendlyName
								

								
									  virtual_path            = var.STF-Auth-VirtualPath
								

								
									}
								
								 

								
									resource "citrix_stf_store_service" "CreateSTFStoreService" {
								

								
									  depends_on        = [ citrix_stf_authentication_service.CreateSTFAuthenticationService ]
								

								
									  site_id                             = citrix_stf_deployment.CreateSTFDeployment.site_id
								

								
									  friendly_name                       = var.STF-Store-FriendlyName
								

								
									  virtual_path                        = var.STF-Store-VirtualPath
								

								
									  authentication_service_virtual_path = "${citrix_stf_authentication_service.CreateSTFAuthenticationService.virtual_path}"
								

								
									  
								

								
									  pna               = {
								

								
									                          enable      = var.STF-EnablePNA
								

								
									                      }
								
								 

								
									 farms             = [
								

								
									               {
								

								
									                          farm_name   = var.CVAD-SiteName
								

								
									                          farm_type   = "XenDesktop"
								

								
									                          servers     = var.STF-Farm-Servers 
								

								
									                          port        = var.STF-Port
								

								
									               },
								

								
									                      ] 
								

								
									}
								
								 

								
									resource "citrix_stf_webreceiver_service" "CreateSTFWebReceiverService"{
								

								
									  depends_on          = [ citrix_stf_store_service.CreateSTFStoreService ]
								

								
									  site_id             = citrix_stf_deployment.CreateSTFDeployment.site_id
								

								
									  friendly_name       = var.STF-WebReceiver-FriendlyName
								

								
									  virtual_path        = var.STF-WebReceiver-VirtualPath
								

								
									  store_virtual_path  = "${citrix_stf_store_service.CreateSTFStoreService.virtual_path}"
								

								
									}
								
								 

								
									resource "citrix_stf_roaming_beacon" "CreateSTFRoamingBeacon" {
								

								
									  depends_on          = [ citrix_stf_store_service.CreateSTFStoreService ]
								

								
									  internal_ip         = var.STF-BaseURL
								

								
									  external_ips        = var.STF-External-Beacons
								

								
									  site_id             = var.STF-SiteID
								

								
									}
								
								 

								
									resource "citrix_stf_roaming_gateway" "CreateSTFRoamingGateway" {
								

								
									  depends_on                     = [ citrix_stf_deployment.CreateSTFDeployment ]
								

								
									  site_id                        = citrix_stf_deployment.CreateSTFDeployment.site_id
								

								
									  name                           = var.STF-GW-Name
								

								
									  logon_type                     = var.STF-GW-LogonType
								

								
									  gateway_url                    = var.STF-GW-URL
								

								
									  callback_url                   = var.STF-GW-CallbackURL
								

								
									  version                        = var.STF-GW-Version
								

								
									  subnet_ip_address              = var.STF-GW-SNIP
								

								
									  stas_bypass_duration           = var.STF-GW-Bypass
								

								
									  session_reliability            = var.STF-GW-SessionReliability
								

								
									  request_ticket_two_stas        = var.STF-GW-Request2STAs
								

								
									  stas_use_load_balancing        = var.STF-GW-UseSTALB
								

								
									  is_cloud_gateway               = var.STF-GW-IsCloudGateway
								

								
									  secure_ticket_authority_urls   = [ 
								

								
									      {
								

								
									      sta_url                    = var.STF-GW-STA1
								

								
									      },
								

								
									      {
								

								
									      sta_url                    = var.STF-GW-STA2
								

								
									      },
								

								
									    ]
								

								
									}
								
								 

								
									resource "citrix_stf_xenapp_default_store" "CreateSTFDefaultStore" {
								

								
									  depends_on                     = [ citrix_stf_store_service.CreateSTFStoreService ]
								

								
									  store_virtual_path             = citrix_stf_store_service.CreateSTFStoreService.virtual_path
								

								
									  store_site_id                  = citrix_stf_store_service.CreateSTFStoreService.site_id
								

								
									}
								

								
									 
								

								
									#################################################################################################################
								

								
									###  All initial configurations for creating a CVAD 2402 LTSR CU1 site on XenServer should have been created  ###
								

								
									#################################################################################################################
								
							
						
					
				
			
		
	

	
		 
	

	
		Module 4: _CVADOnXenServer-Configuration-AfterStorefront
	

	
		These are the Terraform configuration files for Module 4 (excerpts):
	 

	
		CVADOnXenServer-Configuration-AfterStorefront-scripts.tf
	

	
		
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}		
		
			
				
					
						
							
								
									# Terraform deployment of Citrix Virtual Apps and Desktops on XenServer 8
								

								
									## Run PowerShell-Scripts on the DDCs
								
								 

								
									### Install CVAD and SF with all chosen components and create a new site
								

								
									locals {
								

								
									  #### Create License- and PFX-Import script
								

								
									  #### Need to invoke PowerShell as Domain User as the provisioner does not allow to be run in a Domain Users-context, but CVAD SDK needs to be run as a Domain User
								

								
									  LicScriptcontent     = &lt;&lt;-EOT
								

								
									  $PSUsername = '${var.XS8_domain_admin_userwithdomain}'
								

								
									  $PSPassword = '${var.XS8_domain_admin_pw}'
								

								
									  $PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
								

								
									  $PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
								

								
									  Invoke-Command -ComputerName localhost -Credential $PSCredential -ScriptBlock {
								

								
									  Add-PSSnapin Citrix.*
								

								
									  Set-ConfigSite -LicenseServerUri '${var.CVAD-Lic-ServerAddress}' -LicenseServerPort '${var.CVAD-Lic-Port}' -ProductEdition '${var.CVAD-Lic-ProductEdition}'
								

								
									  Restart-Service -Name 'Citrix Licensing'
								

								
									  }
								

								
									  EOT
								
								 

								
									  #### Create PFX-Import script
								

								
									  #### Need to invoke PowerShell as Domain User as the provisioner does not allow to be run in a Domain Users-context, but CVAD SDK needs to be run as a Domain User
								

								
									  PFXScriptcontent     = &lt;&lt;-EOT
								

								
									  $PSUsername = '${var.XS8_domain_admin_userwithdomain}'
								

								
									  $PSPassword = '${var.XS8_domain_admin_pw}'
								

								
									  $PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
								

								
									  $PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
								

								
									  Invoke-Command -ComputerName localhost -Credential $PSCredential -ScriptBlock {
								

								
									  Add-PSSnapin Citrix.*
								

								
									  Import-PfxCertificate -CertStoreLocation Cert:\LocalMachine\Webhosting -FilePath '${var.CVAD_Install_LogPath}/${var.CVAD-PFX-Name}'
								

								
									  }
								

								
									  EOT
								
								 

								
									  #### Create script to change IIS certificate from self-signed certificate to public certificate 
								

								
									  #### Need to invoke PowerShell as Domain User as the provisioner does not allow to be run in a Domain Users-context, but CVAD SDK needs to be run as a Domain User
								

								
									  ChangeIISSSLScriptcontent     = &lt;&lt;-EOT
								

								
									  $PSUsername = '${var.XS8_domain_admin_userwithdomain}'
								

								
									  $PSPassword = '${var.XS8_domain_admin_pw}'
								

								
									  $PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
								

								
									  $PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
								

								
									  Invoke-Command -ComputerName localhost -Credential $PSCredential -ScriptBlock {
								

								
									  Remove-WebBinding -Name '${var.CVAD-IIS-SiteName}' -IP '*' -Port 443 -Protocol https
								

								
									  New-WebBinding -Name '${var.CVAD-IIS-SiteName}' -IP '*' -Port 443 -Protocol https
								

								
									  $SSLCert = Get-ChildItem -Path Cert:\LocalMachine\WebHosting | Where-Object {$_.Subject.Contains('${var.CVAD-IIS-CertSubject}')}
								

								
									  $Binding = Get-WebBinding -Name '${var.CVAD-IIS-SiteName}' -Protocol "https"
								

								
									  $Binding.AddSslCertificate($SSLCert.GetCertHashString(), "WebHosting")
								

								
									  IISReset
								

								
									  }
								

								
									  EOT
								
								 

								
									}
								
								 

								
									#### Write License- and PFX-Import script into local data-directory
								

								
									resource "local_file" "WriteLicScriptIntoDataDirectory" {
								

								
									  filename = "${path.module}/data/CVAD-Lic.ps1"
								

								
									  content  = local.LicScriptcontent
								

								
									}
								
								 

								
									#### Write PFX-Import script into local data-directory
								

								
									resource "local_file" "WritePFXScriptIntoDataDirectory" {
								

								
									  filename = "${path.module}/data/CVAD-PFX.ps1"
								

								
									  content  = local.PFXScriptcontent
								

								
									} 
								
								 

								
									#### Write PFX-Import script into local data-directory
								

								
									resource "local_file" "WriteIISSSLScriptIntoDataDirectory" {
								

								
									  filename = "${path.module}/data/CVAD-IIS.ps1"
								

								
									  content  = local.ChangeIISSSLScriptcontent
								

								
									}
								
								 

								
									### Upload license file, PFX certificate and Lic script to DDC1 and execute it
								

								
									#### Set the Provisioner-Connection
								

								
									 resource "null_resource" "CreateCVADLicPFXOnDDC1" {
								

								
									 depends_on = [ local_file.WriteLicScriptIntoDataDirectory, local_file.WritePFXScriptIntoDataDirectory, local_file.WriteIISSSLScriptIntoDataDirectory ]
								

								
									 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 License script to DDC1
								

								
									  provisioner "file" {
								

								
									    source      = "${path.module}/data/CVAD-Lic.ps1"
								

								
									    destination = "${var.CVAD_Install_LogPath}/CVAD-Lic.ps1"
								

								
									    
								

								
									  }
								
								 

								
									###### Upload PFX script to DDC1
								

								
									  provisioner "file" {
								

								
									    source      = "${path.module}/data/CVAD-PFX.ps1"
								

								
									    destination = "${var.CVAD_Install_LogPath}/CVAD-PFX.ps1"
								

								
									    
								

								
									  }
								
								 

								
									###### Upload PFX file to DDC1
								

								
									  provisioner "file" {
								

								
									    source      = "${path.module}/data/${var.CVAD-PFX-Name}"
								

								
									    destination = "${var.CVAD_Install_LogPath}/${var.CVAD-PFX-Name}"
								

								
									    
								

								
									  }
								
								 

								
									###### Upload IIS-SSLscript to DDC1
								

								
									  provisioner "file" {
								

								
									    source      = "${path.module}/data/CVAD-IIS.ps1"
								

								
									    destination = "${var.CVAD_Install_LogPath}/CVAD-IIS.ps1"
								

								
									    
								

								
									  }
								

								
									 
								

								
									###### Execute the License script to DDC1
								

								
									 provisioner "remote-exec" {
								

								
									    inline = [
								

								
									      "powershell -File ${var.CVAD_Install_LogPath}/CVAD-Lic.ps1"
								

								
									    ]
								

								
									  }
								
								 

								
									###### Execute the PFX script to DDC1
								

								
									 provisioner "remote-exec" {
								

								
									    inline = [
								

								
									      "powershell -File ${var.CVAD_Install_LogPath}/CVAD-PFX.ps1"
								

								
									    ]
								

								
									  }
								

								
									}
								

								
									 
								

								
									### Upload PFX certificate and PFX script to DDC2 and execute it
								

								
									#### Set the Provisioner-Connection
								

								
									 resource "null_resource" "CreateCVADPFXOnDDC2" {
								

								
									 depends_on = [ local_file.WriteLicScriptIntoDataDirectory, local_file.WritePFXScriptIntoDataDirectory, local_file.WriteIISSSLScriptIntoDataDirectory ]
								

								
									 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 PFX script to DDC2
								

								
									  provisioner "file" {
								

								
									    source      = "${path.module}/data/CVAD-PFX.ps1"
								

								
									    destination = "${var.CVAD_Install_LogPath}/CVAD-PFX.ps1"
								

								
									    
								

								
									  }
								
								 

								
									###### Upload PFX file to DDC2
								

								
									  provisioner "file" {
								

								
									    source      = "${path.module}/data/${var.CVAD-PFX-Name}"
								

								
									    destination = "${var.CVAD_Install_LogPath}/${var.CVAD-PFX-Name}"
								

								
									    
								

								
									  }
								
								 

								
									###### Upload IIS-SSLscript to DDC2
								

								
									  provisioner "file" {
								

								
									    source      = "${path.module}/data/CVAD-IIS.ps1"
								

								
									    destination = "${var.CVAD_Install_LogPath}/CVAD-IIS.ps1"
								

								
									    
								

								
									  }
								
								 

								
									###### Execute the PFX script onto DDC2
								

								
									 provisioner "remote-exec" {
								

								
									    inline = [
								

								
									      "powershell -File ${var.CVAD_Install_LogPath}/CVAD-PFX.ps1"
								

								
									    ]
								

								
									  }
								

								
									}
								
								 

								
									### Execute IIS-SSL script on DDC1 
								

								
									#### Set the Provisioner-Connection
								

								
									 resource "null_resource" "ChangeSSLOnDDC1" {
								

								
									 depends_on = [ null_resource.CreateCVADLicPFXOnDDC1 ]
								

								
									 connection {
								

								
									    type            = var.Provisioner_Type
								

								
									    user            = var.Provisioner_Admin-Username
								

								
									    password        = var.Provisioner_Admin-Password
								

								
									    host            = var.Provisioner_DDC1-IP
								

								
									    timeout         = var.Provisioner_Timeout
								
								 

								
									  }
								

								
									 
								

								
									###### Execute the IIS-SSL script on DDC1
								

								
									 provisioner "remote-exec" {
								

								
									    inline = [
								

								
									      "powershell -File ${var.CVAD_Install_LogPath}/CVAD-IIS.ps1"
								

								
									    ]
								

								
									  }
								

								
									}
								

								
									 
								

								
									### Execute IIS-SSL script on DDC2d execute it
								

								
									#### Set the Provisioner-Connection
								

								
									 resource "null_resource" "ChangeSSLOnDDC2" {
								

								
									 depends_on = [ null_resource.CreateCVADPFXOnDDC2 ]
								

								
									connection {
								

								
									    type            = var.Provisioner_Type
								

								
									    user            = var.Provisioner_Admin-Username
								

								
									    password        = var.Provisioner_Admin-Password
								

								
									    host            = var.Provisioner_DDC2-IP
								

								
									    timeout         = var.Provisioner_Timeout
								
								 

								
									  }
								

								
									 
								

								
									###### Execute the IIS-SSL script on DDC2
								

								
									 provisioner "remote-exec" {
								

								
									    inline = [
								

								
									      "powershell -File ${var.CVAD_Install_LogPath}/CVAD-IIS.ps1"
								

								
									    ]
								

								
									  }
								

								
									}
								
								 

								
									##### Reboot DDC1 
								

								
									###### Set the Provisioner-Connection
								

								
									resource "null_resource" "RebootDDC1-2" {
								

								
									  depends_on = [ null_resource.ChangeSSLOnDDC1 ]
								
								 

								
									connection {
								

								
									    type            = var.Provisioner_Type
								

								
									    user            = var.Provisioner_Admin-Username
								

								
									    password        = var.Provisioner_Admin-Password
								

								
									    host            = var.Provisioner_DDC1-IP
								

								
									    timeout         = var.Provisioner_Timeout
								
								 

								
									  }
								
								 

								
									###### Execute the Reboot script
								

								
									  provisioner "remote-exec" {
								

								
									    inline = [
								

								
									      "powershell -File ${var.CVAD_Install_LogPath}/Reboot.ps1"
								

								
									    ]
								

								
									  }
								

								
									}
								
								 

								
									##### Reboot DDC2 
								

								
									###### Set the Provisioner-Connection
								

								
									resource "null_resource" "RebootDDC2-2" {
								

								
									  depends_on = [ null_resource.ChangeSSLOnDDC2 ]
								
								 

								
									connection {
								

								
									    type            = var.Provisioner_Type
								

								
									    user            = var.Provisioner_Admin-Username
								

								
									    password        = var.Provisioner_Admin-Password
								

								
									    host            = var.Provisioner_DDC2-IP
								

								
									    timeout         = var.Provisioner_Timeout
								
								 

								
									  }
								
								 

								
									###### Execute the Reboot script
								

								
									  provisioner "remote-exec" {
								

								
									    inline = [
								

								
									      "powershell -File ${var.CVAD_Install_LogPath}/Reboot.ps1"
								

								
									    ]
								

								
									  }
								

								
									}
								

								
									 
								

								
									
										#####################################################################################
									

									
										###  All configurations after the StoreFront deployment should have been created  ###
									

									
										#####################################################################################
									
								
							
						
					
				
			
		
	

	
		 
	

	
		Module 5: _CVADOnXenServer-CVAD-Entities
	

	
		These are the Terraform configuration files for Module 5 (excerpts):
	 

	
		CVADOnXenServer-CVAD-Entities.tf
	

	
		
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}		
		
			
				
					
						
							
								
									# Terraform deployment of Citrix Virtual Apps and Desktops on XenServer 8
								

								
									## Create the Hosting Connection, a Machine Catalog and a Delivery Group
								

								
									### Create the necessary zone
								

								
									resource "citrix_zone" "TACG-TF-HYP-Zone" {
								

								
									    name        = "${var.CVAD_HC-ZoneName}"
								

								
									}
								
								 

								
									#### Creating the Hypervisor Connection
								

								
									resource "citrix_xenserver_hypervisor" "CreateHypervisorConnection" {
								

								
									  depends_on = [ citrix_zone.TACG-TF-HYP-Zone]
								

								
									    name                        = var.CVAD_XS8-HypConn-Name
								

								
									    zone                        = citrix_zone.TACG-TF-HYP-Zone.id
								

								
									    username                    = var.CVAD_XS8-HypConn-UN
								

								
									    password                    = var.CVAD_XS8-HypConn-PW
								

								
									    password_format             = var.CVAD_XS8-HypConn-PWFormat
								

								
									    addresses                   = var.CVAD_XS8-HypConn-IP
								

								
									    #ssl_thumbprints             = var.CVAD_XS8-SSLThumbs
								
								 

								
									} 
								
								 

								
									#### Sleep 30s to let Background processes settle
								

								
									resource "time_sleep" "wait_30_seconds" {
								

								
									  depends_on = [ citrix_xenserver_hypervisor.CreateHypervisorConnection ]
								

								
									  create_duration = "30s"
								

								
									}
								
								 

								
									data "citrix_hypervisor" "XS8" {
								

								
									  name = var.CVAD_XS8-HypConn-Name
								

								
									}
								
								 

								
									data "citrix_zone" "XS8Zone" {
								

								
									  name = var.CVAD_HC-ZoneName
								

								
									}
								

								
									 
								

								
									output "Hyp" {
								

								
									  value = data.citrix_hypervisor.XS8.id
								

								
									}
								
								 

								
									output "Zone" {
								

								
									  value = data.citrix_zone.XS8Zone.id
								

								
									}
								
								 

								
									#### Creating the Hypervisor Resource Pool
								

								
									resource "citrix_xenserver_hypervisor_resource_pool" "CreateHypervisorPool" {
								

								
									  #depends_on = [ time_sleep.wait_30_seconds]
								

								
									    name                        = var.CVAD_XS8-HypConnPool-Name
								

								
									    hypervisor                  = data.citrix_hypervisor.XS8.id
								

								
									    networks                    = var.CVAD_XS8-HypConnPool-Networks  
								
								 

								
									    storage = [ {
								

								
									      storage_name = var.CVAD_XS8-HypConnPool-Storage
								

								
									    } ]   
								
								 

								
									     temporary_storage = [ {
								

								
									      storage_name = var.CVAD_XS8-HypConnPool-TempStorage
								

								
									    } ]   
								

								
									}  
								

								
									 
								
								 

								
									#### Sleep 30s to let Background processes settle
								

								
									resource "time_sleep" "wait_30_seconds1" {
								

								
									  depends_on = [ citrix_xenserver_hypervisor_resource_pool.CreateHypervisorPool ]
								

								
									  create_duration = "30s"
								

								
									}
								
								 

								
									### Creating a Machine Catalog
								

								
									#### Create the Machine Catalog
								

								
									resource "citrix_machine_catalog" "CreateXS8MCSCatalog" {
								

								
									  depends_on            = [ time_sleep.wait_30_seconds1 ]
								

								
									    name                        = var.CVAD_XS8-MC-Name
								

								
									    description                 = var.CVAD_XS8-MC-Description
								

								
									    allocation_type             = var.CVAD_XS8-MC-AllocationType
								

								
									    session_support             = var.CVAD_XS8-MC-SessionType
								

								
									    provisioning_type           = "MCS"
								

								
									    zone                        = data.citrix_zone.XS8Zone.id
								

								
									    provisioning_scheme         =   {
								

								
									        hypervisor               = data.citrix_hypervisor.XS8.id
								

								
									        hypervisor_resource_pool = citrix_xenserver_hypervisor_resource_pool.CreateHypervisorPool.id
								

								
									        identity_type            = var.CVAD_XS8-MC-IDPType
								
								 

								
									        machine_domain_identity  = {
								

								
									            domain                   = var.CVAD_XS8-MC-Domain
								

								
									            domain_ou                = var.CVAD_XS8-MC-DomainOU
								

								
									            service_account          = var.CVAD_XS8-MC-DomainAdmin-Username-UPN
								

								
									            service_account_password = var.CVAD_XS8-MC-DomainAdmin-Password
								

								
									        }
								
								 

								
									        xenserver_machine_config = {
								

								
									            master_image_vm        = var.CVAD_XS8-MC-MasterImage
								

								
									            cpu_count              = var.CVAD_XS8-MC-CPUCount
								

								
									            memory_mb              = var.CVAD_XS8-MC-Memory
								
								 

								
									           writeback_cache = {
								

								
									                  writeback_cache_disk_size_gb = 10
								

								
									                  writeback_cache_memory_size_mb = 256
								

								
									                }
								
								 

								
									        }
								
								 

								
									        number_of_total_machines =  var.CVAD_XS8-MC-Machine_Count
								
								 

								
									        machine_account_creation_rules = {
								

								
									            naming_scheme      = var.CVAD_XS8-MC-Naming_Scheme_Name
								

								
									            naming_scheme_type = var.CVAD_XS8-MC-Naming_Scheme_Type
								

								
									        }
								

								
									    }
								

								
									}
								
								 

								
									#### Sleep 60s to let CC Background processes settle
								

								
									resource "time_sleep" "wait_30_seconds_2" {
								

								
									  depends_on = [ citrix_machine_catalog.CreateXS8MCSCatalog ]
								

								
									  create_duration = "30s"
								

								
									}   
								
								 

								
									#### Create the Delivery Group based on the Machine Catalog
								

								
									resource "citrix_delivery_group" "CreateDG" {
								

								
									  depends_on = [ time_sleep.wait_30_seconds_2]
								

								
									    name                                    = var.CVAD_XS8-DG-Name
								

								
									    associated_machine_catalogs             = [
								

								
									        {
								

								
									            machine_catalog                 = citrix_machine_catalog.CreateXS8MCSCatalog.id
								

								
									            machine_count                   = var.CVAD_XS8-MC-Machine_Count
								

								
									        }
								

								
									    ]
								

								
									    desktops                                = [
								

								
									        {
								

								
									            published_name                  = var.CVAD_XS8-DG-PublishedDesktopName
								

								
									            description                     = var.CVAD_XS8-DG-Description
								

								
									            restricted_access_users         = {
								

								
									                                               allow_list = [ "TACG\\vdaallowed" ]
								

								
									                                              }
								

								
									            enabled                         = true
								

								
									            enable_session_roaming          = var.CVAD_XS8-DG-SessionRoaming
								

								
									        }
								

								
									        
								

								
									    ] 
								

								
									    autoscale_settings                      = {
								

								
									            autoscale_enabled                               = true
								

								
									            disconnect_peak_idle_session_after_seconds      = 300
								

								
									            log_off_peak_disconnected_session_after_seconds = 300
								

								
									            peak_disconnect_action                          = "Suspend"
								

								
									            off_peak_disconnect_action                      = "Suspend"
								

								
									            
								

								
									            power_time_schemes              = [
								

								
									                                              {
								

								
									                                               days_of_week = [
								

								
									                                                              "Monday",
								

								
									                                                              "Tuesday",
								

								
									                                                              "Wednesday",
								

								
									                                                              "Thursday",
								

								
									                                                              "Friday"
								

								
									                                                              ]
								

								
									                name                        = var.CVAD_XS8-DG-AS-Name
								

								
									                display_name                = var.CVAD_XS8-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\\vdaallowed" ]
								

								
									                                              }
								

								
									    reboot_schedules                        = [
								

								
									                                               {
								

								
									                                                 name = "TACG-XS8-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
								

								
									                                               }
								

								
									  ]
								

								
									}
								

								
									 
								

								
									
										#################################################################################################################
									

									
										###  All CVAD-related entities for deploying a CVAD 2402 LTSR CU1 site on XenServer should have been created  ###
									

									
										#################################################################################################################
									
								
							
						
					
				
			
		
	

	
		 
	

	
		 
	 

	
		 
	 



	 
	
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_09/stf-provider.png.254ddb50e7fa54fac9f67859f2efc196.png" length="74584" type="image/png"/><pubDate>Fri, 13 Sep 2024 10:56:41 +0000</pubDate></item><item><title>Baseline Deployment Prerequisites for Citrix on Azure</title><link>https://community.stage.citrix.com/tech-zone/build/tech-papers/deployment-prerequisites-citrix-on-azure/</link><description><![CDATA[Overview



	The following tech paper focuses on the prerequisites for building an Azure environment to host a Citrix solution. It outlines the key design areas, Citrix environment implications, and initial requirements, emphasizing the importance of a well-architected, secure platform. The document details Azure components, including subscriptions, virtual networks, and resource groups. It guides virtual machine setup, availability zones, storage solutions, and Active Directory configurations. Additionally, the document covers essential considerations for Entra ID-joined VDAs, user and service accounts, DNS records, certificate services, software downloads, and antivirus exclusions.
 


	Prerequisites Checklist



	The environment prerequisites checklist items are generally listed in sequential order. The “Environment Prerequisites” section outlines prerequisite specifics. 
 


	
		Design and Build Azure Landing Zone

		
			
				Create Tenant
			
			
				Create Azure Core Subscription
			
			
				Create Azure Citrix Subscription
			
			
				Create Management Groups
			
			
				Create Azure Virtual Network and Subnets
			
			
				Implement Azure ExpressRoute (Optional)
			
			
				Open Firewall Communication Ports
			
			
				Create Azure Resource Group(s)
			
		
	
	
		Build Azure Infrastructure VMs:
		
			
				Cloud Connector
			
			
				StoreFront (Optional)
			
			
				NetScaler (Optional)
			
			
				FAS (Optional)
			
			
				Certificate Authority (Optional, required for FAS)
			
			
				RDS License Server (Server OS workloads only)
			
			
				Master Image (Server and/or Desktop OS)
			
		
	
	
		Setup Availability Zones for Infrastructure
	
	
		Design and Implement Profile Storage Solution
	
	
		Active Directory:
		
			
				Minimum Functional Level
			
			
				Build OU Structure
			
			
				Confirm Access to the Policy Definitions Folder
			
			
				Review the Entra ID Joined VDA Considerations
			
			
				Create Service Accounts
			
			
				Create DNS Records
			
			
				Request Certificates
			
		
	
	
		Download Citrix Software 
	
	
		Implement Citrix-Recommended Antivirus Exclusions
	



	Environment Prerequisites



	Azure Landing Zone



	Microsoft has created the Cloud Adoption Framework (CAF) for Azure to guide each phase of a customer’s public cloud journey. Within the CAF, Azure Landing Zones outline design guidelines for a well-architected, secure platform, which are critical to a successful Citrix on Azure solution. The table below outlines the key Azure Landing Zone design areas, the relationship to a Citrix environment, and the baseline requirement to begin a Citrix on Azure journey. 
 


	
		Note:
	 

	
		These requirements will change as you scale at an enterprise level. Use the following article and included diagram for initial guidance on a Citrix on Azure Landing Zone design: Enterprise-Scale Support for Citrix on Azure.
	 



	See the following Microsoft documentation for additional information on Azure Landing Zones:
 


	
		Azure Landing Zone Architecture
	
	
		Azure Landing Zone Design Areas
	
	
		Azure Enterprise-Scale Landing Zones
	



	See the following Citrix blogs and documentation for guidance on how Citrix fits into the CAF:
 


	
		Citrix Tech Zone: Citrix DaaS on Azure Reference Architecture
	
	
		Citrix Tech Zone: Citrix DaaS on Azure Design Decisions
	
	
		Citrix Blog: Introducing Citrix on Azure Enterprise Scale Reference Architecture
	



	
		
			
				
					Design Area
				 
			
			
				
					Citrix Environment Implications
				 
			
			
				
					Initial Requirement
				 
			
		
	
	
		
			
				
					Azure Billing and AD Tenant
				 
			
			
				
					An active subscription with Microsoft is needed to consume Azure services. Additionally, an organization will use Entra ID to host the Azure identities and may also choose to use traditional Active Directory services if the Entra ID capabilities alone are not sufficient.
				 
			
			
				
					Set up a Microsoft Enterprise Agreement, Entra ID tenant, and consider a traditional Active Directory tenant based on requirements.
				 
			
		
		
			
				
					Identity and Access Management
				 
			
			
				
					Core considerations for administration, user authentication, and resource privileges. 
				 
			
			
				
					Leverage Entra ID exclusively or a hybrid solution with Entra ID and traditional Active Directory.
				 
			
		
		
			
				
					Network Topology and Connectivity
				 
			
			
				
					Fundamental for connectivity to other Azure components/services, on-premises infrastructure, and external communication.
				 
			
			
				
					Deploy at least one Citrix network and subnet with the opportunity to separate components based on the use case (internet, internal, etc.). Consider an Azure ExpressRoute for robust connectivity to on-premises resources. Evaluate the use of a firewall and/or Azure network security groups. 
				 
			
		
		
			
				
					Resource Organization
				 
			
			
				
					Organization via naming, tagging, subscription design, and management group design becomes crucial as companies plan and scale their Citrix on Azure deployment alongside non-Citrix use cases.
				 
			
			
				
					Deploy a single, dedicated Azure subscription for Citrix in a single region.
				 
			
		
		
			
				
					Security
				 
			
			
				
					Critical foundation to secure all environment components, from workloads to user data.
				 
			
			
				
					Work with the Security Architecture team to develop a robust security stance for the Citrix platform.
				 
			
		
		
			
				
					Management
				 
			
			
				
					A management baseline is required for stable, ongoing operations in the cloud to provide visibility, operations compliance, and successful administration.
				 
			
			
				
					Develop a multi-tiered administration model based on the principle of least privilege. This model applies to Azure and Citrix Cloud services.
				 
			
		
		
			
				
					Governance
				 
			
			
				
					Governance is important for tracking and optimizing costs, monitoring the platform’s compliance and security stance, and keeping the Azure landing zone up to date.
				 
			
			
				
					Consider leveraging Azure native tools like Azure Monitor, Cost Management, and Security Center. Implement and maintain an antivirus solution.
				 
			
		
		
			
				
					Platform Automation and DevOps
				 
			
			
				
					Modernize the Citrix deployment and expansion through infrastructure as code.
				 
			
			
				
					It is not required for an initial Citrix deployment but may be considered a future enhancement.
				 
			
		
	



	 
 


	Azure Subscriptions



	Citrix recommends deploying a single dedicated Azure subscription for the Citrix environment (infrastructure and workloads) at a minimum. Additional subscriptions may be used for scale, security, or company standards. Many enterprises often have a separate subscription for non-Citrix core services and components. 
 


	
		Note:
	 

	
		Azure subscriptions may be deployed across regions if the VM count stays below the Citrix Limits.  
	 



	 
 


	Network



	Azure Networks



	An Azure Virtual Network is required in each Azure Region to host virtual machines. A minimum of one Virtual Network is needed. However, many enterprises deploy separate Virtual Networks for Infrastructure, NetScalers, Server OS Virtual Delivery Agents (VDA), and Desktop OS VDAs. Additionally, subnetting can be used to segment Azure Virtual Networks. Separating infrastructure and workloads into different networks and/or subnets lets you control and restrict traffic via Azure network security groups (NSGs). Citrix recommends working with the network team to design the Azure networking architecture.
 


	Azure ExpressRoute



	Many enterprises have components that will remain in on-premises data centers (e.g., application backends and user data). To establish connectivity from on-premises data centers to Azure, Citrix recommends an Azure ExpressRoute for best performance. The throughput will be dependent on the expected network load between the locations. Alternatively, you can consider a site-to-site VPN.
 


	Routing and Firewall



	Firewall rules must be created with the ports detailed in Citrix Tech Zone: Communication Ports Used by Citrix Technologies. The System and Connectivity Requirements article details the required internet connectivity and URL whitelisting for various components to communicate with Citrix Cloud services.
 


	Azure Resource Groups



	Resource Groups logically separate and group resources within the Azure subscription. Given this, the number of Resource Groups will vary depending on customer preferences. The following outlines example Resource Groups for a Citrix on Azure deployment.
 


	
		Infrastructure
	
	
		Citrix Master Images
	
	
		Citrix VDAs (multiple often deployed for different use cases)
	
	
		Network
	
	
		Testing (separate often deployed for Infrastructure, Master Images, VDAs) 
	



	Azure Virtual Machines 



	The following table outlines the virtual machines required to deploy a baseline Citrix on Azure environment. All virtual machines must be created, accessible on the network, and joined to the primary domain (Windows machines only). 
 


	
		Note:
	 

	
		Several components are optional based on design decisions, such as the access tier and authentication method. Citrix recommends using the latest generally available Azure VM SKU version.
	 



	 
 


	
		
			
				
					Component
				 
			
			
				
					Operating System
				 
			
			
				
					Quantity
				 
			
			
				
					Instance Type
				 
			
			
				
					Storage
				 
			
			
				
					Notes
				 
			
		
	
	
		
			
				
					Citrix StoreFront 
				 
			
			
				
					Windows Server 2022
				 
			
			
				
					2
				 
			
			
				
					D4s_vX
				 

				
					(4vCPU x 16 GiB RAM)
				 
			
			
				
					128 GiB
				 
			
			
				
					Optional. Dependent on on-premises access tier versus Citrix Cloud access tier (Workspace and Gateway Services).
				 
			
		
		
			
				
					Citrix Cloud Connector
				 
			
			
				
					Windows Server 2022
				 
			
			
				
					2
				 
			
			
				
					D4s_vX
				 

				
					(4vCPU x 16 GiB RAM)
				 
			
			
				
					128 GiB
				 
			
			
				
					Required for all deployments to broker communication to Citrix Cloud.
				 
			
		
		
			
				
					Microsoft Remote Desktop License Server
				 
			
			
				
					Windows Server 2022
				 
			
			
				
					1
				 
			
			
				
					D2s_vX
				 

				
					(2vCPU x 8 GiB RAM)
				 
			
			
				
					64 GiB
				 
			
			
				
					Required when deploying Remote Desktop Services (RDS) based workloads. Two servers are recommended for high availability in production deployments.
				 
			
		
		
			
				
					Federated Authentication Service (FAS)
				 
			
			
				
					Windows Server 2022
				 
			
			
				
					2
				 
			
			
				
					D4s_vX
				 

				
					(4vCPU x 16 GiB RAM)
				 
			
			
				
					128 GiB
				 
			
			
				
					Optional. Enables single sign-on into VDAs when using SAML-based authentication.  Dependent on authentication method and desired user experience.
				 
			
		
		
			
				
					Certificate Authority
				 
			
			
				
					Windows Server 2022
				 
			
			
				
					2
				 
			
			
				
					D4s_vX
				 

				
					(4vCPU x 16 GiB RAM)
				 
			
			
				
					128 GiB
				 
			
			
				
					Optional. Component used in conjunction with FAS.
				 
			
		
		
			
				
					Server OS Master Image
				 
			
			
				
					Windows Server 2022
				 
			
			
				
					1
				 
			
			
				
					D8s_vX
				 

				
					(8 vCPU x 32 GiB RAM)
				 
			
			
				
					128 GiB
				 
			
			
				
					The sizing of the VDAs is customer-dependent and can be changed during the Machine Creation Services (MCS) provisioning process. Enterprises must decide which Citrix delivery method is most appropriate for their user base (Server OS vs. Desktop OS, Single Session vs. Multi-Session, etc.). 
				 
			
		
		
			
				
					Desktop OS Master Image
				 
			
			
				
					Windows 11
				 
			
			
				
					1
				 
			
			
				
					D4s_vX
				 

				
					(4 vCPU x 16 GiB RAM)
				 
			
			
				
					128 GiB
				 
			
		
		
			
				
					NetScaler VPX 1000 Appliances
				 
			
			
				
					NS Firmware 14.x
				 
			
			
				
					2
				 
			
			
				
					Ds3_vX
				 

				
					(4 vCPU x 14 GiB RAM)
				 
			
			
				
					TBD
				 
			
			
				
					Optional. Dependent on on-premises access tier versus Citrix Cloud access tier (Workspace and Gateway Services).
				 
			
		
	



	 
	Azure Availability 



	VMs should be placed across multiple Availability Zones in Azure to ensure the high availability of infrastructure components. Availability Zones place VMs across separated groups of data centers within a region. If Availability Zones are unavailable in the chosen Azure region, Availability Sets may be used instead. 
 


	
		Note:
	 

	
		Availability Zones have a higher SLA than Availability Sets.
	 



	 
 


	Storage



	For enterprises leveraging user profiles for their workloads (file-based or container-based), Citrix recommends deploying an Azure storage solution to locate the profiles close to the Azure workloads for optimal performance. Options include using PaaS offerings like Azure Files or Azure NetApp Files or deploying and maintaining a customer-managed storage solution. Citrix always recommends highly available storage solutions for critical environment components. See the following references for profile storage solutioning guidance:
 


	
		Citrix Tech Zone: Citrix Profile Management with Azure Files
	
	
		Citrix Tech Zone: User Data and Profile Considerations
	
	
		Citrix Tech Zone: Deployment Guide - Deploying Azure Files for Citrix Profile Management and Citrix User personalization layers
	
	
		Citrix Tech Zone: Deployment Guide - Citrix Profile Management Containers
	
	
		Citrix Blog: Making your Citrix Profile Management share highly available
	



	Active Directory



	Citrix requires Active Directory for most Citrix on Azure deployments. This includes machine and user identity management, authentication, and authorization. See Citrix eDocs: Machine Identities - Active Directory Joined for details.
 


	The exception would be enterprises that have deployed Citrix Cloud services (e.g., Workspace and Gateway Services) exclusively with Azure Entra ID-joined or non-domain-joined workloads. In these cases, Cloud Connector servers would not be required. Citrix recommends developing a holistic strategy for using Active Directory and Azure Entra ID across the organization.
 


	With Active Directory in use, the following prerequisites must be in place to properly configure the environment.
 


	
		
			
				
					Prerequisite
				 
			
			
				
					Requirement
				 
			
		
	
	
		
			
				
					Domain Controller Functional Level
				 
			
			
				
					Domain Controller (2x) with Windows Server 2008 R2 forest and domain level or higher.
				 
			
		
		
			
				
					Dedicated Citrix Organizational Unit (OU) for New Environment
				 
			
			
				
					Citrix recommends a dedicated OU structure for the new infrastructure and workloads. The following provides a sample OU structure:
				 

				
					
						Citrix Production (Inheritance Blocked)

						
							
								Infrastructure (Cloud Connectors, StoreFront, FAS, Servers)
							
							
								&lt;Azure Region&gt;
								
									
										Maintenance (Golden Images)
									
									
										Desktop OS VDAs
									
									
										Server OS VDAs
									
								
							
						
					
				
			
		
		
			
				
					Access to the domain Policy Definitions folder 
				 
			
			
				
					Upon installation, .admx and .adml files will be placed on the FAS servers. They must then be uploaded to the domain Policy Definitions folder.
				 

				
					New GPOs will then be created using the settings in these files and linked to FAS, StoreFront, and VDA components in each Azure region.
				 
			
		
	



	Entra ID Joined VDA Considerations



	If Citrix VDAs are either Pure Entra ID (previously Azure AD) or Entra ID Hybrid-joined, enterprises should be aware of the requirements and limitations of these deployment methodologies. For details, see Citrix eDocs: Pure Entra ID-Joined VDAs and Citrix eDocs: Entra ID Hybrid-Joined VDAs.
 


	User and Service Accounts



	The following user accounts and service accounts are required:
 


	
		Azure Service Principal: An Azure Service Principal is required for Machine Creation Services (MCS) provisioning and power management of VDAs. The Service Principal can either be a Subscription Scope or Narrow Scope. Citrix only recommends Subscription Scope for Azure Subscriptions dedicated to Citrix. To align with the principle of least privilege, Citrix recommends the Narrow Scope. See Citrix Support: Azure Role Based Access Control in Citrix Virtual Apps and Desktops and Citrix eDocs: Connection to Microsoft Azure for details. 
	
	
		Citrix Administrators Group: Members of this group will have full administrative rights to all Citrix virtual machines and be defined as Full Administrators in Citrix Cloud Web Studio. The following permissions are required on the Citrix OU:
		
			
				Create sub-OUs
			
			
				Create, edit, and link GPOs
			
			
				Create, delete, and reset passwords for Computer Accounts in the Citrix VDAs OU(s)
			
		
	



	DNS Records



	If NetScaler appliances will be used to front-end the Citrix environment, the following DNS records will be required:
 


	
		External DNS record pointed to the NetScaler public IP for the Gateway.
	
	
		Internal DNS records for each desired load balancing VIP (StoreFront, Cloud Connectors).
	



	Certificate Services



	Depending on the architecture of the Citrix on Azure solution (on-premises vs. Citrix Cloud access tier, inclusion of Citrix FAS), the following certificates will be required for the Citrix environment:
 


	
		NetScaler: External certificate for the NetScaler Gateway from a public CA
	
	
		StoreFront: Internal SAN certificate for StoreFront load balancing VIP and server names
	
	
		Cloud Connector: Internal SAN certificate for Cloud Connector load balancing VIP and server names (on-premises access tier only)
	
	
		Microsoft Certificate Authority: If Citrix FAS is used, two Intermediate CAs per Azure region are recommended for high availability
	
	
		Domain Controller: If Citrix FAS will be used, Domain Controllers must have a Domain Controller Authentication certificate bound to support Smart Card logon
	



	Software 



	The links below provide access to all Citrix-relevant software needed for the deployment. The software must be stored on a file share accessible by the virtual machines listed in the Virtual Machines section of this document.
 


	
		Citrix Cloud Connector: Downloaded from the Citrix Cloud portal
	
	
		Citrix StoreFront and Virtual Delivery Agent: https://www.citrix.com/downloads/citrix-virtual-apps-and-desktops/ 
		
			
				Enterprises can choose the Long Term Service Release (LTSR) or Current Release (CR) version of the Citrix solution. The LTSR version provides a longer support lifecycle without new features, only Cumulative Update hotfixes. The CR version has a more frequent release cycle with new features and a shorter support period. See Citrix FAQ: Virtual Apps and Desktop LTSR for details.
			
		
	
	
		Citrix Workspace App: https://www.citrix.com/downloads/workspace-app/ 
		
			
				The Citrix Workspace App client is recommended for all endpoints accessing Citrix on Azure workloads. The full client provides additional features and functionality beyond the HTML5 client. You can choose the Long Term Service Release (LTSR) or Current Release (CR) version of the Citrix Workspace App. Users may download the Citrix Workspace App from the Citrix Downloads page or have their organization push it down.
			
		
	



	Antivirus Exclusions



	Citrix recommends that you evaluate and implement the Citrix antivirus exclusions detailed in Citrix Tech Zone: Endpoint Security, Antivirus, and Antimalware Best Practices. The recommendations in Microsoft eDocs: Windows Defender in VDI Environments should be evaluated and implemented if this technology is used.
 


	It is essential to understand that antivirus exclusions and optimizations increase the attack surface of a system and might expose computers to various security threats. The guidelines in the article typically represent the best trade-off between security and performance. Citrix does not recommend implementing these exclusions or optimizations until rigorous testing has been conducted in a lab environment to understand the tradeoffs between security and performance thoroughly. Citrix also recommends that organizations engage their antivirus and security teams to review the following guidelines before proceeding with any production deployment.]]></description><pubDate>Thu, 12 Sep 2024 18:09:00 +0000</pubDate></item><item><title>Deploy FlexApp One Applications with Citrix DaaS</title><link>https://community.stage.citrix.com/tech-zone/learn/poc-guides/citrix-daas-flexapp-one/</link><description><![CDATA[Overview



	FlexApp is an application layering solution developed by Liquidware that attaches any application to a Windows session without modifying the underlying base image via the traditional application installation process.
 


	FlexApp One applications are encapsulated in a single, shareable file, allowing them to operate without requiring an additional application player on Windows sessions. Users can initiate the application by clicking on the container file, which quickly integrates the application(s) into their Windows workspace. This solution is compatible with all Windows session types and supports various Windows application formats, including EXE, MSI, and MSIX.
 


	Citrix DaaS can deliver FlexApp applications to end users through the Citrix Personalization component and FlexApp delivery agent from single or multi-session Citrix Virtual Delivery Agents (VDA).
 


	This proof of concept guide provides the steps to deliver FlexApp applications via Citrix DaaS. The following is covered in this guide:
 


	
		Installation of the Citrix Personalization component
	
	
		Installation of the FlexApp service
	
	
		Citrix DaaS FlexApp package discovery
	
	
		Add FlexApp applications to a Delivery Group
	
	
		Connect via Citrix Gateway Service to launch a FlexApp application
	



	Prerequisites



	
		An existing Citrix DaaS subscription
	
	
		Windows Server 2022 Image
		
			
				Windows Server 2019 and Windows 10/11 single session are also supported.
			
		
	
	
		Citrix VDA 2402 ISO downloaded
	
	
		SMB network or Azure File Share
		
			
				The Citrix VDA must have read permission to the storage path. For more information on the permissions required for Azure File Share, visit our product documentation.
			
		
	
	
		FlexApp One application(s)
		
			
				Pre-packaged FlexApp applications can be downloaded from Liquidware for testing purposes.
			
		
	
	
		Citrix Delivery Group
		
			
				The Citrix Delivery Group must have Citrix VDAs with VDA version 2311 or later installed.
			
			
				The Citrix Personalization component must be installed on the VDAs within the Delivery Group.
			
		
	



	Prepare Citrix Virtual Delivery Agent



	The Citrix Personalization component and the FlexApp agent must be installed on the Citrix VDA to support the delivery of FlexApp applications.
 


	Install Citrix Personalization component



	The Citrix Personalization component manages the publishing process for the FlexApp application package and is not installed by default during the VDA installation process. We will use an existing VDA for our deployment and add the component. The component can be installed during the initial VDA install as well.
 


	
		Connect to your Windows Server VDA.
	
	
		Mount your Citrix Virtual Apps and Desktops ISO
	



	
 


	
		Open Apps &amp; Features, select Citrix Virtual Apps and Desktops 7 2402 LTSR - Virtual Delivery Agent, and click Modify.
	



	
 


	
		Click Yes to allow the app to make changes if prompted.
	



	
 


	
		Select Add components and prerequisites.
	



	
 


	
		Click Next on the Core Components window.
	



	
 


	
		Select Citrix Personalization for App-V - VDA and click Next.
	



	
 


	
		Click Next on the Delivery Controller Window.
	



	
 


	
		Click Next on the Features window.
	



	
 


	
		Click Next on the Firewall windows.
	



	
 


	
		Review the Summary page and click Install.
	



	
 


	
		The Citrix App Personalization component installs.
	



	
 


	
		The install completes. Click Next.
	



	
 


	
		Click Finish.
	



	
 


	You have successfully installed the Citrix App Personalization component.
 


	Install FlexApp Service



	The FlexApp One service is required on the Citrix VDA to deliver FlexApp One applications. If you have a FlexApp environment, install the agent and FlexApp applications on your VDA. 
 


	For our POC deployment, we will use the FlexApp One pre-packaged test applications. To activate the FlexApp service (LwlContainerService), we must open one of the FlexApp test applications to install and start the service on your Citrix VDA automatically.
 


	
		Download and extract 2 or 3 of the pre-packaged FlexApp One applications to your file share.
	



	
 


	
		Open your file share and run the executable for one of FlexApp applications.
	



	
 


	
		When prompted, click Yes to install FlexApp onto the Citrix VDA.
	



	
 


	
		Click OK once FlexApp is installed to activate your FlexApp application.
	



	
 


	
		The application launches. Close the application and open Services to validate that the LwlContainerService is running and that the Startup Type is set to Automatic.
	



	
 


	
		Once confirmed, you can exit out of your Citrix VDA.
	



	Upload Applications to Citrix DaaS



	
		Connect to Citrix DaaS web console and select App Packages.
	



	
 


	
		Click Add Source or Package.
	



	
 


	There are two ways to add FlexApp packages to Citrix DaaS: add the file share where all FlexApp One applications are stored or add a single package. For our deployment, we are adding our file share source.
 


	
		Select Add Source.
	



	
 


	
		Provide a name for the App Package source and select the Delivery Group you have created to deliver FlexApp One packages.
	



	
 


	
		Choose Network share under Location type. Provide the UNC path, select FlexApp for package type, and select Yes for subfolder search. Click Add Source.
	



	
 


	
		The Add Source process runs.
	



	
 


	
		Once complete, the Source added will display in the Sources tab within App Packages with Import successful status.
	



	
 


	
		Click Packages.
	



	
 


	
		The FlexApp One packages stored within your file share will be displayed.
	



	
 


	Add FlexApp apps to Delivery Group



	
		Select the first FlexApp package to be added to your Delivery Group and click Assign to delivery groups.
	



	
 


	
		Select the applications to assign and click Next.
	



	
 


	
		Select your Delivery Group and click Next.
	



	
 


	
		Review the summary page and click Finish.
	



	
 


	
		Repeat the process for any further FlexApp applications assigned to the Delivery Group.
	



	Validate and Launch FlexApp Application



	
		Open your Citrix Workspace app or browse your Citrix Workspace.
	
	
		Validate that the FlexApp applications added to your Delivery Group are present.
	



	
 


	
		Launch the FlexApp application.
	



	
 


	
		Your FlexApp application launches successfully.
	



	
 


	
		Validate the application launch within Citrix DaaS web console.
	



	
 


	Summary



	This guide walked you through the configuration of deploying FlexApp One application packages to Citrix DaaS.  This consisted of installing the Citrix Personalization components, installing the FlexApp service, Citrix DaaS FlexApp package discovery, adding FlexApp applications to a Delivery Group, and launching a FlexApp application via Citrix Workspace. For more information on deploying FlexApp applications, visit the Citrix App Packages product documentation.]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_09/image.png.ed71e46f266603c5bc04ddab2d7c63bf.png" length="139046" type="image/png"/><pubDate>Tue, 10 Sep 2024 11:47:00 +0000</pubDate></item><item><title>Deployment Guide: Citrix Secure Private Access On-Premises</title><link>https://community.stage.citrix.com/tech-zone/build/deployment-guides/secure-private-access-on-premises/</link><description><![CDATA[Audience



	This document is intended for architects, network designers, technical professionals, partners, and consultants interested in implementing the Citrix Secure Private Access On-Premises solution. It is also designed for network administrators, Citrix administrators, managed service providers, or anyone looking to deploy this solution.
 


	Solution Overview



	Citrix Secure Private Access On-Premises is a customer-managed Zero Trust Network Access (ZTNA) solution that provides VPN less access to Internal web and SaaS applications with least privilege principle, single sign-on (SSO), Multifactor Authentication and Device posture assessment, application-level security controls and app protection features along with a seamless end-user experience. The solution uses the StoreFront on-premises and Citrix Workspace app to enable a seamless and secure access experience to web and SaaS apps within Citrix Enterprise Browser. This solution also uses the NetScaler Gateway to enforce authentication and authorization controls.
 


	Citrix Secure Private Access On-Premises solution enhances an organization’s overall security and compliance posture by easily delivering Zero Trust access to browser-based (internal web apps and SaaS apps) apps using the StoreFront on-premises portal, which is a unified access portal to web and SaaS apps, along with virtual apps and desktops, as an integrated part of Citrix Workspace.
 


	Citrix Secure Private Access combines NetScaler Gateway and StoreFront elements to deliver an integrated experience for end users and administrators.
 


	
		
			
				
					Functionality
				 
			
			
				
					Service/Component providing the functionality
				 
			
		
	
	
		
			
				
					Consistent UI to access apps
				 
			
			
				
					StoreFront on-premises/Citrix Workspace app
				 
			
		
		
			
				
					SSO to SaaS and Web apps
				 
			
			
				
					NetScaler Gateway
				 
			
		
		
			
				
					Multifactor Authentication (MFA) and device posture (aka End-Point Analysis)
				 
			
			
				
					NetScaler Gateway
				 
			
		
		
			
				
					Security controls and App protection controls for web and SaaS apps
				 
			
			
				
					Citrix Enterprise Browser
				 
			
		
		
			
				
					Authorization policies
				 
			
			
				
					Secure Private Access
				 
			
		
		
			
				
					Configuration and Management
				 
			
			
				
					Citrix Secure Private Access UI, NetScaler UI
				 
			
		
		
			
				
					Visibility, Monitoring, and Troubleshooting
				 
			
			
				
					Citrix Secure Private Access UI and Citrix Director
				 
			
		
	



	Use Cases



	Citrix Secure Private Access (SPA) On-Premises solution with Citrix Virtual Apps and Desktops On-Premises provides a unified and secure end-user experience to virtualized and browser-based apps (web apps and SaaS apps) with consistent security.
 


	SPA On-Premises solution is designed to address the following use cases via a customer-managed solution.
 


	Use case #1: Secure access for Employees and Contractors to internal web and SaaS apps from managed or unmanaged devices without publishing a browser or using a VPN.
 


	Use case #2: Provide comprehensive last-mile Zero Trust enforcement with admin-configurable browser security controls for internal web and SaaS apps from managed or unmanaged devices without publishing a browser or using a VPN.
 


	Use case #3: Accelerate Mergers and Acquisitions (M&amp;A) user access across multiple identity providers, ensure consistent security, and provide seamless end-user access across multiple user groups.
 


	System Requirements



	This article guides you through deploying Secure Private Access with StoreFront and NetScaler Gateway. Citrix Enterprise Browser (included in the Citrix Workspace app) is the client software used to interact securely with your SaaS or internal web apps. 
	Global App Config Service (GACS) is a requirement for browser management of Citrix Enterprise Browser.
 


	
		Note: 
	 

	
		This article does not include guidance on deploying Citrix Virtual Apps and Desktops.
	 



	This guide assumes that the reader has a basic understanding of the following Citrix and NetScaler offerings and general Windows administrative experience:
 


	 
 


	
		Citrix Workspace app
	



	
		Citrix Secure Access client
	



	 
 


	
		StoreFront
	
	
		NetScaler Gateway
	
	
		Global App Configuration service
	
	
		Windows Server
	
	
		SQL Express or Server
	



	Product communication matrix Secure Private Access for on-premises (Secure Private Access plug-in)
 


	Versions:
 


	
		Citrix Workspace app

		
			
				Windows – 2403 and above
			
			
				macOS – 2402 and above
			
		
	



	 
 


	
		Citrix Secure Access client (support for TCP/UDP apps)
	



	
		
			
				Windows – 24.6.1.17 and later
			
		
	



	
		
			
				macOS – 24.06.2 and later
			
		
	



	
		StoreFront – LTSR 2203 or CR 2212 and later     
	



	
		Director 2402 or later
	



	
		NetScaler Gateway – 13.0 and above 
		We recommend using the latest build of NetScaler 13.1 or 14.1 for optimized performance.
	



	
		
			
				NetScaler Gateway – 14.1–25.56 and later is required for TCP/UDP app support
			
		
	



	 
 


	
		Windows Server – 2019 and above (.NET 6.x and above runtime must be supported)
	
	
		SQL Express or Server – 2019 and above
	



	
		Note: 
	 

	
		Citrix Secure Private Access On-Premises are not supported on Citrix Workspace app for iOS and Android.
	 



	Refer to the following documentation for more details as needed:
 


	
		Citrix Workspace app

		
			
				Windows
			
			
				macOS
			
		
	
	
		StoreFront
		
			
				System requirements
			
			
				Plan your StoreFront deployment
			
		
	
	
		NetScaler Gateway
		
			
				Before Getting Started
			
			
				Common Citrix Gateway deployments
			
		
	
	
		Global App Configuration service (GACS)
		
			
				Manage Citrix Enterprise Browser through Global App Configuration service
			
			
				Manage single sign-on for Web and SaaS apps through the Global App Configuration service
			
		
	



	Technical Overview



	Access to internal web apps is possible from any location and on any device at any time through NetScaler Gateway with Citrix Enterprise Browser (incl. in Citrix Workspace app) installed. The same applies to SaaS apps, with the difference that the access can be direct or indirect through NetScaler Gateway.
 


	
 


	Citrix Enterprise Browser and Citrix Workspace app connect to NetScaler Gateway using a TLS-encrypted connection. NetScaler Gateway provides zero trust-based access by assessing the user’s device, strong nFactor user authentication, app authorization, and single sign-on (SSO). 
	StoreFront enumerates virtual and non-virtual apps through Citrix Desktop Delivery Controller and Secure Private Access (SPA) plug-in. Citrix Enterprise Browser tunnels internal traffic (for example, https://website.company.local) to NetScaler Gateway to allow access without needing a public-facing DNS entry. SaaS application access can be direct or, for special use cases. indirect through NetScaler Gateway. Citrix Secure Private Access with Citrix Enterprise Browser allows the configuration of additional security controls for web and SaaS apps like Watermarking, copy/paste-, up/download-, and print restrictions. These restrictions are dynamically applied on a per-app basis.
 


	Scenarios



	Citrix Secure Private Access On-Premises can be deployed in any environment with one or more StoreFront servers and NetScaler Gateways. This section describes a few different scenarios that have been successfully implemented and validated.
 


	
		Scenario 1 – Single server deployment 
		Scenario 1 is for testing purposes only and should not be considered in production environments because of less redundancy.
	
	
		Scenario 2 – Scalable deployment 
		Scenario 2 is designed for performance and redundancy. This is a recommended production deployment.
	
	
		Scenario 3 – Geo deployment (Coming Soon) 
		Scenario 3 is for large enterprises with geographical data center redundancy.
	



	Scenario 1 - Simple deployment



	Scenario 1 is a straightforward deployment that uses the least infrastructure resources. Because of less redundancy, this scenario is not recommended for use in production.
 


	
		Note: 
	 

	
		We assume that a working Citrix Virtual Apps and Desktops infrastructure is installed and a NetScaler is deployed in a DMZ.
	 



	On-premises infrastructure environment
 


	
		Active Directory
	
	
		NetScaler VPX/MPX (Gateway)
	
	
		Combined StoreFront and SPA plug-in server
	
	
		Webserver containing websites
	
	
		Webserver certificate
	



	[1] 
 


	
		Note: 
	 

	
		This is a simplified architectural overview of scenario 1. For more detailed information on communication, please see Secure Private Access for on-premises (Secure Private Access plug-in).
	 



	Installation (Scenario 1)



	StoreFront



	1. Install a web server certificate on the StoreFront and Secure Private Access machine.
 


	2. Download the Citrix Virtual Apps and Desktops ISO file from Citrix Download Center.
 


	3. Run the ISO installer AutoSelect.exe.
 


	4. Select Start from Virtual Apps and Desktops.
 


	
 


	5. Because we want a combined StoreFront and SPA plug-in server, we first install Citrix StoreFront.
 


	
 


	6. In the Citrix StoreFront installer, accept the license agreement and click Next.
 


	
 


	 
 


	7. In the Review prerequisites page, click Next.
 


	
 


	8. In the Ready to Install page, click Install.
 


	
 


	9. When the installation is successfully finished, click Finish.
 


	
 


	10. Click Yes in the reboot dialog to restart the server.
 


	
 


	Secure Private Access



	1. After the reboot, run the ISO installer again.
 


	2. Now that Citrix StoreFront is installed let’s continue installing Secure Private Access.
 


	
 


	3. Accept the license agreement in the Secure Private Access installer and click Next.
 


	
 


	4. On the Core Components page, click Next.
 


	
 


	5. On the Additional Components page, select Use SQL Express on the same machine and click Next.
 


	
 


	
		Note:
	 

	
		In a production environment, it is recommended to use a dedicated database server.
	 



	6. On the Firewall page, click Next to create local Windows Firewall rules automatically.
 


	
 


	7. On the Summary page, review your installation settings and click Install.
 


	
 


	8. On the Finish Installation page, click Finish.
 


	
 


	
		Note: 
	 

	
		The SPA admin console opens automatically in a browser window. Before we start configuring SPA, we need to configure a StoreFront store.
	 



	Configuration (Scenario 1)



	StoreFront



	1. Open the Internet Information Service (IIS) Manager console and verify that the correct web server certificate is assigned.
 


	2. Open the Citrix StoreFront console and create a new deployment.
 


	3. Enter the base URL and click Next.
 


	
		Note: 
	 

	
		Multiple StoreFront servers are load-balanced in a production environment for redundancy and scalability. Therefore, the base URL will be the FQDN of the load balancer virtual server IP.
	 



	4. On the getting started page, click Next.
 


	5. On the store name and access page, enter a store name, for example, Store, and click Next.
 


	6. On the Delivery Controllers page, enter your Citrix Delivery Controller and click Next.
 


	7. On the Remote Access page, enable Remote Access, select No VPN tunnel, add your NetScaler Gateway appliance, and Next.
 


	8. On the Configure Authentication Methods page, verify that the User name and password and Pass-through from Citrix Gateway are correct, and click Next.
 


	9. On the XenApp Services URL page, click Create.
 


	10. Verify that the store was successfully created on the Summary page and click Finish.
 


	Secure Private Access – Initial configuration wizard



	
		Note: 
	 

	
		Please create a StoreFront store before running the Secure Private Access initial configuration wizard! Configuring Kerberos authentication for the browser you use for the Secure Private Access admin console is recommended. Secure Private Access uses Integrated Windows Authentication (IWA) for admin authentication. If Kerberos authentication isn’t set, the browser will prompt you to enter your credentials when accessing the Secure Private Access admin console. Please refer to our SSO to admin console documentation.
	 



	1. From the Start menu, open Citrix Secure Private Access.
 


	2. Click Continue to start the initial configuration wizard on the SPA admin console page.
 


	
 


	3. On the Step 1 page, select Create a new Secure Private Access site and click Next.
 


	
 


	4. On the Step 2 page, enter your SQL server host and Site name and click Test connection. 
	The resulting database name is a combination of "CitrixAccessSecurity".
 


	
 


	5. Select the type of deployment, Automatically or Manually. In this scenario, select Automatically and click Next.
 


	
 


	
		Note: 
	 

	
		For more information on a manual database setup, follow the instructions documented at Step 2: Configure databases - Manual configuration.
	 



	6. On the Step 3 page, enter the Secure Private Access address, StoreFront Store URL, Public NetScaler Gateway address, the NetScaler Gateway virtual IP address, and callback URL. 
	When all URLs are successfully verified, click Next.
 


	
 


	7. On the Step 4 page, click Save to start the configuration process.
 


	
 


	
		Note: 
	 

	
		Because the SPA plug-in is installed on the StoreFront machine, we do not need to run the StoreFront script manually on the StoreFront server. The setup routine automatically does this.
	 



	8. After the configuration process is completed, click Close.
 


	
 


	Secure Private Access – App creation



	1. In the menu on the left, click Applications.
 


	
 


	2. On the right side, click Add an app
 


	
 


	3. In the Add an app dialog, add the required fields marked with a red star and click Save.
 


	
 


	
		Note: 
	 

	
		For details on application parameters, see Configure applications.
	 



	 
 


	4. In the menu on the left, click Access Policies.
 


	
 


	5. On the right side, click Create policy
 


	
 


	6. In the Create policy dialog, add the required fields marked with a red star and click Save.
 


	
 


	
		Note: 
	 

	
		For details on application access policies, see Configure access policies for the applications.
	 



	 
 


	NetScaler Gateway



	The NetScaler Gateway scripts used with Citrix Secure Private Access are reviewed and updated frequently. For the latest scripts and processes to create or update the NetScaler Gateway configuration, please follow the instructions on the NetScaler gateway configuration for Web/SaaS applications product documentation site.
 


	                                                                           
 


	Support for smart access tag



	NetScaler Gateway sends the smart access tags automatically starting with the following versions. This enhancement removes the required gateway callback from the SPA plug-in and adds it to NetScaler Gateway.
 


	
		13.1 - 48.47 and later
	
	
		14.1 - 4.42 and later
	



	The above script automatically enables the enhancement flags ns_vpn_enable_spa_onprem and ns_vpn_disable_spa_onprem.
 


	To make the changes persistent, please follow the instructions on Support for smart access tags     .
 


	 
	               
 


	1. A new NetScaler command script (the default is /var/tmp/ns_gateway_secure_access) is generated.
 


	
 


	2. Switch back to the NetScaler CLI using the command exit.
 


	3. Before executing the new NetScaler command script, let us verify the current NetScaler Gateway configuration and update it for Secure Private Access on-premises.
 


	4. On the Gateway virtual server, verify the following: *ICA only is set to false (OFF)
 


	
		TCP Profile is set to nstcp_default_XA_XD_profile
	
	
		Deployment Type is set to ICA_STOREFRONT
	



	
 


	
		On the Gateway session action for the Workspace app, verify the following: *transparentInterception is set to OFF

		
			
				SSO is set to ON *ssoCredential is set to PRIMARY
			
			
				useMIP is set to NS *useIIP is set to OFF
			
			
				icaProxy is set to OFF *wihome is set to "https://xa04-spa.training.local/Citrix/StoreWeb" - replace with real store URL
			
			
				ClientChoices is set to OFF *ntDomain is set to "training.local" - used for SSO
			
			
				defaultAuthorizationAction is set to ALLOW *authorizationGroup is set to SecureAccessGroup (Make sure that this group is created in NetScaler, not Active Directory. It’s used to bind Secure Private Access specific authorization policies)
			
			
				clientlessVpnMode is set to ON *clientlessModeUrlEncoding is set to TRANSPARENT
			
			
				SecureBrowse is set to ENABLED *Storefronturl is set to "https://xa04-spa.training.local" - replace with StoreFront FQDN
			
			
				sfGatewayAuthType is set to domain
			
		
	



	
		Note: 
	 

	
		For details on session action parameters, see the Command line reference for vpn-sessionAction.
	 



	Based on the above example, the default session action before adding SPA looks like:
 


	add vpn sessionAction AC_OS_172.16.1.106 -transparentInterception OFF -defaultAuthorizationAction ALLOW -SSO ON -ssoCredential PRIMARY -icaProxy ON -wihome "https://xa04-spa.training.local/Citrix/StoreWeb" -ClientChoices OFF -ntDomain training.local -clientlessVpnMode OFF -storefronturl "https://xa04-spa.training.local" -sfGatewayAuthType domain
 


	Let’s create the authorization group and a new session action and modify it for Secure Private Access on-premises:
 


	add aaa group SecureAccessGroup add vpn sessionAction AC_OS_172.16.1.106_SPAOP -transparentInterception OFF -defaultAuthorizationAction ALLOW -authorizationGroup SecureAccessGroup -SSO ON -ssoCredential PRIMARY -useMIP NS -useIIP OFF -icaProxy OFF -wihome "https://xa04-spa.training.local/Citrix/StoreWeb" -ClientChoices OFF -ntDomain training.local -clientlessVpnMode ON -clientlessModeUrlEncoding TRANSPARENT -SecureBrowse ENABLED -storefronturl "https://xa04-spa.training.local" -sfGatewayAuthType domain
 


	Switch the session policy for the Workspace app to the new session action:
 


	set vpn sessionPolicy PL_OS_172.16.1.106 -action AC_OS_172.16.1.106_SPAOP
 


	1. Run the new NetScaler commands script with the batch command.
 


	batch -fileName /var/tmp/ns_gateway_secure_access_update -outfile /var/tmp/ns_gateway_secure_access_update_output.log -ntimes 1.
 


	2. Verify the log file that there is no error For example:
 


	shell cat /var/tmp/ns_gateway_secure_access_update_output.log
 


	
		Note: 
	 

	
		In this scenario, one error is shown in the log file because StoreFront and SPA plug-in are installed on the same machine. ERROR: Specified pattern or range is already bound to dataset/patset
	 



	3. On the StoreFront and SPA plug-in machine, open Citrix Secure Private Access from the Start menu.
 


	4. On the SPA admin console page, click Mark as done in the Configure Gateway section.
 


	
 


	Scenario 2 – Scalable deployment



	In Scenario 2, the NetScaler Gateway, StoreFront, SPA plug-in, and SQL server are deployed in Microsoft Azure, whereas all other services are deployed on-premises.
 


	
		Note: 
	 

	
		NetScaler Gateway, StoreFront, SPA plug-in, and SQL server can also be deployed in the local data center. This scenario should only showcase that deploying in any cloud is possible too. We assume that a working Citrix Virtual Apps and Desktops infrastructure is installed and a NetScaler is deployed in Azure.
	 



	Cloud Infrastructure environment



	
		Azure Load Balancer for NetScaler with static public IP
	
	
		2x NetScaler VPX (Gateway) on Azure
	
	
		2x StoreFront server
	
	
		2x SPA plug-in server
	
	
		1x Database server
	
	
		2x Active Directory server
	
	
		Webserver containing websites
	
	
		Webserver certificates
	



	
 


	
		Note: 
	 

	
		This is a simplified architectural overview of scenario 2. For more detailed communication information, see Secure Private Access for on-premises (Secure Private Access plug-in).
	 



	Installation (Scenario 2)



	StoreFront



	1.On the StoreFront machine, install a web server certificate containing the load balancing FQDN and StoreFront server FQDNs. 
	For more information about certificates, have a look at StoreFront certificate requirements.
 


	2. Download the Citrix Virtual Apps and Desktops ISO file from Citrix Download Center.
 


	3. Run the ISO installer AutoSelect.exe.
 


	4. Select Start from Virtual Apps and Desktops.
 


	
 


	5. Because we want to have a seperated StoreFront and SPA plug-in server, we first install Citrix StoreFront.
 


	
 


	6. In the Citrix StoreFront installer, accept the license agreement and click Next.
 


	
 


	7. In the Review prerequisites page, click Next.
 


	
 


	8. In the Ready to install page, click Install.
 


	
 


	9. When the installation is successfully finished, click Finish.
 


	
 


	10. Click Yes in the reboot dialog to restart the server.
 


	
 


	11. For redundancy, install a second StoreFront server following the same steps.
 


	Secure Private Access



	1. Install a web server certificate matching the load balancer FQDN name on the Secure Private Access machine. The same certificate must be installed on the other SPA plug-in nodes. 
	If the load balancing protocol used is SSL, the same certificate must be used on the load balancer.
 


	2. Mount the downloaded Citrix Virtual Apps and Desktops ISO file and run the installer AutoSelect.exe.
 


	3. Select Start from Virtual Apps and Desktops.
 


	4. Click Secure Private Access to start the installation.
 


	
 


	5. Accept the license agreement in the Secure Private Access installer and click Next.
 


	
 


	6. On the Core Components page, click Next.
 


	
 


	7. On the Additional Components page, deselect Use SQL Express on the same machine and click Next.
 


	
 


	
		Note: 
	 

	
		A dedicated database server is recommended for production deployment.
	 



	8. On the Firewall page, click Next to automatically create local Windows Firewall rules.
 


	
 


	9. On the Summary page, review your installation settings and click Install.
 


	
 


	10. On the Finish Installation page, click Finish.
 


	
 


	11. For redundancy, install a second SPA plug-in server following the same steps.
 


	
		Note: 
	 

	
		The SPA admin console opens automatically in a browser window. Before we start configuring SPA, we need to configure a StoreFront store.
	 



	Configuration (Scenario 2)



	StoreFront



	1. Open the Internet Information Service (IIS) Manager console and verify that the correct web server certificate is assigned.
 


	2. Open the Citrix StoreFront console and create a new deployment.
 


	3. Enter the base URL using the load balancer FQDN and click Next. 
	For example, https://stf-lb.training.local/.
 


	
		Note: 
	 

	
		The load balancing configuration is completed later.
	 



	4. On the getting started page, click Next.
 


	5. On the store name and access page, enter a store name, for example, StoreLB, and click Next.
 


	6. On the Delivery Controllers page, enter your Citrix Delivery Controller and click Next.
 


	7. On the Remote Access page, enable Remote Access, select No VPN tunnel, add your NetScaler Gateway appliance, and Next.
 


	8. On the Configure Authentication Methods page, verify that User name and password and Pass-through from Citrix Gateway, and click Next.
 


	9. On the XenApp Services URL page, click Create.
 


	10. On the Summary page, verify that the store was successfully created and click Finish.
 


	11. Open Windows PowerShell to update the StoreFront monitoring service URL and run the following commands:
 


	$ServiceUrl = "https://localhost:443/StorefrontMonitor"
 


	    Set-STFServiceMonitor -ServiceUrl $ServiceUrl
 


	    Get-STFServiceMonitor
 


	   Default StoreFront monitoring service URL
 


	If you want to revert the service URL change, run the above commands again with a changed $ServiceUrl = "http://localhost:8000/StorefrontMonitor".
 


	1. Verify that the Receiver for Web Sites loopback communication is set to On.
 


	Get-STFWebReceiverService -VirtualPath "/Citrix/StoreLBWeb" | Get-STFWebReceiverCommunication | Format-Table Loopback
 


	    
 


	    Loopback
 


	    --------
 


	    On
 


	    
 


	2. Join the second StoreFront server in the server group. 
	Please follow the documented instructions for Join an existing server group.
 


	Secure Private Access – Initial configuration wizard



	
		Important:
	 

	
		Please create a StoreFront store before running the Secure Private Access initial configuration wizard.
	 



	Information



	It is recommended that you configure Kerberos authentication for the browser you use for the Secure Private Access admin console. Secure Private Access uses Integrated Windows Authentication (IWA) for its admin authentication. 
	If Kerberos authentication isn’t set, the browser prompts you to enter your credentials when accessing the Secure Private Access admin console. For more information, refer to our SSO to admin console documentation.
 


	1. From the Start menu, open Citrix Secure Private Access.
 


	
		Important
	 

	
		Verify the web server certificate protecting the SPA admin console within the web browser. The certificate must be uploaded before the Secure Private Access installation.
	 



	2. Click Continue to start the initial configuration wizard on the SPA admin console page.
 


	
 


	3. On the Step 1 page, select Create a new Secure Private Access site and click Next.
 


	
 


	4. On the Step 2 page, enter your SQL server host and Site name and click Test connection. 
	The resulting database name is a combination of "CitrixAccessSecurity".
 


	
 


	5. Select the type of deployment, Automatically or Manually. In this scenario, select Manually and click Download script.
 


	
 


	
		Note: 
	 

	
		The displayed error is expected because the database does not exist.
	 



	Secure Private Access – manual database setup



	1. Open the SQL Server Management Studio and connect to the database engine using a database administrator account.
 


	2. In the SQL Server Management Studio, click File, select Open and select File.
 


	3. In the Open File dialog, search for the downloaded SQL script and click Open.
 


	4. Verify the script content and click Execute. The script creates the database and a login for the Windows server training\xa05-spa.
 


	5. Switch back to the SPA admin console and click Test connection. 
	The connection is now successful and the server has write permissions to the database.
 


	6. Click Next.
 


	
 


	7. On the Step 3 page, enter the Secure Private Access address, StoreFront Store URL, Public NetScaler Gateway address, the NetScaler Gateway virtual IP address, and callback URL. 
	When all URLs are successfully verified, click Next.
 


	
 


	8. On the Step 4 page, click Save to start the configuration process.
 


	
 


	
		Note: 
	 

	
		Because StoreFront is installed on a different server, the SPA plug-in PowerShell script must manually be executed on the StoreFront server. The StoreFront server group replication mechanism propagates the changes to all members.
	 



	9. After the configuration process is completed, click Close.
 


	
 


	10. Join the second SPA plug-in server to the cluster. Open another browser and open the second SPA plug-in admin console and click Continue.
 


	
 


	11. On the Step 1 page, select Join an existing SC Secure Private Access site and click Next.
 


	
 


	12. On the Step 2 page, enter your SQL server host and Site name, click Test connection, select Manually and click Download script.
 


	
 


	Secure Private Access – manual database setup



	1. Open the SQL Server Management Studio and connect to the database engine using a database administrator account.
 


	2. In the SQL Server Management Studio, click File, select Open and select File.
 


	3. In the Open File dialog, search for the downloaded SQL script and click Open.
 


	4. Verify the script content and click Execute. The script verifies that the database exits and creates the login for the Windows server training\xa04-spa.
 


	5. Switch back to the SPA admin console and click Next. 
	6. The server now has write permissions to the database. Click Next.
 


	
 


	6. On the Step 4 page, click Save to start the configuration process.
 


	
 


	7. After the configuration process is completed, click Close.
 


	
 


	8. The SPA plug-in cluster can be managed over each node.
 


	Secure Private Access – App creation



	1. In the menu on the left, click Applications.
 


	
 


	2. On the right side, click Add an app
 


	
 


	3. In the Add an app dialog, add the required fields marked with a red star and click Save.
 


	
 


	
		Note: 
	 

	
		For details on application parameters, see Configure applications.
	 



	4. In the menu on the left, click Access Policies.
 


	
 


	5. On the right side, click Create policy
 


	
 


	6. In the Create policy dialog, add the required fields marked with a red star and click Save.
 


	
 


	
		Note: 
	 

	
		For details on application access policies, see Configure access policies for the applications.
	 



	Secure Private Access – StoreFront configuration



	1. On the Secure Private Access server, open the Start menu and open Citrix Secure Private Access.
 


	2, In the menu on the left, click Settings.
 


	3, In the menu on the left, click Settings and select the Integrations tab.
 


	4. In the StoreFront Store URL section, click Download script.
 


	
 


	5. Copy the downloaded file StoreFrontScripts.zip to a StoreFront server and exact the files to any folder.
 


	6. Open a Windows x64 bit compatible PowerShell window with admin privilege and run the PowerShell script ConfigureStorefront.ps1. 
	The script modifies the StoreFront store (in this scenario, StoreLB) to support Secure Private Access applications.
 


	NetScaler StoreFront and SPA Plugin Load Balancing



	
		Note: 
	 

	
		The example below does not enable SSL Default Profiles. If your NetScaler configuration does, add the cipher directly to the SSL profile and ignore the virtual server cipher configuration.
	 



	The following servers are used -
 


	
		xa04-stf.training.local
	
	
		xa05-stf.training.local
	
	
		xa04-spa.training.local
	
	
		xa05-spa.training.local
	



	IP addresses
 


	
		172.16.1.107 (StoreFront load balancing VIP)
	
	
		172.16.1.108 (SPA plug-in load balancing VIP)
	



	Certificates
 


	
		dh5-2048.key (Diffie-Hellman key, group 5, 2048 bit)
	
	
		stf-lb.training.local
	
	
		spa-lb.training.local
	



	Create the Diffie-Hellman key and replace the server names, IP addresses, and certificates before running the commands in NetScaler CLI.
 


	
		Connect to NetScaler CLI using an SSH client and run the following commands:
	



	## SSL Profile ##
 


	    ## Do not forget to replace the Diffie-Hellmann key name ##
 


	    add ssl profile SECURE_ssl_profile_frontend -dhCount 1000 -dh ENABLED -dhFile "/nsconfig/ssl/dh5-2048.key" -eRSA ENABLED -eRSACount 1000 -sessReuse ENABLED -sessTimeout 120 -tls1 DISABLED -tls11 DISABLED
 


	    
 


	    ## Monitors ##
 


	    add lb monitor mon-StoreFront STOREFRONT -scriptName nssf.pl -dispatcherIP 127.0.0.1 -dispatcherPort 3013 -LRTM DISABLED -secure YES -storefrontcheckbackendservices YES
 


	    add lb monitor mon-SPA-Plugin HTTP -respCode 200 -httpRequest "GET /secureAccess/health" -LRTM DISABLED -secure YES
 


	    add lb monitor mon-SPA-Admin-console HTTP -respCode 200 -httpRequest "GET /accessSecurity/health" -LRTM DISABLED -secure YES
 


	    
 


	    ## Server ##
 


	    ## Do not forget to replace server names ##
 


	    add server xa04-stf.training.local xa04-stf.training.local
 


	    add server xa05-stf.training.local xa05-stf.training.local
 


	    add server xa04-spa.training.local xa04-spa.training.local
 


	    add server xa05-spa.training.local xa05-spa.training.local
 


	    
 


	    ## Services ##
 


	    ## Do not forget to replace service names ##
 


	    add service xa04-stf.training.local_443 xa04-stf.training.local SSL 443 -gslb NONE -maxClient 0 -maxReq 0 -cip ENABLED X-Forwarded-For -usip NO -useproxyport YES -sp OFF -cltTimeout 180 -svrTimeout 360 -CKA NO -TCPB NO -CMP NO -state DISABLED
 


	    bind service xa04-stf.training.local_443 -monitorName mon-StoreFront
 


	    
 


	    add service xa05-stf.training.local_443 xa05-stf.training.local SSL 443 -gslb NONE -maxClient 0 -maxReq 0 -cip ENABLED X-Forwarded-For -usip NO -useproxyport YES -sp OFF -cltTimeout 180 -svrTimeout 360 -CKA NO -TCPB NO -CMP NO
 


	    bind service xa05-stf.training.local_443 -monitorName mon-StoreFront
 


	    
 


	    add service xa04-spa.training.local_443 xa04-spa.training.local SSL 443 -gslb NONE -maxClient 0 -maxReq 0 -cip DISABLED -usip NO -useproxyport YES -sp OFF -cltTimeout 180 -svrTimeout 360 -CKA NO -TCPB NO -CMP NO -state DISABLED
 


	    bind service xa04-spa.training.local_443 -monitorName mon-SPA-Plugin
 


	    
 


	    add service xa05-spa.training.local_443 xa05-spa.training.local SSL 443 -gslb NONE -maxClient 0 -maxReq 0 -cip DISABLED -usip NO -useproxyport YES -sp OFF -cltTimeout 180 -svrTimeout 360 -CKA NO -TCPB NO -CMP NO
 


	    bind service xa05-spa.training.local_443 -monitorName mon-SPA-Plugin
 


	    
 


	    add service xa04-spa.training.local_4443 xa04-spa.training.local SSL 4443 -gslb NONE -maxClient 0 -maxReq 0 -cip DISABLED -usip NO -useproxyport YES -sp OFF -cltTimeout 180 -svrTimeout 360 -CKA NO -TCPB NO -CMP NO -state DISABLED
 


	    bind service xa04-spa.training.local_4443 -monitorName mon-SPA-Admin-console
 


	    
 


	    add service xa05-spa.training.local_4443 xa05-spa.training.local SSL 4443 -gslb NONE -maxClient 0 -maxReq 0 -cip DISABLED -usip NO -useproxyport YES -sp OFF -cltTimeout 180 -svrTimeout 360 -CKA NO -TCPB NO -CMP NO
 


	    bind service xa05-spa.training.local_4443 -monitorName mon-SPA-Admin-console
 


	    
 


	    bind service xa04-spa.training.local_443 -monitorName mon-SPA-Plugin
 


	    
 


	    ## LB vServer ##
 


	    ## Do not forget to replace vServer names and IP addresses ##
 


	    add lb vserver lbvs-stf-lb.training.local_443 SSL 172.16.1.107 443 -persistenceType COOKIEINSERT -persistenceBackup SOURCEIP -cookieName STFPersistence -cltTimeout 180
 


	    add lb vserver lbvs-spa-lb.training.local_443 SSL 172.16.1.108 443 -persistenceType NONE -cltTimeout 180
 


	    add lb vserver lbvs-spa-lb.training.local_4443 SSL 172.16.1.108 4443 -persistenceType NONE -cltTimeout 180
 


	    
 


	    ## Do not forget to replace vServer names and service bindings ##
 


	    bind lb vserver lbvs-stf-lb.training.local_443 xa04-stf.training.local_443
 


	    bind lb vserver lbvs-stf-lb.training.local_443 xa05-stf.training.local_443
 


	    bind lb vserver lbvs-spa-lb.training.local_443 xa04-spa.training.local_443
 


	    bind lb vserver lbvs-spa-lb.training.local_443 xa05-spa.training.local_443
 


	    bind lb vserver lbvs-spa-lb.training.local_4443 xa04-spa.training.local_4443
 


	    bind lb vserver lbvs-spa-lb.training.local_4443 xa05-spa.training.local_4443
 


	    
 


	    ## Do not forget to replace vServer names ##
 


	    set ssl vserver lbvs-stf-lb.training.local_443 -sslProfile SECURE_ssl_profile_frontend
 


	    set ssl vserver lbvs-spa-lb.training.local_443 -sslProfile SECURE_ssl_profile_frontend
 


	    set ssl vserver lbvs-spa-lb.training.local_4443 -sslProfile SECURE_ssl_profile_frontend
 


	    
 


	    ## Do not forget to replace vServer names ##
 


	    bind ssl vserver lbvs-stf-lb.training.local_443 -cipherName SECURE
 


	    bind ssl vserver lbvs-spa-lb.training.local_443 -cipherName SECURE
 


	    bind ssl vserver lbvs-spa-lb.training.local_4443 -cipherName SECURE
 


	    
 


	    ## Do not forget to replace vServer names and certificates ##
 


	    bind ssl vserver lbvs-stf-lb.training.local_443 -certkeyName stf-lb.training.local
 


	    bind ssl vserver lbvs-spa-lb.training.local_443 -certkeyName spa-lb.training.local
 


	    bind ssl vserver lbvs-spa-lb.training.local_4443 -certkeyName spa-lb.training.local
 


	 NetScaler Gateway
 


	The NetScaler Gateway scripts used with Citrix Secure Private Access are reviewed and updated frequently. For the latest scripts and processes to create or update the NetScaler Gateway configuration, please follow the instructions on the NetScaler gateway configuration for Web/SaaS applications product documentation site.
 


	Support for smart access tag



	NetScaler Gateway sends the smart access tags automatically, starting with the following versions. This enhancement removes the required gateway callback from the SPA plug-in and adds it to NetScaler Gateway.
 


	
		13.1 - 48.47 and later
	
	
		14.1 - 4.42 and later
	



	The above script automatically enables the enhancement flags ns_vpn_enable_spa_onprem and ns_vpn_disable_spa_onprem.
 


	To make the changes persistent, run the following commands in the NetScaler shell. 
	 
 


	root@xa04-adc01# echo "nsapimgr_wr.sh -ys call=ns_vpn_enable_spa_onprem"&gt;&gt; /nsconfig/rc.netscaler 
	root@xa04-adc01# echo "nsapimgr_wr.sh -ys call=toggle_vpn_enable_securebrowse_client_mode"&gt;&gt; /nsconfig/rc.netscaler
 


	For more details, look at Support for smart access tags
 


	1. A new NetScaler command script (the default is /var/tmp/ns_gateway_secure_access) is generated.
 


	
 


	2. Switch back to the NetScaler CLI using the command exit.
 


	3. Before executing the new NetScaler command script, let us verify the current NetScaler Gateway configuration and update it for Secure Private Access on-premises.
 


	4. On the Gateway virtual server, verify the following: *ICA only is set to false (OFF)
 


	
		TCP Profile is set to nstcp_default_XA_XD_profile
	
	
		Deployment Type is set to ICA_STOREFRONT
	



	
 


	
		On the Gateway session action for the Workspace app, verify the following: *transparentInterception is set to OFF

		
			
				SSO is set to ON *ssoCredential is set to PRIMARY
			
			
				useMIP is set to NS *useIIP is set to OFF
			
			
				icaProxy is set to OFF *wihome is set to "https://stf-lb.training.local/Citrix/StoreLBWeb" - replace with real store URL
			
			
				ClientChoices is set to OFF *ntDomain is set to "training.local" - used for SSO
			
			
				defaultAuthorizationAction is set to ALLOW *authorizationGroup is set to SecureAccessGroup (Make sure that this group is created in NetScaler, not Active Directory. It’s used to bind Secure Private Access specific authorization policies)
			
			
				clientlessVpnMode is set to ON *clientlessModeUrlEncoding is set to TRANSPARENT
			
			
				SecureBrowse is set to ENABLED *Storefronturl is set to "https://stf-lb.training.local" - replace with StoreFront FQDN
			
			
				sfGatewayAuthType is set to domain
			
		
	



	
		Note: 
	 

	
		For details on session action parameters, see the Command line reference for vpn-sessionAction.
	 



	 
 


	Based on the above example, the default session action before adding SPA looks like:
 


	 
	add vpn sessionAction AC_OS_172.16.1.106 -transparentInterception OFF -defaultAuthorizationAction ALLOW -SSO ON -ssoCredential PRIMARY -icaProxy ON -wihome "https://stf-lb.training.local/Citrix/StoreLBWeb" -ClientChoices OFF -ntDomain training.local -clientlessVpnMode OFF -storefronturl "https://stf-lb.training.local" -sfGatewayAuthType domain
 


	Let’s create the authorization group and a new session action and modify it for Secure Private Access on-premises:
 


	 
	add aaa group SecureAccessGroup 
	add vpn sessionAction AC_OS_172.16.1.106_SPAOP -transparentInterception OFF -defaultAuthorizationAction ALLOW -authorizationGroup SecureAccessGroup -SSO ON -ssoCredential PRIMARY -useMIP NS -useIIP OFF -icaProxy OFF -wihome "https://stf-lb.training.local/Citrix/StoreLBWeb" -ClientChoices OFF -ntDomain training.local -clientlessVpnMode ON -clientlessModeUrlEncoding TRANSPARENT -SecureBrowse ENABLED -storefronturl "https://stf-lb.training.local" -sfGatewayAuthType domain
 


	Switch the session policy for the Workspace app to the new session action:
 


	 
	set vpn sessionPolicy PL_OS_172.16.1.106 -action AC_OS_172.16.1.106_SPAOP
 


	5. Run the new NetScaler commands script with the batch command. 
	For example:
 


	 
	batch -fileName /var/tmp/ns_gateway_secure_access_update -outfile /var/tmp/ns_gateway_secure_access_update_output.log -ntimes 1.
 


	6. Verify the log file that there is no error For example:
 


	 
	shell cat /var/tmp/ns_gateway_secure_access_update_output.log
 


	 
 


	
		Note: 
	 

	
		In this scenario, one error is shown in the log file because StoreFront and SPA plug-in are installed on the same machine. ERROR: Specified pattern or range is already bound to dataset/patset
	 



	7. On the StoreFront and SPA plug-in machine, open Citrix Secure Private Access from the Start menu.
 


	8. On the SPA admin console page, click Mark as done in the Configure Gateway section.
 


	
 


	Testing any scenario



	1. Open the Citrix Workspace app and create a new account. 
	In our scenarios, the URL https://citrix.training.com was used.
 


	
 


	2. Log on to NetScaler Gateway.
 


	
 


	3. Secure Private Access apps along with Citrix Virtual Apps and Desktops are displayed. 
	In this scenario, no CVAD app is marked as a favorite. Thus, they are only displayed under APPS.
 


	
 


	4. Launch web app Extranet.
 


	
 


	
		Note:
	 

	
		All security controls are enabled on this application.
	 

	
		
			Restrict clipboard access
		
		
			Restrict printing
		
		
			Restrict downloads
		
		
			Restrict uploads
		
		
			Display Watermark
		
		
			Restrict key logging
		
		
			Restrict screen capture
		
	

	
		The above screenshot shows "Display Watermark". 
		The screenshot below shows "Restrict screen capture".
	 



	



	Summary



	Citrix Secure Private Access for on-premises allows zero trust-based access to SaaS and internal web apps. This deployment guide covered publishing web apps and setting security controls. The result is an integrated solution with single sign-on for users to access SaaS and internal web apps like virtual apps.]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_09/image.png.8e18fda764d991d68621d18c774c85bb.png" length="137286" type="image/png"/><pubDate>Mon, 09 Sep 2024 13:00:00 +0000</pubDate></item><item><title>Deployment Guide: Using Terraform to deploy a Citrix Cloud Resource Location on XenServer 8</title><link>https://community.stage.citrix.com/tech-zone/automation/citrix-terraform-xenserver/</link><description><![CDATA[Using Terraform to deploy a Citrix DaaS Resource Location on XenServer 8 
	 



	Overview



	This guide will showcase the possibility of deploying a Citrix DaaS Resource Location on XenServer 8 using Terraform. We want to reduce manual interventions to the absolute minimum. 
	 
	In the end, you will have created:
 


	
		A new Citrix Cloud Resource Location (RL) running on-premise on XenServer 8
	
	
		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 on XenServer 8
	
	
		A Machine Catalog and a Delivery Group in the just-created Resource Location
	



	 
 


	Installation and Configuration of Terraform



	Please find a description of installing Terraform and initial configurations in our Tech Zone Deployment Guide: Installing Terraform and Configuring the Citrix Terraform Provider.
 


	All Terraform configuration files can be found later on GitHub.
 


	 
	Deployment overview 
	 
	This guide uses an existing domain and will not deploy a new one. For further instructions on deploying a new domain, refer to the guide Citrix DaaS and Terraform - Automatic Deployment of a Resource Location on Microsoft Azure. 
 


	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 primary Domain Controller using IPSec-based Site-to-Site VPNs. Each Resource Location has its subdomain. 
	 
 


	
		Note: 
	 

	
		We did not use loop-constructs or count keywords —each resource is created explicitly—for easier reading and understanding. 
		 
	 



	 
 


	
		Important: 
	 

	
		The Image Template VM must be configured before Terraform uses it to create the Machine Catalog. Please ensure that WinRM is functioning even though the VM is NOT part of a Domain. Instructions are provided later in this guide. 
		The Administrative VM must have Terraform installed - use the Chocolately Guidance provided previously for installation. 
		 
	 



	 
 


	The Terraform flow is split into four different modules:
 


	
		Module One - this part can be run on any computer where Terraform is installed: 
		This module creates all the resources needed for the XenServer cluster.

		
			
				Create two Windows Server 2022-based VMs used as Cloud Connector VMs in Module 2
			
			
				Create a Windows Server 2022-based VM acting as an Administrative workstation for running Terraform Modules 3 and 4. 
				This is necessary because WinRM is used for further configuration and deployment. 
				 
			
		
	
	
		Module Two—This part can be run on any computer where Terraform is installed, but it relies heavily on WinRM. It can only be run after Module One is completed. 
		Due to the very early version of the Terraform provider for XenServer, the following essential steps must be done by calling PowerShell scripts on the three previously created VMs using WinRM:
		
			
				Rename the VM names to meet the AD requirements
			
			
				Set the correct IP addresses
			
			
				Add the 3 VMs to the Domain 
				 
			
		
	
	
		Module Three - this part can only be run after the completion of Module Two: 
		This Module contains the initial Citrix Cloud-related configurations.
		
			
				Installing the required software on the CCs
			
			
				Installing the required 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 
						 
					
				
			
		
	
	
		Module Four - this part can only be run after the completion of Module Three: 
		All Citrix Cloud-related entities are created in this Module.
		
			
				Creating a dedicated Hypervisor Connection to XenServer 8
			
			
				Creating a dedicated Hypervisor Resource Pool
			
			
				Creating a Machine Catalog based on the referenced Master Image
			
			
				Creating a Delivery Group
			
			
				Creating AutoScale settings and applying these to the newly created Delivery Group
			
		
	



	
		Important:
	 

	
		Make sure that all Terraform-related VMs can communicate using WinRM. The deployment fails if the Admin-VM cannot connect to the CCs using WinRM. 
		 
		Various configuration guides for WinRM can be found on the Internet. 
		 We rely on GPOs related to WinRM to configure all Domain Members accordingly. 
		 
	 



	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 td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\TACG&gt; test-wsman -ComputerName &lt;IP-Address of the computer you want to reach&gt; -Credential &lt;IP-Address of the computer you want to reach&gt;\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 &lt;IP-Address of the computer you want to reach&gt; -Credential &lt;IP-Address of the computer you wish to reach&gt;\administrator  
					 

					
						The response should look like:   
						[10.10.111.21]: PS C:\Users\Administrator\Documents&gt;
					 
				
			
		
	

	
		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 #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}		
		
			
				
					
						
							
								locals {
							 

							
								  #### Test the WinRM communication 
								 #### Need to invoke PowerShell as Domain User as the provisioner does not allow it to be run in a Domain Users-context
							 

							
								  TerraformTestWinRMScript     = &lt;&lt;-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 td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;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.10.111.21
						 

						
							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!  
						 

						
							 
						 

						
							#&lt; CLIXML
						 

						
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
						 

						
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;
						 

						
							null_resource.CreateTestScriptOnCC1 (remote-exec): C:\Users\Administrator&gt;powershell -File c:/temp/xdinst/DATA/Terraform-Test-WinRM.ps1
						 

						
							 
						 

						
							#&lt; CLIXML
						 

						
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
						 

						
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;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 that must be set. 
		Some configuration settings are propagated throughout the whole Terraform configuration...
	 

	
		You must start each module 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. 
		 
	 

	
		
			Important:
		 

		
			Each module/step must be completed successfully before the next module can be started. 
			 
		 
	

	
		 
	 

	
		File System structure
	

	
		Root-Directory
	 

	
		Module 1: _CCRLOnXenServer-Creation:
	 

	
		
			
				
					
						Filename                                                                                           
					 
				
				
					
						Purpose                                            
					 
				
			
		
		
			
				
					
						_CCRLOnXenServer-Create.tf
					 
				
				
					
						Resource configuration and primary flow definition
					 
				
			
			
				
					
						_CCRLOnXenServer-Create-variables.tf
					 
				
				
					
						Definition of Variables
					 
				
			
			
				
					
						_CCRLOnXenServer-Create.auto.tfvars.json
					 
				
				
					
						Setting the values of the Variables
					 
				
			
			
				
					
						_CCRLOnXenServer-Provider.tf
					 
				
				
					
						Provider definition and configuration
					 
				
			
			
				
					
						_CCRLOnXenServer-Provider-variables.tf
					 
				
				
					
						Definition of Variables
					 
				
			
			
				
					
						_CCRLOnXenServer-Provider.auto.tfvars.json
					 
				
				
					
						Setting the values of the Variables
					 
				
			
		
	

	
		 
		Module 2: _CCRLOnXenServer- InitialConfiguration:
	 

	
		
			
				
					
						Filename                                                                                             
					 
				
				
					
						Purpose                                          
					 
				
			
		
		
			
				
					
						_CCRLOnXenServer- InitialConfiguration.tf
					 
				
				
					
						Resource configuration and primary flow definition
					 
				
			
			
				
					
						_CCRLOnXenServer- InitialConfiguration-variables.tf
					 
				
				
					
						Definition of Variables
					 
				
			
			
				
					
						_CCRLOnXenServer- InitialConfiguration.auto.tfvars.json
					 
				
				
					
						Setting the values of the Variables
					 
				
			
			
				
					
						_CCRLOnXenServer-Provider.tf
					 
				
				
					
						Provider definition and configuration
					 
				
			
			
				
					
						_CCRLOnXenServer-Provider-variables.tf
					 
				
				
					
						Definition of Variables
					 
				
			
			
				
					
						_CCRLOnXenServer-Provider.auto.tfvars.json
					 
				
				
					
						Setting the values of the Variables
					 
				
			
			
				
					
						DATA Directory
					 
				
				
					
						Contains dynamically created scripts which will be uploaded and executed on the Cloud Connector VMs
					 
				
			
			
				
					
						_WinRM-Test Directory
					 
				
				
					
						Contains Terraform scripts to test the WinRM configuration and communication
					 
				
			
			
				
					
						 
					 
				
				
					
						 
					 
				
			
		
	

	
		Module 3: _CCRLOnXenServer- CCConfiguration:
	 

	
		
			
				
					
						Filename                                                                                             
					 
				
				
					
						Purpose                                          
					 
				
			
		
		
			
				
					
						CCRLOnXenServer-CCConfiguration.tf
					 
				
				
					
						Resource configuration and primary flow definition
					 
				
			
			
				
					
						CCRLOnXenServer-CCConfiguration-variables.tf
					 
				
				
					
						Definition of Variables
					 
				
			
			
				
					
						CCRLOnXenServer- CCConfiguration.auto.tfvars.json
					 
				
				
					
						Setting the values of the Variables
					 
				
			
			
				
					
						_CCRLOnXenServer-Provider.tf
					 
				
				
					
						Provider definition and configuration
					 
				
			
			
				
					
						_CCRLOnXenServer-Provider-variables.tf
					 
				
				
					
						Definition of Variables
					 
				
			
			
				
					
						_CCRLOnXenServer-Provider.auto.tfvars.json
					 
				
				
					
						Setting the values of the Variables
					 
				
			
			
				
					
						DATA Directory
					 
				
				
					
						Contains dynamically created scripts which will be uploaded and executed on the Cloud Connector VMs
					 
				
			
			
				
					
						 
					 
				
				
					
						 
					 
				
			
		
	

	
		Module 4: _CCRLOnXenServer-CCStuff:
	 

	
		
			
				
					
						Filename                                                                                             
					 
				
				
					
						Purpose                                           
					 
				
			
		
		
			
				
					
						_CCRLOnXenServer-CCStuff.tf
					 
				
				
					
						Resource configuration and primary flow definition
					 
				
			
			
				
					
						_CCRLOnXenServer-CCStuff-variables.tf
					 
				
				
					
						Definition of Variables
					 
				
			
			
				
					
						_CCRLOnXenServer-CCStuff.auto.tfvars.json
					 
				
				
					
						Setting the values of the Variables
					 
				
			
			
				
					
						terraform.tvars
					 
				
				
					
						Setting the values of Variables that can not be set by a .json-based file
					 
				
			
			
				
					
						_CCRLOnXenServer-Provider.tf
					 
				
				
					
						Provider definition and configuration
					 
				
			
			
				
					
						_CCRLOnXenServer-Provider-variables.tf
					 
				
				
					
						Definition of Variables
					 
				
			
			
				
					
						_CCRLOnXenServer-Provider.auto.tfvars.json
					 
				
				
					
						Setting the values of the Variables
					 
				
			
		
	

	
		
			Caution:
		 

		
			All Terraform-related directories and files (.terraform, terraform.lock.hcl, terraform.tfstate, terraform.tfstate) must not be changed or deleted - doing so might break the deployment. 
			 
		 
	

	
		Change the settings in the .json or .tfvars files to match your needs.
	 

	
		To ensure a smooth and error-free build, the following prerequisites must be met before setting the corresponding settings or running the Terraform workflow. 
		 
	 

	
		Software Components for Configuration and Deployment
	

	
		
			Important:
		 

		
			The Cloud Connector Installer and the Citrix Virtual Apps and Desktops PowerShell SDK Installer must be manually uploaded to a location where Terraform can download them during the configuration run. 
			 
		 
	

	
		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
		
		
			XenServer 8 PowerShell SDK. Download the XenServer 8 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 to which all the required software is uploaded. 
		The URIs of the Storage Repository can be set in the corresponding variables:
	 

	
		
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}		
		
			
				
					
						
							
								"CC_Install_CWCURI":"https://XXXX.blob.core.windows.net/tfdata/cwcconnector.exe", 
								"CC_Install_RPoSHURI":"https://XXXX.blob.core.windows.net/tfdata/CitrixPoshSdk.exe“
							 
						
					
				
			
		
	

	
		We must also determine various information outside the XenServer Cluster to correctly set specific variables, such as Network and Master Image names.
	 

	
		The first step is to connect to the XenServer 8-Cluster:
	 

	
		
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}		
		
			
				
					
						
							PS C:\_TACG\_CCRLOnXenServer\_CCRLOnXenServer-Creation&gt;Import-Module XenServerPSModule 
							PS C:\_TACG\_CCRLOnXenServer\_CCRLOnXenServer-Creation&gt; Connect-Xenserver -Url https://10.10.111.11 -Username root -Password *********************** 
							 
							New Security Certificate 
							The certificate fingerprint of the server you have connected to is : 
							4B:89:BF:DA:B6:B4:20:71:D9:45:28:A8:FC:9E:60:12:96:0A:5F:C3 
							The certificate on this server is not trusted. 
							Do you wish to continue? 
							[J] Ja  [N] Nein  [H] Anhalten  [?] Hilfe (Standard ist "J"): J 
							 
							PS C:\_TACG\_CCRLOnXenServer\_CCRLOnXenServer-Creation&gt; 
						 
					
				
			
		
	

	
		Now we need to get information about the available VM templates:
	 

	
		
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}		
		
			
				
					
						
							PS C:\_TACG\_CCRLOnXenServer\_CCRLOnXenServer-Creation&gt; Get-XenVM |  Where {$_.is_a_template -eq $True -and $_.domid -ne 0}  | select name_label | Sort-Object -Property name_label 
							 
							name_label 
							---------- 
							CentOS 7 
							CentOS Stream 9 (preview) 
							Debian Bookworm 12 (preview) 
							Debian Bullseye 11 
							Debian Buster 10 
							Gooroom Platform 2.0 
							NeoKylin Linux Server 7 
							Oracle Linux 7 
							Oracle Linux 8 
							Other install media 
							Red Hat Enterprise Linux 7 
							Red Hat Enterprise Linux 8 
							Red Hat Enterprise Linux 9 (preview) 
							Rocky Linux 8 
							Rocky Linux 9 (preview) 
							Scientific Linux 7 
							SUSE Linux Enterprise 15 (64-bit) 
							SUSE Linux Enterprise Server 12 SP5 (64-bit) 
							TMPL-W11 
							TMPL-W2K22 
							Ubuntu Focal Fossa 20.04 
							Ubuntu Jammy Jellyfish 22.04 
							Windows 10 (64-bit) 
							Windows 11 
							Windows Server 2016 (64-bit) 
							Windows Server 2019 (64-bit) 
							Windows Server 2022 (64-bit) 
							 
							 
							PS C:\_TACG\_CCRLOnXenServer\_CCRLOnXenServer-Creation&gt;
						 
					
				
			
		
	

	
		We have determined all the values needed and can start the deployment.
	 

	
		
			Important: 
		 

		
			Remember to put all previously determined entity values in the corresponding .auto.tfvars.json variable files. 
			 
		 
	

	
		 
	 

	
		Module 1: Create the initially needed Virtual Machines on XenServer 8
	

	
		This module is split into the following configuration parts:
	 

	
		
			Creating the initially needed Resources on XenServer 8:

			
				
					Creating two Windows Server 2022-based VMs, which will be used as Cloud Connector VMs
				
				
					Creating a Windows Server 2022-based VM acting as an Administrative Workstation for running Terraform Modules 3 and 4 is necessary because we will use WinRM for further configuration and deployment in Modules 3 and 4
				
			
		
	

	
		Terraform automatically does all these steps.
	 

	
		
			Important:
		 

		
			This first part is separated from the following steps as we assume no Admin-VM is installed in the corresponding cluster. 
		 

		
			The Admin-VM is created in Module 1 and configured in Module 2, and all subsequent modules must be run from it. 
			Working with WinRM calls over the Internet would pose a security threat and significantly reduce the performance of the Terraform-based deployment. 
		 

		
			These are the reasons for the split approach. 
			 
		 
	

	
		 
	 

	
		
			Important:
		 

		
			Before starting the Terraform workflow, please ensure you have configured the variables according to your needs. 
			 
		 
	

	
		The configuration can be started by following the standard Terraform workflow:
	 

	
		
			terraform init,
		 

		
			terraform plan
		 
	

	
		and if no errors occur
	 

	
		
			terraform apply
		 
	

	
		 
	 

	
		
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}		
		
			
				
					
						
							PS C:\_TACG\_CCRLOnXenServer\_CCRLOnXenServer-Creation&gt; terraform init 
							Initializing the backend... 
							Initializing provider plugins... 
							- Finding latest version of xenserver/xenserver... 
							- Installing xenserver/xenserver v0.1.1... 
							- Installed xenserver/xenserver v0.1.1 (self-signed, key ID FABD99A0D7648275) 
							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\_CCRLOnXenServer\_CCRLOnXenServer-Creation&gt; terraform plan 
							data.xenserver_sr.sr: Reading... 
							data.xenserver_network.network: Reading... 
							data.xenserver_sr.sr: Read complete after 0s 
							data.xenserver_network.network: Read complete after 0s 
							 
							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: 
							 
							  # xenserver_sr.local will be created 
							  + resource "xenserver_sr" "local" { 
							      + content_type     = "ISOs" 
							      + device_config    = { 
							          + "cifspassword" = "*************" 
							          + "location"     = "\\\\tacg-dc.the-austrian-citrix-guy.at\\_sw" 
							          + "type"         = "cifs" 
							          + "username"     = "************@the-austrian-citrix-guy.at" 
							          + "vers"         = "1.0" 
							        } 
							      + host             = (known after apply) 
							      + id               = (known after apply) 
							      + name_description = "ISO repository on TACG-DC" 
							      + name_label       = "ISO on TACG-DCN" 
							      + shared           = true 
							      + sm_config        = { 
							          + "iso_type" = "cifs" 
							        } 
							      + type             = "iso" 
							      + uuid             = (known after apply) 
							    } 
							 
							  # xenserver_vm.vm["windows-vm"] will be created 
							  + resource "xenserver_vm" "vm" { 
							      + boot_mode         = (known after apply) 
							      + boot_order        = (known after apply) 
							      + cdrom             = "Citrix_Virtual_Apps_and_Desktops_7_2402_LTSR_CU1_1100.iso" 
							      + check_ip_timeout  = 0 
							      + cores_per_socket  = 2 
							      + default_ip        = (known after apply) 
							      + dynamic_mem_max   = (known after apply) 
							      + dynamic_mem_min   = (known after apply) 
							      + hard_drive        = (known after apply) 
							      + id                = (known after apply) 
							      + name_description  = "" 
							      + name_label        = "TACG-XS-CC1N" 
							      + network_interface = [ 
							          + { 
							              + device       = "0" 
							              + mac          = (known after apply) 
							              + network_uuid = "14c664c3-1d69-5baa-6e51-21f7059aec3a" 
							              + other_config = (known after apply) 
							              + vif_ref      = (known after apply) 
							            }, 
							        ] 
							      + other_config      = {} 
							      + static_mem_max    = 8589934592 
							      + static_mem_min    = (known after apply) 
							      + template_name     = "TMPL-W2K22-WinRM" 
							      + uuid              = (known after apply) 
							      + vcpus             = 2 
							    } 
							 
							  # xenserver_vm.vm["windows-vm1"] will be created 
							  + resource "xenserver_vm" "vm" { 
							      + boot_mode         = (known after apply) 
							      + boot_order        = (known after apply) 
							      + cdrom             = "Citrix_Virtual_Apps_and_Desktops_7_2402_LTSR_CU1_1100.iso" 
							      + check_ip_timeout  = 0 
							      + cores_per_socket  = 2 
							      + default_ip        = (known after apply) 
							      + dynamic_mem_max   = (known after apply) 
							      + dynamic_mem_min   = (known after apply) 
							      + hard_drive        = (known after apply) 
							      + id                = (known after apply) 
							      + name_description  = "" 
							      + name_label        = "TACG-XS-CC2N" 
							      + network_interface = [ 
							          + { 
							              + device       = "0" 
							              + mac          = (known after apply) 
							              + network_uuid = "14c664c3-1d69-5baa-6e51-21f7059aec3a" 
							              + other_config = (known after apply) 
							              + vif_ref      = (known after apply) 
							            }, 
							        ] 
							      + other_config      = {} 
							      + static_mem_max    = 8589934592 
							      + static_mem_min    = (known after apply) 
							      + template_name     = "TMPL-W2K22-WinRM" 
							      + uuid              = (known after apply) 
							      + vcpus             = 2 
							    } 
							 
							  # xenserver_vm.vm["windows-vm2"] will be created 
							  + resource "xenserver_vm" "vm" { 
							      + boot_mode         = (known after apply) 
							      + boot_order        = (known after apply) 
							      + cdrom             = "Citrix_Virtual_Apps_and_Desktops_7_2402_LTSR_CU1_1100.iso" 
							      + check_ip_timeout  = 0 
							      + cores_per_socket  = 2 
							      + default_ip        = (known after apply) 
							      + dynamic_mem_max   = (known after apply) 
							      + dynamic_mem_min   = (known after apply) 
							      + hard_drive        = (known after apply) 
							      + id                = (known after apply) 
							      + name_description  = "" 
							      + name_label        = "TACG-XS-AVMN" 
							      + network_interface = [ 
							          + { 
							              + device       = "0" 
							              + mac          = (known after apply) 
							              + network_uuid = "14c664c3-1d69-5baa-6e51-21f7059aec3a" 
							              + other_config = (known after apply) 
							              + vif_ref      = (known after apply) 
							            }, 
							        ] 
							      + other_config      = {} 
							      + static_mem_max    = 8589934592 
							      + static_mem_min    = (known after apply) 
							      + template_name     = "TMPL-W2K22-WinRM" 
							      + uuid              = (known after apply) 
							      + vcpus             = 2 
							    } 
							 
							Plan: 4 to add, 0 to change, 0 to destroy. 
							 
							Changes to Outputs: 
							  + local_storage_output = [ 
							      + { 
							          + allowed_operations   = [ 
							              + "vdi_enable_cbt", 
							              + "vdi_list_changed_blocks", 
							              + "unplug", 
							              + "plug", 
							              + "pbd_create", 
							              + "vdi_disable_cbt", 
							              + "update", 
							              + "pbd_destroy", 
							              + "vdi_resize", 
							              + "vdi_clone", 
							              + "vdi_data_destroy", 
							              + "scan", 
							              + "vdi_snapshot", 
							              + "vdi_mirror", 
							              + "vdi_create", 
							              + "vdi_destroy", 
							              + "vdi_set_on_boot", 
							            ] 
							          + blobs                = {} 
							          + clustered            = false 
							          + content_type         = "user" 
							          + current_operations   = {} 
							          + introduced_by        = "OpaqueRef:NULL" 
							          + is_tools_sr          = false 
							          + local_cache_enabled  = true 
							          + name_description     = "" 
							          + name_label           = "Local storage" 
							          + other_config         = { 
							              + i18n-key                       = "local-storage" 
							              + i18n-original-value-name_label = "Local storage" 
							            } 
							          + pbds                 = [ 
							              + "OpaqueRef:40fbb5af-e4ee-3317-09f3-57b2be72f1ae", 
							            ] 
							          + physical_size        = 939477946368 
							          + physical_utilisation = 60174692352 
							          + shared               = false 
							          + sm_config            = { 
							              + devserial = "" 
							            } 
							          + tags                 = [] 
							          + type                 = "ext" 
							          + uuid                 = "aab1bef3-f1d2-f0ab-6f14-4daa13dd7060" 
							          + vdis                 = [ 
							              + "OpaqueRef:f2e71aba-94e4-88da-923a-361090282d91", 
							              + "OpaqueRef:d862d75a-766b-bb35-4127-01ea042ad0ad", 
							              + "OpaqueRef:d6e7d03c-333f-35d5-9e0a-4876c000994a", 
							              + "OpaqueRef:bf1790a7-d7dd-f599-957b-e779ad04dd2a", 
							              + "OpaqueRef:76f22e16-9319-1aaf-3f24-8074c3259bba", 
							              + "OpaqueRef:5b3f713e-466f-92c4-cb78-c105c3764ad9", 
							              + "OpaqueRef:56682099-65cf-8c1d-af9d-8519382b45c7", 
							              + "OpaqueRef:4f085f55-1d81-58ab-a988-5674adc7a98f", 
							              + "OpaqueRef:2e1577a1-0f60-29f3-803d-21a6248fed85", 
							              + "OpaqueRef:1230fbed-704b-e433-d1ca-ea29bdc45bf8", 
							            ] 
							          + virtual_allocation   = 581968068608 
							        }, 
							    ] 
							  + network_output       = [ 
							      + { 
							          + allowed_operations   = [] 
							          + assigned_ips         = {} 
							          + blobs                = {} 
							          + bridge               = "xenbr0" 
							          + current_operations   = {} 
							          + default_locking_mode = "unlocked" 
							          + managed              = true 
							          + mtu                  = 1500 
							          + name_description     = "" 
							          + name_label           = "Pool-wide network associated with eth0" 
							          + other_config         = {} 
							          + pifs                 = [ 
							              + "OpaqueRef:3604b1d6-c451-54c1-325c-d07f7390d831", 
							            ] 
							          + purpose              = [] 
							          + tags                 = [] 
							          + uuid                 = "14c664c3-1d69-5baa-6e51-21f7059aec3a" 
							          + vifs                 = [ 
							              + "OpaqueRef:b597e0b1-13c5-bb48-ef3f-c9ed2581a6cb", 
							              + "OpaqueRef:b56bea38-6c5c-3475-8956-7a8737ac6f0a", 
							              + "OpaqueRef:ab51cf35-be24-b612-5bde-af6fbd9aeec9", 
							              + "OpaqueRef:3feebcb9-808a-3946-1340-822fde8377eb", 
							              + "OpaqueRef:3b01a49b-c144-7409-8229-9ff07c8a3f89", 
							              + "OpaqueRef:0acad856-acb4-f184-6000-d8bbca3dfaf4", 
							              + "OpaqueRef:06a4b984-6640-b552-47fe-3a04f910a274", 
							            ] 
							        }, 
							    ] 
							  + vm_out               = { 
							      + TACG-XS-AVMN = { 
							          + boot_mode         = (known after apply) 
							          + boot_order        = (known after apply) 
							          + cdrom             = "Citrix_Virtual_Apps_and_Desktops_7_2402_LTSR_CU1_1100.iso" 
							          + check_ip_timeout  = 0 
							          + cores_per_socket  = 2 
							          + default_ip        = (known after apply) 
							          + dynamic_mem_max   = (known after apply) 
							          + dynamic_mem_min   = (known after apply) 
							          + hard_drive        = (known after apply) 
							          + id                = (known after apply) 
							          + name_description  = "" 
							          + name_label        = "TACG-XS-AVMN" 
							          + network_interface = [ 
							              + { 
							                  + device       = "0" 
							                  + mac          = (known after apply) 
							                  + network_uuid = "14c664c3-1d69-5baa-6e51-21f7059aec3a" 
							                  + other_config = (known after apply) 
							                  + vif_ref      = (known after apply) 
							                }, 
							            ] 
							          + other_config      = {} 
							          + static_mem_max    = 8589934592 
							          + static_mem_min    = (known after apply) 
							          + template_name     = "TMPL-W2K22-WinRM" 
							          + uuid              = (known after apply) 
							          + vcpus             = 2 
							        } 
							      + TACG-XS-CC1N = { 
							          + boot_mode         = (known after apply) 
							          + boot_order        = (known after apply) 
							          + cdrom             = "Citrix_Virtual_Apps_and_Desktops_7_2402_LTSR_CU1_1100.iso" 
							          + check_ip_timeout  = 0 
							          + cores_per_socket  = 2 
							          + default_ip        = (known after apply) 
							          + dynamic_mem_max   = (known after apply) 
							          + dynamic_mem_min   = (known after apply) 
							          + hard_drive        = (known after apply) 
							          + id                = (known after apply) 
							          + name_description  = "" 
							          + name_label        = "TACG-XS-CC1N" 
							          + network_interface = [ 
							              + { 
							                  + device       = "0" 
							                  + mac          = (known after apply) 
							                  + network_uuid = "14c664c3-1d69-5baa-6e51-21f7059aec3a" 
							                  + other_config = (known after apply) 
							                  + vif_ref      = (known after apply) 
							                }, 
							            ] 
							          + other_config      = {} 
							          + static_mem_max    = 8589934592 
							          + static_mem_min    = (known after apply) 
							          + template_name     = "TMPL-W2K22-WinRM" 
							          + uuid              = (known after apply) 
							          + vcpus             = 2 
							        } 
							      + TACG-XS-CC2N = { 
							          + boot_mode         = (known after apply) 
							          + boot_order        = (known after apply) 
							          + cdrom             = "Citrix_Virtual_Apps_and_Desktops_7_2402_LTSR_CU1_1100.iso" 
							          + check_ip_timeout  = 0 
							          + cores_per_socket  = 2 
							          + default_ip        = (known after apply) 
							          + dynamic_mem_max   = (known after apply) 
							          + dynamic_mem_min   = (known after apply) 
							          + hard_drive        = (known after apply) 
							          + id                = (known after apply) 
							          + name_description  = "" 
							          + name_label        = "TACG-XS-CC2N" 
							          + network_interface = [ 
							              + { 
							                  + device       = "0" 
							                  + mac          = (known after apply) 
							                  + network_uuid = "14c664c3-1d69-5baa-6e51-21f7059aec3a" 
							                  + other_config = (known after apply) 
							                  + vif_ref      = (known after apply) 
							                }, 
							            ] 
							          + other_config      = {} 
							          + static_mem_max    = 8589934592 
							          + static_mem_min    = (known after apply) 
							          + template_name     = "TMPL-W2K22-WinRM" 
							          + uuid              = (known after apply) 
							          + vcpus             = 2 
							        } 
							    } 
							 
							────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 
							 
							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. 
							 
							C:\_TACG\_CCRLOnXenServer\_CCRLOnXenServer-Creation&gt; terraform apply 
							data.xenserver_network.network: Reading... 
							data.xenserver_sr.sr: Reading... 
							data.xenserver_sr.sr: Read complete after 0s 
							data.xenserver_network.network: Read complete after 0s 
							 
							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: 
							 
							  # xenserver_sr.local will be created 
							  + resource "xenserver_sr" "local" { 
							      + content_type     = "ISOs" 
							      + device_config    = { 
							          + "cifspassword" = "************" 
							          + "location"     = "\\\\tacg-dc.the-austrian-citrix-guy.at\\_sw" 
							          + "type"         = "cifs" 
							          + "username"     = "************@the-austrian-citrix-guy.at" 
							          + "vers"         = "1.0" 
							        } 
							      + host             = (known after apply) 
							      + id               = (known after apply) 
							      + name_description = "ISO repository on TACG-DC" 
							      + name_label       = "ISO on TACG-DCN" 
							      + shared           = true 
							      + sm_config        = { 
							          + "iso_type" = "cifs" 
							        } 
							      + type             = "iso" 
							      + uuid             = (known after apply) 
							    } 
							 
							  # xenserver_vm.vm["windows-vm"] will be created 
							  + resource "xenserver_vm" "vm" { 
							      + boot_mode         = (known after apply) 
							      + boot_order        = (known after apply) 
							      + cdrom             = "Citrix_Virtual_Apps_and_Desktops_7_2402_LTSR_CU1_1100.iso" 
							      + check_ip_timeout  = 0 
							      + cores_per_socket  = 2 
							      + default_ip        = (known after apply) 
							      + dynamic_mem_max   = (known after apply) 
							      + dynamic_mem_min   = (known after apply) 
							      + hard_drive        = (known after apply) 
							      + id                = (known after apply) 
							      + name_description  = "" 
							      + name_label        = "TACG-XS-CC1N" 
							      + network_interface = [ 
							          + { 
							              + device       = "0" 
							              + mac          = (known after apply) 
							              + network_uuid = "14c664c3-1d69-5baa-6e51-21f7059aec3a" 
							              + other_config = (known after apply) 
							              + vif_ref      = (known after apply) 
							            }, 
							        ] 
							      + other_config      = {} 
							      + static_mem_max    = 8589934592 
							      + static_mem_min    = (known after apply) 
							      + template_name     = "TMPL-W2K22-WinRM" 
							      + uuid              = (known after apply) 
							      + vcpus             = 2 
							    } 
							 
							  # xenserver_vm.vm["windows-vm1"] will be created 
							  + resource "xenserver_vm" "vm" { 
							      + boot_mode         = (known after apply) 
							      + boot_order        = (known after apply) 
							      + cdrom             = "Citrix_Virtual_Apps_and_Desktops_7_2402_LTSR_CU1_1100.iso" 
							      + check_ip_timeout  = 0 
							      + cores_per_socket  = 2 
							      + default_ip        = (known after apply) 
							      + dynamic_mem_max   = (known after apply) 
							      + dynamic_mem_min   = (known after apply) 
							      + hard_drive        = (known after apply) 
							      + id                = (known after apply) 
							      + name_description  = "" 
							      + name_label        = "TACG-XS-CC2N" 
							      + network_interface = [ 
							          + { 
							              + device       = "0" 
							              + mac          = (known after apply) 
							              + network_uuid = "14c664c3-1d69-5baa-6e51-21f7059aec3a" 
							              + other_config = (known after apply) 
							              + vif_ref      = (known after apply) 
							            }, 
							        ] 
							      + other_config      = {} 
							      + static_mem_max    = 8589934592 
							      + static_mem_min    = (known after apply) 
							      + template_name     = "TMPL-W2K22-WinRM" 
							      + uuid              = (known after apply) 
							      + vcpus             = 2 
							    } 
							 
							  # xenserver_vm.vm["windows-vm2"] will be created 
							  + resource "xenserver_vm" "vm" { 
							      + boot_mode         = (known after apply) 
							      + boot_order        = (known after apply) 
							      + cdrom             = "Citrix_Virtual_Apps_and_Desktops_7_2402_LTSR_CU1_1100.iso" 
							      + check_ip_timeout  = 0 
							      + cores_per_socket  = 2 
							      + default_ip        = (known after apply) 
							      + dynamic_mem_max   = (known after apply) 
							      + dynamic_mem_min   = (known after apply) 
							      + hard_drive        = (known after apply) 
							      + id                = (known after apply) 
							      + name_description  = "" 
							      + name_label        = "TACG-XS-AVMN" 
							      + network_interface = [ 
							          + { 
							              + device       = "0" 
							              + mac          = (known after apply) 
							              + network_uuid = "14c664c3-1d69-5baa-6e51-21f7059aec3a" 
							              + other_config = (known after apply) 
							              + vif_ref      = (known after apply) 
							            }, 
							        ] 
							      + other_config      = {} 
							      + static_mem_max    = 8589934592 
							      + static_mem_min    = (known after apply) 
							      + template_name     = "TMPL-W2K22-WinRM" 
							      + uuid              = (known after apply) 
							      + vcpus             = 2 
							    } 
							 
							Plan: 4 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 
							 
							xenserver_sr.local: Creating... 
							xenserver_sr.local: Creation complete after 1s [id=2948d73a-4432-299b-2311-280af7b3221c] 
							xenserver_vm.vm["windows-vm2"]: Creating... 
							xenserver_vm.vm["windows-vm1"]: Creating... 
							xenserver_vm.vm["windows-vm"]: Creating... 
							xenserver_vm.vm["windows-vm2"]: Creation complete after 1s [id=91d6c991-2385-36e2-9710-18f19f1fe2c6] 
							xenserver_vm.vm["windows-vm"]: Still creating... [10s elapsed] 
							xenserver_vm.vm["windows-vm1"]: Still creating... [10s elapsed] 
							xenserver_vm.vm["windows-vm1"]: Creation complete after 15s [id=47810c47-29fb-445d-1b7f-febc849759b4] 
							xenserver_vm.vm["windows-vm"]: Still creating... [20s elapsed] 
							xenserver_vm.vm["windows-vm"]: Still creating... [30s elapsed] 
							xenserver_vm.vm["windows-vm"]: Creation complete after 32s [id=49dac206-2173-8996-4b67-4517963b0bd3] 
							 
							Apply complete! Resources: 4 added, 0 changed, 0 destroyed. 
							 
							 
							PS C:\_TACG\_CCRLOnXenServer\_CCRLOnXenServer-Creation&gt;
						 
					
				
			
		
	

	
		XenCenter shows the created and running VMs: 
		
	 

	
		The Terraform flow was completed successfully, and the next module can begin.
	 

	
		Module 2: Configuration of the Cloud Connector and Admin-VMs
	

	
		Due to the very early version of the Terraform provider for XenServer, the following essential steps must be done by calling PowerShell scripts on the 3 previously created VMs using WinRM as the Terraform provider for XenServer 8 is not able to fulfill these:
	 

	
		
			
				
					Rename the VM names to meet the AD requirements
				
				
					Set the correct IP addresses
				
				
					Add the 3 VMs to the Domain
				
			
		
	

	
		
			Note: 
		 

		
			This module is complex as many automated actions and different entities are created and run on the VMs. It contains a mixture of PowerShell scripts and Terraform scripts to achieve a working deployment. 
			 
		 
	

	
		Please ensure you have configured the variables according to your needs using the corresponding .auto.tfvars.json file.
	 

	
		
			Important:
		 

		
			Before starting the Terraform workflow, please ensure you have configured the variables according to your needs. 
			 
		 
	

	
		The configuration can be started by following the standard Terraform workflow:
	 

	
		
			terraform init,
		 

		
			terraform plan 
			 
		 
	

	
		and if no errors occur
	 

	
		
			terraform apply
		 
	

	
		 
	 

	
		
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}		
		
			
				
					
						
							PS C:\_TACG\_CCRLOnXenServer\_CCRLOnXenServer-InitialConfiguration&gt; terraform init 
							Initializing the backend... 
							Initializing provider plugins... 
							- Finding latest version of hashicorp/null... 
							- Finding latest version of xenserver/xenserver... 
							- Finding latest version of hashicorp/local... 
							- Installing hashicorp/null v3.2.2... 
							- Installed hashicorp/null v3.2.2 (signed by HashiCorp) 
							- Installing xenserver/xenserver v0.1.1... 
							- Installed xenserver/xenserver v0.1.1 (self-signed, key ID FABD99A0D7648275) 
							- 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\_CCRLOnXenServer\_CCRLOnXenServer-InitialConfiguration&gt; terraform plan 
							data.xenserver_vm.vm_data: Reading... 
							data.xenserver_network.network: Reading... 
							data.xenserver_sr.sr: Reading... 
							data.xenserver_network.network: Read complete after 0s 
							data.xenserver_sr.sr: Read complete after 0s 
							data.xenserver_vm.vm_data: Read complete after 0s 
							 
							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.WriteAVMIPScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteAVMIPScriptIntoDataDirectory" { 
							      + content              = &lt;&lt;-EOT 
							            $Adapter = Get-NetAdapter | ? {$_.Status -eq "up"} 
							            $IFIndx = $Adapter.InterfaceIndex 
							            $IpAddress = '10.10.111.23' 
							            $Prefix = '24' 
							            $GW = '10.10.0.1' 
							            $DNS = '10.10.100.1' 
							            $IPType = 'IPv4' 
							 
							            $adapter | New-NetIPAddress -AddressFamily $IPType -IPAddress $IpAddress -PrefixLength $Prefix -DefaultGateway $GW 
							            $adapter | Set-DnsClientServerAddress -ServerAddresses $DNS 
							        EOT 
							      + 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             = "./data/AVM-IP-Script.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WriteAddToDomainScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteAddToDomainScriptIntoDataDirectory" { 
							      + 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             = "./data/AddToDomain-Script.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WriteCC1IPScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteCC1IPScriptIntoDataDirectory" { 
							      + content              = &lt;&lt;-EOT 
							            $Adapter = Get-NetAdapter | ? {$_.Status -eq "up"} 
							            $IFIndx = $Adapter.InterfaceIndex 
							            $IpAddress = '10.10.111.21' 
							            $Prefix = '24' 
							            $GW = '10.10.0.1' 
							            $DNS = '10.10.100.1' 
							            $IPType = 'IPv4' 
							 
							            $adapter | New-NetIPAddress -AddressFamily $IPType -IPAddress $IpAddress -PrefixLength $Prefix -DefaultGateway $GW 
							            $adapter | Set-DnsClientServerAddress -ServerAddresses $DNS 
							        EOT 
							      + 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             = "./data/CC1-IP-Script.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WriteCC2IPScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteCC2IPScriptIntoDataDirectory" { 
							      + content              = &lt;&lt;-EOT 
							            $Adapter = Get-NetAdapter | ? {$_.Status -eq "up"} 
							            $IFIndx = $Adapter.InterfaceIndex 
							            $IpAddress = '10.10.111.22' 
							            $Prefix = '24' 
							            $GW = '10.10.0.1' 
							            $DNS = '10.10.100.1' 
							            $IPType = 'IPv4' 
							 
							            $adapter | New-NetIPAddress -AddressFamily $IPType -IPAddress $IpAddress -PrefixLength $Prefix -DefaultGateway $GW 
							            $adapter | Set-DnsClientServerAddress -ServerAddresses $DNS 
							        EOT 
							      + 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             = "./data/CC2-IP-Script.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WriteInstallationScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteInstallationScriptIntoDataDirectory" { 
							      + content              = &lt;&lt;-EOT 
							            New-Item -Path C:/TEMP/XDINST -ItemType Directory 
							        EOT 
							      + 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             = "./data/InstallationLogPath-Script.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WriteRebootScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteRebootScriptIntoDataDirectory" { 
							      + content              = &lt;&lt;-EOT 
							            Restart-Computer -Force 
							        EOT 
							      + 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             = "./data/Reboot-Script.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WriteRenameComputerAVMScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteRenameComputerAVMScriptIntoDataDirectory" { 
							      + 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             = "./data/RenameComputer-AVM-Script.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WriteRenameComputerCC1ScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteRenameComputerCC1ScriptIntoDataDirectory" { 
							      + 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             = "./data/RenameComputer-CC1-Script.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WriteRenameComputerCC2ScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteRenameComputerCC2ScriptIntoDataDirectory" { 
							      + 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             = "./data/RenameComputer-CC2-Script.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # null_resource.UploadAndExecuteRenameScripts-AVM will be created 
							  + resource "null_resource" "UploadAndExecuteRenameScripts-AVM" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.UploadAndExecuteRenameScripts-CC1 will be created 
							  + resource "null_resource" "UploadAndExecuteRenameScripts-CC1" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.UploadAndExecuteRenameScripts-CC2 will be created 
							  + resource "null_resource" "UploadAndExecuteRenameScripts-CC2" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.UploadAndExecuteScripts-AVM will be created 
							  + resource "null_resource" "UploadAndExecuteScripts-AVM" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.UploadAndExecuteScripts-CC1 will be created 
							  + resource "null_resource" "UploadAndExecuteScripts-CC1" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.UploadAndExecuteScripts-CC2 will be created 
							  + resource "null_resource" "UploadAndExecuteScripts-CC2" { 
							      + id = (known after apply) 
							    }
						 

						
							 
							# null_resource.UploadAndExecuteChangeIPScript-AVM will be created 
							  + resource "null_resource" "UploadAndExecuteChangeIPScript-AVM" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.UploadAndExecuteChangeIPScript-CC1 will be created 
							  + resource "null_resource" "UploadAndExecuteChangeIPScript-CC1" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.UploadAndExecuteChangeIPScript-CC2 will be created 
							  + resource "null_resource" "UploadAndExecuteChangeIPScript-CC2" { 
							      + id = (known after apply) 
							    } 
							 
							  # time_sleep.WaitToRebootAVMAfterIPChange will be created 
							  + resource "time_sleep" "WaitToRebootAVMAfterIPChange" { 
							      + create_duration = "60s" 
							      + id              = (known after apply) 
							    } 
							 
							  # time_sleep.WaitToRebootAVMAfterRename will be created 
							  + resource "time_sleep" "WaitToRebootAVMAfterRename" { 
							      + create_duration = "60s" 
							      + id              = (known after apply) 
							    } 
							 
							  # time_sleep.WaitToRebootCC1AfterIPChange will be created 
							  + resource "time_sleep" "WaitToRebootCC1AfterIPChange" { 
							      + create_duration = "60s" 
							      + id              = (known after apply) 
							    } 
							 
							  # time_sleep.WaitToRebootCC1AfterRename will be created 
							  + resource "time_sleep" "WaitToRebootCC1AfterRename" { 
							      + create_duration = "60s" 
							      + id              = (known after apply) 
							    } 
							 
							  # time_sleep.WaitToRebootCC2AfterIPChange will be created 
							  + resource "time_sleep" "WaitToRebootCC2AfterIPChange" { 
							      + create_duration = "60s" 
							      + id              = (known after apply) 
							    } 
							 
							  # time_sleep.WaitToRebootCC2AfterRename will be created 
							  + resource "time_sleep" "WaitToRebootCC2AfterRename" { 
							      + create_duration = "60s" 
							      + id              = (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\_CCRLOnXenServer\_CCRLOnXenServer-InitialConfiguration&gt; terraform validate 
							Success! The configuration is valid. 
							 
							PS C:\_TACG\_CCRLOnXenServer\_CCRLOnXenServer-InitialConfiguration&gt; terraform apply 
							data.xenserver_network.network: Reading... 
							data.xenserver_sr.sr: Reading... 
							data.xenserver_vm.vm_data: Reading... 
							data.xenserver_network.network: Read complete after 0s 
							data.xenserver_sr.sr: Read complete after 0s 
							data.xenserver_vm.vm_data: Read complete after 0s 
							 
							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.WriteAVMIPScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteAVMIPScriptIntoDataDirectory" { 
							      + content              = &lt;&lt;-EOT 
							            $Adapter = Get-NetAdapter | ? {$_.Status -eq "up"} 
							            $IFIndx = $Adapter.InterfaceIndex 
							            $IpAddress = '10.10.111.23' 
							            $Prefix = '24' 
							            $GW = '10.10.0.1' 
							            $DNS = '10.10.100.1' 
							            $IPType = 'IPv4' 
							 
							            $adapter | New-NetIPAddress -AddressFamily $IPType -IPAddress $IpAddress -PrefixLength $Prefix -DefaultGateway $GW 
							            $adapter | Set-DnsClientServerAddress -ServerAddresses $DNS 
							        EOT 
							      + 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             = "./data/AVM-IP-Script.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WriteAddToDomainScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteAddToDomainScriptIntoDataDirectory" { 
							      + 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             = "./data/AddToDomain-Script.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WriteCC1IPScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteCC1IPScriptIntoDataDirectory" { 
							      + content              = &lt;&lt;-EOT 
							            $Adapter = Get-NetAdapter | ? {$_.Status -eq "up"} 
							            $IFIndx = $Adapter.InterfaceIndex 
							            $IpAddress = '10.10.111.21' 
							            $Prefix = '24' 
							            $GW = '10.10.0.1' 
							            $DNS = '10.10.100.1' 
							            $IPType = 'IPv4' 
							 
							            $adapter | New-NetIPAddress -AddressFamily $IPType -IPAddress $IpAddress -PrefixLength $Prefix -DefaultGateway $GW 
							            $adapter | Set-DnsClientServerAddress -ServerAddresses $DNS 
							        EOT 
							      + 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             = "./data/CC1-IP-Script.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WriteCC2IPScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteCC2IPScriptIntoDataDirectory" { 
							      + content              = &lt;&lt;-EOT 
							            $Adapter = Get-NetAdapter | ? {$_.Status -eq "up"} 
							            $IFIndx = $Adapter.InterfaceIndex 
							            $IpAddress = '10.10.111.22' 
							            $Prefix = '24' 
							            $GW = '10.10.0.1' 
							            $DNS = '10.10.100.1' 
							            $IPType = 'IPv4' 
							 
							            $adapter | New-NetIPAddress -AddressFamily $IPType -IPAddress $IpAddress -PrefixLength $Prefix -DefaultGateway $GW 
							            $adapter | Set-DnsClientServerAddress -ServerAddresses $DNS 
							        EOT 
							      + 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             = "./data/CC2-IP-Script.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WriteInstallationScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteInstallationScriptIntoDataDirectory" { 
							      + content              = &lt;&lt;-EOT 
							            New-Item -Path C:/TEMP/XDINST -ItemType Directory 
							        EOT 
							      + 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             = "./data/InstallationLogPath-Script.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WriteRebootScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteRebootScriptIntoDataDirectory" { 
							      + content              = &lt;&lt;-EOT 
							            Restart-Computer -Force 
							        EOT 
							      + 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             = "./data/Reboot-Script.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WriteRenameComputerAVMScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteRenameComputerAVMScriptIntoDataDirectory" { 
							      + 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             = "./data/RenameComputer-AVM-Script.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WriteRenameComputerCC1ScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteRenameComputerCC1ScriptIntoDataDirectory" { 
							      + 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             = "./data/RenameComputer-CC1-Script.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WriteRenameComputerCC2ScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteRenameComputerCC2ScriptIntoDataDirectory" { 
							      + 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             = "./data/RenameComputer-CC2-Script.ps1" 
							      + id                   = (known after apply) 
							    } 
							 
							  # null_resource.UploadAndExecuteRenameScripts-AVM will be created 
							  + resource "null_resource" "UploadAndExecuteRenameScripts-AVM" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.UploadAndExecuteRenameScripts-CC1 will be created 
							  + resource "null_resource" "UploadAndExecuteRenameScripts-CC1" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.UploadAndExecuteRenameScripts-CC2 will be created 
							  + resource "null_resource" "UploadAndExecuteRenameScripts-CC2" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.UploadAndExecuteScripts-AVM will be created 
							  + resource "null_resource" "UploadAndExecuteScripts-AVM" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.UploadAndExecuteScripts-CC1 will be created 
							  + resource "null_resource" "UploadAndExecuteScripts-CC1" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.UploadAndExecuteScripts-CC2 will be created 
							  + resource "null_resource" "UploadAndExecuteScripts-CC2" { 
							      + id = (known after apply) 
							    }
						 

						
							# null_resource.UploadAndExecuteChangeIPScript-AVM will be created 
							  + resource "null_resource" "UploadAndExecuteChangeIPScript-AVM" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.UploadAndExecuteChangeIPScript-CC1 will be created 
							  + resource "null_resource" "UploadAndExecuteChangeIPScript-CC1" { 
							      + id = (known after apply) 
							    } 
							 
							  # null_resource.UploadAndExecuteChangeIPScript-CC2 will be created 
							  + resource "null_resource" "UploadAndExecuteChangeIPScript-CC2" { 
							      + id = (known after apply) 
							    } 
							 
							  # time_sleep.WaitToRebootAVMAfterIPChange will be created 
							  + resource "time_sleep" "WaitToRebootAVMAfterIPChange" { 
							      + create_duration = "60s" 
							      + id              = (known after apply) 
							    } 
							 
							  # time_sleep.WaitToRebootAVMAfterRename will be created 
							  + resource "time_sleep" "WaitToRebootAVMAfterRename" { 
							      + create_duration = "60s" 
							      + id              = (known after apply) 
							    } 
							 
							  # time_sleep.WaitToRebootCC1AfterIPChange will be created 
							  + resource "time_sleep" "WaitToRebootCC1AfterIPChange" { 
							      + create_duration = "60s" 
							      + id              = (known after apply) 
							    } 
							 
							  # time_sleep.WaitToRebootCC1AfterRename will be created 
							  + resource "time_sleep" "WaitToRebootCC1AfterRename" { 
							      + create_duration = "60s" 
							      + id              = (known after apply) 
							    } 
							 
							  # time_sleep.WaitToRebootCC2AfterIPChange will be created 
							  + resource "time_sleep" "WaitToRebootCC2AfterIPChange" { 
							      + create_duration = "60s" 
							      + id              = (known after apply) 
							    } 
							 
							  # time_sleep.WaitToRebootCC2AfterRename will be created 
							  + resource "time_sleep" "WaitToRebootCC2AfterRename" { 
							      + create_duration = "60s" 
							      + id              = (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 
							 
							local_file.WriteCC2IPScriptIntoDataDirectory: Creating... 
							local_file.WriteRenameComputerAVMScriptIntoDataDirectory: Creating... 
							local_file.WriteRenameComputerCC1ScriptIntoDataDirectory: Creating... 
							local_file.WriteRebootScriptIntoDataDirectory: Creating... 
							local_file.WriteCC1IPScriptIntoDataDirectory: Creating... 
							local_file.WriteRenameComputerCC2ScriptIntoDataDirectory: Creating... 
							local_file.WriteAddToDomainScriptIntoDataDirectory: Creating... 
							local_file.WriteInstallationScriptIntoDataDirectory: Creating... 
							local_file.WriteAVMIPScriptIntoDataDirectory: Creating... 
							local_file.WriteRebootScriptIntoDataDirectory: Creation complete after 0s [id=2baf7fa209ad203f96017f90f5727642e5f44a03] 
							local_file.WriteCC2IPScriptIntoDataDirectory: Creation complete after 0s [id=b6a802f645867f1c5e9c018aa92789000affe765] 
							local_file.WriteRenameComputerCC1ScriptIntoDataDirectory: Creation complete after 0s [id=25754d4f8195e5e36c1ca98c4605875417b1f30f] 
							local_file.WriteRenameComputerAVMScriptIntoDataDirectory: Creation complete after 0s [id=9665cdd3269641adfc58740aacffddf74cc73100] 
							local_file.WriteCC1IPScriptIntoDataDirectory: Creation complete after 0s [id=5a783ca192c6a71755e1e0f090795cac7c9fdac6] 
							local_file.WriteRenameComputerCC2ScriptIntoDataDirectory: Creation complete after 0s [id=da3a25ab7f0a13864f1c209cbe25399579cd5a43] 
							local_file.WriteAVMIPScriptIntoDataDirectory: Creation complete after 0s [id=3afc3111cb1908a3273fafb3d9306476a84573f9] 
							local_file.WriteAddToDomainScriptIntoDataDirectory: Creation complete after 0s [id=9665cdd3269641adfc58740aacffddf74cc73100] 
							local_file.WriteInstallationScriptIntoDataDirectory: Creation complete after 0s [id=fc4ebc0269b8b4100c831a23c05d1851387a1259] 
							null_resource.UploadAndExecuteScripts-CC2: Creating... 
							null_resource.UploadAndExecuteScripts-CC1: Creating... 
							null_resource.UploadAndExecuteScripts-AVM: Creating... 
							null_resource.UploadAndExecuteScripts-CC1: Provisioning with 'file'... 
							null_resource.UploadAndExecuteScripts-CC2: Provisioning with 'file'... 
							null_resource.UploadAndExecuteScripts-AVM: Provisioning with 'file'... 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadAndExecuteScripts-AVM: Provisioning with 'remote-exec'... 
							null_resource.UploadAndExecuteScripts-AVM (remote-exec): Connecting to remote host via WinRM... 
							null_resource.UploadAndExecuteScripts-AVM (remote-exec):   Host: 10.10.11.122 
							null_resource.UploadAndExecuteScripts-AVM (remote-exec):   Port: 5985 
							null_resource.UploadAndExecuteScripts-AVM (remote-exec):   User: administrator 
							null_resource.UploadAndExecuteScripts-AVM (remote-exec):   Password: true 
							null_resource.UploadAndExecuteScripts-AVM (remote-exec):   HTTPS: false 
							null_resource.UploadAndExecuteScripts-AVM (remote-exec):   Insecure: false 
							null_resource.UploadAndExecuteScripts-AVM (remote-exec):   NTLM: false 
							null_resource.UploadAndExecuteScripts-AVM (remote-exec):   CACert: false 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadAndExecuteScripts-CC1: Provisioning with 'remote-exec'... 
							null_resource.UploadAndExecuteScripts-CC1 (remote-exec): Connecting to remote host via WinRM... 
							null_resource.UploadAndExecuteScripts-CC1 (remote-exec):   Host: 10.10.11.124 
							null_resource.UploadAndExecuteScripts-CC1 (remote-exec):   Port: 5985 
							null_resource.UploadAndExecuteScripts-CC1 (remote-exec):   User: administrator 
							null_resource.UploadAndExecuteScripts-CC1 (remote-exec):   Password: true 
							null_resource.UploadAndExecuteScripts-CC1 (remote-exec):   HTTPS: false 
							null_resource.UploadAndExecuteScripts-CC1 (remote-exec):   Insecure: false 
							null_resource.UploadAndExecuteScripts-CC1 (remote-exec):   NTLM: false 
							null_resource.UploadAndExecuteScripts-CC1 (remote-exec):   CACert: false 
							null_resource.UploadAndExecuteScripts-AVM (remote-exec): Connected! 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadAndExecuteScripts-CC2: Provisioning with 'remote-exec'... 
							null_resource.UploadAndExecuteScripts-CC1 (remote-exec): Connected! 
							null_resource.UploadAndExecuteScripts-CC2 (remote-exec): Connecting to remote host via WinRM... 
							null_resource.UploadAndExecuteScripts-CC2 (remote-exec):   Host: 10.10.11.123 
							null_resource.UploadAndExecuteScripts-CC2 (remote-exec):   Port: 5985 
							null_resource.UploadAndExecuteScripts-CC2 (remote-exec):   User: administrator 
							null_resource.UploadAndExecuteScripts-CC2 (remote-exec):   Password: true 
							null_resource.UploadAndExecuteScripts-CC2 (remote-exec):   HTTPS: false 
							null_resource.UploadAndExecuteScripts-CC2 (remote-exec):   Insecure: false 
							null_resource.UploadAndExecuteScripts-CC2 (remote-exec):   NTLM: false 
							null_resource.UploadAndExecuteScripts-CC2 (remote-exec):   CACert: false 
							null_resource.UploadAndExecuteScripts-CC2 (remote-exec): Connected! 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
							null_resource.UploadAndExecuteScripts-CC1 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/temp/InstallationLogPath-Script.ps1 
							 
							 
							null_resource.UploadAndExecuteScripts-CC1 (remote-exec):     Directory: C:\TEMP 
							 
							 
							null_resource.UploadAndExecuteScripts-CC1 (remote-exec): Mode                 LastWriteTime         Length Name 
							null_resource.UploadAndExecuteScripts-CC1 (remote-exec): ----                 -------------         ------ ---- 
							null_resource.UploadAndExecuteScripts-CC1 (remote-exec): d-----        29.08.2024     13:20                XDINST 
							 
							 
							 
							null_resource.UploadAndExecuteScripts-CC2 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/temp/InstallationLogPath-Script.ps1 
							 
							 
							null_resource.UploadAndExecuteScripts-CC2 (remote-exec):     Directory: C:\TEMP 
							 
							 
							null_resource.UploadAndExecuteScripts-CC2 (remote-exec): Mode                 LastWriteTime         Length Name 
							null_resource.UploadAndExecuteScripts-CC2 (remote-exec): ----                 -------------         ------ ---- 
							null_resource.UploadAndExecuteScripts-CC2 (remote-exec): d-----        29.08.2024     13:20                XDINST 
							 
							 
							 
							null_resource.UploadAndExecuteScripts-AVM (remote-exec): C:\Users\Administrator&gt;powershell -File C:/temp/InstallationLogPath-Script.ps1 
							 
							 
							null_resource.UploadAndExecuteScripts-AVM (remote-exec):     Directory: C:\TEMP 
							 
							 
							null_resource.UploadAndExecuteScripts-AVM (remote-exec): Mode                 LastWriteTime         Length Name 
							null_resource.UploadAndExecuteScripts-AVM (remote-exec): ----                 -------------         ------ ---- 
							null_resource.UploadAndExecuteScripts-AVM (remote-exec): d-----        29.08.2024     13:20                XDINST 
							 
							 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadAndExecuteScripts-CC1: Creation complete after 2s [id=7652495874960574770] 
							null_resource.UploadAndExecuteRenameScripts-CC1: Creating... 
							null_resource.UploadAndExecuteRenameScripts-CC1: Provisioning with 'file'... 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadAndExecuteScripts-CC2: Creation complete after 2s [id=7721170861022728555] 
							null_resource.UploadAndExecuteRenameScripts-CC2: Creating... 
							null_resource.UploadAndExecuteRenameScripts-CC2: Provisioning with 'file'... 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadAndExecuteScripts-AVM: Creation complete after 2s [id=5779558265363706953] 
							null_resource.UploadAndExecuteRenameScripts-AVM: Creating... 
							null_resource.UploadAndExecuteRenameScripts-AVM: Provisioning with 'file'... 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadAndExecuteRenameScripts-CC1: Provisioning with 'remote-exec'... 
							null_resource.UploadAndExecuteRenameScripts-CC1 (remote-exec): Connecting to remote host via WinRM... 
							null_resource.UploadAndExecuteRenameScripts-CC1 (remote-exec):   Host: 10.10.11.124 
							null_resource.UploadAndExecuteRenameScripts-CC1 (remote-exec):   Port: 5985 
							null_resource.UploadAndExecuteRenameScripts-CC1 (remote-exec):   User: administrator 
							null_resource.UploadAndExecuteRenameScripts-CC1 (remote-exec):   Password: true 
							null_resource.UploadAndExecuteRenameScripts-CC1 (remote-exec):   HTTPS: false 
							null_resource.UploadAndExecuteRenameScripts-CC1 (remote-exec):   Insecure: false 
							null_resource.UploadAndExecuteRenameScripts-CC1 (remote-exec):   NTLM: false 
							null_resource.UploadAndExecuteRenameScripts-CC1 (remote-exec):   CACert: false 
							null_resource.UploadAndExecuteRenameScripts-CC1 (remote-exec): Connected! 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadAndExecuteRenameScripts-CC2: Provisioning with 'remote-exec'... 
							null_resource.UploadAndExecuteRenameScripts-CC2 (remote-exec): Connecting to remote host via WinRM... 
							null_resource.UploadAndExecuteRenameScripts-CC2 (remote-exec):   Host: 10.10.11.123 
							null_resource.UploadAndExecuteRenameScripts-CC2 (remote-exec):   Port: 5985 
							null_resource.UploadAndExecuteRenameScripts-CC2 (remote-exec):   User: administrator 
							null_resource.UploadAndExecuteRenameScripts-CC2 (remote-exec):   Password: true 
							null_resource.UploadAndExecuteRenameScripts-CC2 (remote-exec):   HTTPS: false 
							null_resource.UploadAndExecuteRenameScripts-CC2 (remote-exec):   Insecure: false 
							null_resource.UploadAndExecuteRenameScripts-CC2 (remote-exec):   NTLM: false 
							null_resource.UploadAndExecuteRenameScripts-CC2 (remote-exec):   CACert: false 
							null_resource.UploadAndExecuteRenameScripts-CC2 (remote-exec): Connected! 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadAndExecuteRenameScripts-AVM: Provisioning with 'remote-exec'... 
							null_resource.UploadAndExecuteRenameScripts-AVM (remote-exec): Connecting to remote host via WinRM... 
							null_resource.UploadAndExecuteRenameScripts-AVM (remote-exec):   Host: 10.10.11.122 
							null_resource.UploadAndExecuteRenameScripts-AVM (remote-exec):   Port: 5985 
							null_resource.UploadAndExecuteRenameScripts-AVM (remote-exec):   User: administrator 
							null_resource.UploadAndExecuteRenameScripts-AVM (remote-exec):   Password: true 
							null_resource.UploadAndExecuteRenameScripts-AVM (remote-exec):   HTTPS: false 
							null_resource.UploadAndExecuteRenameScripts-AVM (remote-exec):   Insecure: false 
							null_resource.UploadAndExecuteRenameScripts-AVM (remote-exec):   NTLM: false 
							null_resource.UploadAndExecuteRenameScripts-AVM (remote-exec):   CACert: false 
							null_resource.UploadAndExecuteRenameScripts-AVM (remote-exec): Connected! 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
							null_resource.UploadAndExecuteRenameScripts-CC1 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/RenameComputer-CC1-Script.ps1 
							 
							null_resource.UploadAndExecuteRenameScripts-CC2 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/RenameComputer-CC2-Script.ps1 
							 
							null_resource.UploadAndExecuteRenameScripts-AVM (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/RenameComputer-AVM-Script.ps1 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadAndExecuteRenameScripts-CC1: Creation complete after 2s [id=3665035315952973388] 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadAndExecuteRenameScripts-CC2: Creation complete after 2s [id=5438447231913886059] 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadAndExecuteRenameScripts-AVM: Creation complete after 3s [id=8810800766147028823]
						 

						
							time_sleep.WaitToRebootCC2AfterRename: Creating... 
							time_sleep.WaitToRebootAVMAfterRename: Creating... 
							time_sleep.WaitToRebootCC1AfterRename: Creating... 
							time_sleep.WaitToRebootCC2AfterRename: Still creating... [10s elapsed] 
							time_sleep.WaitToRebootCC1AfterRename: Still creating... [10s elapsed] 
							time_sleep.WaitToRebootAVMAfterRename: Still creating... [10s elapsed] 
							time_sleep.WaitToRebootAVMAfterRename: Still creating... [20s elapsed] 
							time_sleep.WaitToRebootCC2AfterRename: Still creating... [20s elapsed] 
							time_sleep.WaitToRebootCC1AfterRename: Still creating... [20s elapsed] 
							time_sleep.WaitToRebootCC2AfterRename: Still creating... [30s elapsed] 
							time_sleep.WaitToRebootCC1AfterRename: Still creating... [30s elapsed] 
							time_sleep.WaitToRebootAVMAfterRename: Still creating... [30s elapsed] 
							time_sleep.WaitToRebootCC2AfterRename: Still creating... [40s elapsed] 
							time_sleep.WaitToRebootCC1AfterRename: Still creating... [40s elapsed] 
							time_sleep.WaitToRebootAVMAfterRename: Still creating... [40s elapsed] 
							time_sleep.WaitToRebootCC1AfterRename: Still creating... [50s elapsed] 
							time_sleep.WaitToRebootAVMAfterRename: Still creating... [50s elapsed] 
							time_sleep.WaitToRebootCC2AfterRename: Still creating... [50s elapsed] 
							time_sleep.WaitToRebootCC2AfterRename: Creation complete after 1m0s [id=2024-08-29T12:00:55Z] 
							time_sleep.WaitToRebootCC1AfterRename: Creation complete after 1m0s [id=2024-08-29T12:00:55Z] 
							time_sleep.WaitToRebootAVMAfterRename: Creation complete after 1m0s [id=2024-08-29T12:00:55Z] 
							null_resource.UploadAndExecuteChangeIPScript-AVM: Creating... 
							null_resource.UploadAndExecuteChangeIPScript-CC2: Creating... 
							null_resource.UploadAndExecuteChangeIPScript-CC1: Creating... 
							null_resource.UploadAndExecuteChangeIPScript-CC2: Provisioning with 'file'... 
							null_resource.UploadAndExecuteChangeIPScript-CC1: Provisioning with 'file'... 
							null_resource.UploadAndExecuteChangeIPScript-AVM: Provisioning with 'file'... 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadAndExecuteChangeIPScript-CC1: Provisioning with 'remote-exec'... 
							null_resource.UploadAndExecuteChangeIPScript-CC1 (remote-exec): Connecting to remote host via WinRM... 
							null_resource.UploadAndExecuteChangeIPScript-CC1 (remote-exec):   Host: 10.10.11.124 
							null_resource.UploadAndExecuteChangeIPScript-CC1 (remote-exec):   Port: 5985 
							null_resource.UploadAndExecuteChangeIPScript-CC1 (remote-exec):   User: administrator 
							null_resource.UploadAndExecuteChangeIPScript-CC1 (remote-exec):   Password: true 
							null_resource.UploadAndExecuteChangeIPScript-CC1 (remote-exec):   HTTPS: false 
							null_resource.UploadAndExecuteChangeIPScript-CC1 (remote-exec):   Insecure: false 
							null_resource.UploadAndExecuteChangeIPScript-CC1 (remote-exec):   NTLM: false 
							null_resource.UploadAndExecuteChangeIPScript-CC1 (remote-exec):   CACert: false 
							null_resource.UploadAndExecuteChangeIPScript-CC1 (remote-exec): Connected! 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadAndExecuteChangeIPScript-AVM: Provisioning with 'remote-exec'... 
							null_resource.UploadAndExecuteChangeIPScript-AVM (remote-exec): Connecting to remote host via WinRM... 
							null_resource.UploadAndExecuteChangeIPScript-AVM (remote-exec):   Host: 10.10.11.122 
							null_resource.UploadAndExecuteChangeIPScript-AVM (remote-exec):   Port: 5985 
							null_resource.UploadAndExecuteChangeIPScript-AVM (remote-exec):   User: administrator 
							null_resource.UploadAndExecuteChangeIPScript-AVM (remote-exec):   Password: true 
							null_resource.UploadAndExecuteChangeIPScript-AVM (remote-exec):   HTTPS: false 
							null_resource.UploadAndExecuteChangeIPScript-AVM (remote-exec):   Insecure: false 
							null_resource.UploadAndExecuteChangeIPScript-AVM (remote-exec):   NTLM: false 
							null_resource.UploadAndExecuteChangeIPScript-AVM (remote-exec):   CACert: false 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadAndExecuteChangeIPScript-CC2: Provisioning with 'remote-exec'... 
							null_resource.UploadAndExecuteChangeIPScript-CC2 (remote-exec): Connecting to remote host via WinRM... 
							null_resource.UploadAndExecuteChangeIPScript-CC2 (remote-exec):   Host: 10.10.11.123 
							null_resource.UploadAndExecuteChangeIPScript-CC2 (remote-exec):   Port: 5985 
							null_resource.UploadAndExecuteChangeIPScript-CC2 (remote-exec):   User: administrator 
							null_resource.UploadAndExecuteChangeIPScript-CC2 (remote-exec):   Password: true 
							null_resource.UploadAndExecuteChangeIPScript-CC2 (remote-exec):   HTTPS: false 
							null_resource.UploadAndExecuteChangeIPScript-CC2 (remote-exec):   Insecure: false 
							null_resource.UploadAndExecuteChangeIPScript-CC2 (remote-exec):   NTLM: false 
							null_resource.UploadAndExecuteChangeIPScript-CC2 (remote-exec):   CACert: false 
							null_resource.UploadAndExecuteChangeIPScript-AVM (remote-exec): Connected! 
							null_resource.UploadAndExecuteChangeIPScript-CC2 (remote-exec): Connected! 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
							null_resource.UploadAndExecuteChangeIPScript-CC1 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/CC1-IP-Script.ps1 
							 
							null_resource.UploadAndExecuteChangeIPScript-CC2 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/CC2-IP-Script.ps1 
							 
							null_resource.UploadAndExecuteChangeIPScript-AVM (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/AVM-IP-Script.ps1 
							 
						 

						
							null_resource.UploadAndExecuteChangeIPScript-CC2: Still creating... [10s elapsed] 
							null_resource.UploadAndExecuteChangeIPScript-AVM: Still creating... [10s elapsed] 
							null_resource.UploadAndExecuteChangeIPScript-CC1: Still creating... [10s elapsed] 
							null_resource.UploadAndExecuteChangeIPScript-CC1: Still creating... [20s elapsed] 
							null_resource.UploadAndExecuteChangeIPScript-CC2: Still creating... [20s elapsed] 
							null_resource.UploadAndExecuteChangeIPScript-AVM: Still creating... [20s elapsed] 
							null_resource.UploadAndExecuteChangeIPScript-AVM: Still creating... [30s elapsed] 
							null_resource.UploadAndExecuteChangeIPScript-CC2: Still creating... [30s elapsed] 
							null_resource.UploadAndExecuteChangeIPScript-CC1: Still creating... [30s elapsed] 
							null_resource.UploadAndExecuteChangeIPScript-AVM: Still creating... [40s elapsed] 
							null_resource.UploadAndExecuteChangeIPScript-CC2: Still creating... [40s elapsed] 
							null_resource.UploadAndExecuteChangeIPScript-CC1: Still creating... [40s elapsed] 
							null_resource.UploadAndExecuteChangeIPScript-CC1: Still creating... [50s elapsed] 
							null_resource.UploadAndExecuteChangeIPScript-CC2: Still creating... [50s elapsed] 
							null_resource.UploadAndExecuteChangeIPScript-AVM: Still creating... [50s elapsed] 
							null_resource.UploadAndExecuteChangeIPScript-CC2: Still creating... [1m0s elapsed] 
							null_resource.UploadAndExecuteChangeIPScript-CC1: Still creating... [1m0s elapsed] 
							null_resource.UploadAndExecuteChangeIPScript-AVM: Still creating... [1m0s elapsed] 
							null_resource.UploadAndExecuteChangeIPScript-CC2: Still creating... [1m10s elapsed] 
							null_resource.UploadAndExecuteChangeIPScript-AVM: Still creating... [1m10s elapsed] 
							null_resource.UploadAndExecuteChangeIPScript-CC1: Still creating... [1m10s elapsed] 
							╷ 
							│ Error: remote-exec provisioner error 
							│ 
							│   with null_resource.UploadAndExecuteChangeIPScript-CC1, 
							│   on CCRLOnXenServer-InitialConfiguration.tf line 372, in resource "null_resource" "UploadAndExecuteChangeIPScript-CC1": 
							│  372:  provisioner "remote-exec" { 
							│ 
							│ error executing "C:/Temp/terraform_342347787.cmd": unknown error Post "http://10.10.11.124:5985/wsman": net/http: timeout awaiting response headers 
							╵ 
							╷ 
							│ Error: remote-exec provisioner error 
							│ 
							│   with null_resource.UploadAndExecuteChangeIPScript-CC2, 
							│   on CCRLOnXenServer-InitialConfiguration.tf line 400, in resource "null_resource" "UploadAndExecuteChangeIPScript-CC2": 
							│  400:  provisioner "remote-exec" { 
							│ 
							│ error executing "C:/Temp/terraform_1797935761.cmd": unknown error Post "http://10.10.11.123:5985/wsman": net/http: timeout awaiting response headers 
							╵ 
							╷ 
							│ Error: remote-exec provisioner error 
							│ 
							│   with null_resource.UploadAndExecuteChangeIPScript-AVM, 
							│   on CCRLOnXenServer-InitialConfiguration.tf line 428, in resource "null_resource" "UploadAndExecuteChangeIPScript-AVM": 
							│  428:  provisioner "remote-exec" { 
							│ 
							│ error executing "C:/Temp/terraform_342347787.cmd": unknown error Post "http://10.10.11.122:5985/wsman": net/http: timeout awaiting response headers 
							╵
						 

						
							 
							PS C:\_TACG\_CCRLOnXenServer\_CCRLOnXenServer-InitialConfiguration&gt;
						 
					
				
			
		
	

	
		 
	 

	
		
			Important: 
		 

		
			The errors shown above occur because Terraform changes the IP addresses of the VMs and the provisioner cannot connect to the previous IP address anymore. 
			The provisioner runs in a timeout and produces these errors. 
			 
		 
	

	
		In the example above, the IP addresses during VM creation were the DHCP addresses 10.10.11.122-124. The screenshot below reflects the successful renaming of the VMs: 
		
	 

	
		Looking into Server Manager on the VM after running the Terraform script shows the successful change of the IP address to the needed one, and the successful registration in the Domain: 
		
	 

	
		The Terraform flow was completed successfully, and the next module can begin. 
		 
	 

	
		Module 3: Initial Citrix Cloud-based configurations of the Cloud Connector VMs
	

	
		The current Citrix Terraform Provider version 1.0.1 has major improvements built in—there is no need to run 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. 
		Compared to the previous versions, it dramatically simplifies the Terraform workflow. 
		 
		Example of previous Terraform scripts to create a Resource Location, a Zone in the RL, and the configuration file for the unattended installation of the Cloud Connector software before the actual Provider´s version:
	 

	
		
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}		
		
			
				
					
						
							
								### Retrieving the BearerToken
							 

							
								#### Create PowerShell script to download BearerToken
							 

							
								resource "local_file" "GetBearerToken" {
							 

							
								content  = &lt;&lt;-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  = &lt;&lt;-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"]
							 

							
								 
							 

							
								  }
							 

							
								}
							 

							
								### Create PowerShell file for determining the SiteID
							 

							
								resource "local_file" "GetSiteIDScript" {
							 

							
								  depends_on = [time_sleep.wait_60_seconds]
							 

							
								  content  = &lt;&lt;-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  = &lt;&lt;-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  = &lt;&lt;-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_corr.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 valid CWC.JSON file
							 

							
								resource "terraform_data" "ExecuteCreateValidCWCOnAVM" {
							 

							
								  depends_on = [ local_file.CreateValidCWCOnAVM ]
							 

							
								  provisioner "local-exec" {
							 

							
								     command = "${var.CC_Install_LogPath}/DATA/CreateValidCWCOnAVM.ps1"
							 

							
								    interpreter = ["PowerShell", "-File"]
							 

							
								 
							 

							
								  }
							 

							
								}
							 
						
					
				
			
		
	

	
		The actual Terraform provider dramatically simplifies the process of creating a Resource Location, a Zone in the Resource Location, and the configuration file for the unattended installation of the Cloud Connector software:
	 

	
		
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}		
		
			
				
					
						
							
								### Create a Resource Location in Citrix Cloud
							 

							
								resource "citrix_cloud_resource_location" "CreateCCRLForXenServer" {
							 

							
								    name = var.CC-RL-Name
							 

							
								}
							 

							
								 
							 

							
								### Create a Zone in the just created Resource Location in Citrix Cloud
							 

							
								resource "citrix_zone" "CreateZoneInCCRLForXenServer" {
							 

							
								   depends_on = [ citrix_cloud_resource_location.CreateCCRLForXenServer ]
							 

							
								    resource_location_id = citrix_cloud_resource_location.CreateCCRLForXenServer.id
							 

							
								}
							 

							
								 
							 

							
								### Create JSON-file for Cloud COnnector installer
							 

							
								resource "local_file" "CWC-Configuration" {
							 

							
								  depends_on = [ citrix_zone.CreateZoneInCCRLForXenServer ]
							 

							
								  content  = jsonencode(
							 

							
								        {
							 

							
								        "customerName"          = var.CC_CustomerID,
							 

							
								        "clientId"              = var.CC_APIKey-ClientID,
							 

							
								        "clientSecret"          = var.CC_APIKey-ClientSecret,
							 

							
								        "resourceLocationId"    = citrix_cloud_resource_location.CreateCCRLForXenServer.id
							 

							
								        "acceptTermsOfService"  = true
							 

							
								        }
							 

							
								      )
							 

							
								  filename = "${var.Install_LogPath}/DATA/cwc.json"
							 

							
								}
							 
						
					
				
			
		
	

	
		
			Important:
		 

		
			The Terraform script will pause for some time after creating the Resource Location.   
			This is because of time constraints in the back-end. 
		 

		
			Creating the Zone related to the Resource Location takes some time. We have seen delays of up to 8 minutes before the Zone was created. If the script proceeds too fast, the Zone ID readout will fail, and the Terraform script will not be able to continue successfully. 
			 
		 
	

	
		Before running Terraform, we cannot see the Resource Location and no corresponding Zone: 
		
	 

	
		
	 

	
		No Cloud Connector Software is installed on the Cloud Connector VMs: 
		
	 

	
		 
	 

	
		
			Important:
		 

		
			Before starting the Terraform workflow, please ensure you have configured the variables according to your needs. 
			 
		 
	

	
		The configuration can be started by following the standard Terraform workflow:
	 

	
		
			terraform init,
		 

		
			terraform plan
		 
	

	
		and if no errors occur
	 

	
		
			terraform apply
		 
	

	
		 
	 

	
		
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}		
		
			
				
					
						
							PS C:\_TACG\_CCRLOnXenServer-CCConfiguration&gt; terraform init 
							Initializing the backend... 
							Initializing provider plugins... 
							- Finding latest version of xenserver/xenserver... 
							- Finding latest version of hashicorp/null... 
							- Finding latest version of hashicorp/local... 
							- Finding citrix/citrix versions matching "1.0.1"... 
							- Installing xenserver/xenserver v0.1.1... 
							- Installed xenserver/xenserver v0.1.1 (self-signed, key ID FABD99A0D7648275) 
							- Installing hashicorp/null v3.2.2... 
							- Installed hashicorp/null v3.2.2 (signed by HashiCorp) 
							- Installing hashicorp/local v2.5.1... 
							- Installed hashicorp/local v2.5.1 (signed by HashiCorp) 
							- Installing citrix/citrix v1.0.1... 
							- Installed citrix/citrix v1.0.1 (self-signed, key ID BD4BD0E690CB7D88) 
							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\_CCRLOnXenServer-CCConfiguration&gt; terraform plan 
							 
							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_cloud_resource_location.CreateCCRLForXenServer will be created 
							  + resource "citrix_cloud_resource_location" "CreateCCRLForXenServer" { 
							      + id            = (known after apply) 
							      + internal_only = false 
							      + name          = "TACG-TF-RL-ONP-XenServer8N" 
							      + time_zone     = "GMT Standard Time" 
							    } 
							 
							  # citrix_zone.CreateZoneInCCRLForXenServer will be created 
							  + resource "citrix_zone" "CreateZoneInCCRLForXenServer" { 
							      + description          = (known after apply) 
							      + id                   = (known after apply) 
							      + name                 = (known after apply) 
							      + resource_location_id = (known after apply) 
							    } 
							 
						 

						
							  # local_file.WriteRLID will be created 
							  + resource "local_file" "WriteRLID" { 
							      + content              = (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) 
							      + directory_permission = "0777" 
							      + file_permission      = "0777" 
							      + filename             = "C:/TEMP/XDINST/DATA/RLID.json" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WriteZoneID will be created 
							  + resource "local_file" "WriteZoneID" { 
							      + content              = (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) 
							      + directory_permission = "0777" 
							      + file_permission      = "0777" 
							      + filename             = "C:/TEMP/XDINST/DATA/ZoneID.json" 
							      + 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) 
							    } 
							 
							  # 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.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.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: 11 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\_CCRLOnXenServer-CCConfiguration&gt; terraform validate 
							Success! The configuration is valid. 
							 
							PS C:\_TACG\_CCRLOnXenServer-CCConfiguration&gt; terraform apply 
							 
							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_cloud_resource_location.CreateCCRLForXenServer will be created 
							  + resource "citrix_cloud_resource_location" "CreateCCRLForXenServer" { 
							      + id            = (known after apply) 
							      + internal_only = false 
							      + name          = "TACG-TF-RL-ONP-XenServer8N" 
							      + time_zone     = "GMT Standard Time" 
							    } 
							 
							  # citrix_zone.CreateZoneInCCRLForXenServer will be created 
							  + resource "citrix_zone" "CreateZoneInCCRLForXenServer" { 
							      + description          = (known after apply) 
							      + id                   = (known after apply) 
							      + name                 = (known after apply) 
							      + resource_location_id = (known after apply) 
							    } 
							 
							  # local_file.WriteRLID will be created 
							  + resource "local_file" "WriteRLID" { 
							      + content              = (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) 
							      + directory_permission = "0777" 
							      + file_permission      = "0777" 
							      + filename             = "C:/TEMP/XDINST/DATA/RLID.json" 
							      + id                   = (known after apply) 
							    } 
							 
							  # local_file.WriteZoneID will be created 
							  + resource "local_file" "WriteZoneID" { 
							      + content              = (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) 
							      + directory_permission = "0777" 
							      + file_permission      = "0777" 
							      + filename             = "C:/TEMP/XDINST/DATA/ZoneID.json" 
							      + 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) 
							    } 
							 
							  # 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.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.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: 11 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_cloud_resource_location.CreateCCRLForXenServer: Creating... 
							citrix_cloud_resource_location.CreateCCRLForXenServer: Creation complete after 1s [id=a5379809-4a93-4cdf-8d5f-22443055ea6d] 
							citrix_zone.CreateZoneInCCRLForXenServer: Creating... 
							citrix_zone.CreateZoneInCCRLForXenServer: Still creating... [10s elapsed] 
							citrix_zone.CreateZoneInCCRLForXenServer: Still creating... [20s elapsed] 
							citrix_zone.CreateZoneInCCRLForXenServer: Still creating... [30s elapsed] 
							citrix_zone.CreateZoneInCCRLForXenServer: Still creating... [40s elapsed] 
							citrix_zone.CreateZoneInCCRLForXenServer: Still creating... [50s elapsed] 
							citrix_zone.CreateZoneInCCRLForXenServer: Still creating... [1m0s elapsed] 
							citrix_zone.CreateZoneInCCRLForXenServer: Still creating... [1m10s elapsed] 
							citrix_zone.CreateZoneInCCRLForXenServer: Still creating... [1m20s elapsed] 
							citrix_zone.CreateZoneInCCRLForXenServer: Still creating... [1m30s elapsed] 
							citrix_zone.CreateZoneInCCRLForXenServer: Still creating... [1m40s elapsed] 
							citrix_zone.CreateZoneInCCRLForXenServer: Still creating... [1m50s elapsed] 
							citrix_zone.CreateZoneInCCRLForXenServer: Still creating... [2m0s elapsed] 
							citrix_zone.CreateZoneInCCRLForXenServer: Still creating... [2m10s elapsed] 
							citrix_zone.CreateZoneInCCRLForXenServer: Still creating... [2m20s elapsed] 
							citrix_zone.CreateZoneInCCRLForXenServer: Still creating... [2m30s elapsed] 
							citrix_zone.CreateZoneInCCRLForXenServer: Still creating... [2m40s elapsed] 
							citrix_zone.CreateZoneInCCRLForXenServer: Still creating... [2m50s elapsed] 
							citrix_zone.CreateZoneInCCRLForXenServer: Still creating... [3m0s elapsed] 
							citrix_zone.CreateZoneInCCRLForXenServer: Still creating... [3m10s elapsed] 
							citrix_zone.CreateZoneInCCRLForXenServer: Still creating... [3m20s elapsed] 
							citrix_zone.CreateZoneInCCRLForXenServer: Still creating... [3m30s elapsed] 
							citrix_zone.CreateZoneInCCRLForXenServer: Still creating... [3m40s elapsed] 
							citrix_zone.CreateZoneInCCRLForXenServer: Creation complete after 3m44s [id=1fec47a4-c220-45cc-94a8-32ede9d9d432] 
							local_file.CWC-Configuration: Creating... 
							local_file.CWC-Configuration: Creation complete after 0s [id=00b1916cec759b8f8be328a761f25149856bb546] 
							local_file.WriteRLID: Creating... 
							local_file.WriteRLID: Creation complete after 0s [id=388de542211cb4434aa441222283feca58221223] 
							local_file.WriteZoneID: Creating... 
							local_file.WriteZoneID: Creation complete after 0s [id=258885edc58252b2a258e5f2d222122003d8ee21] 
							local_file.InstallCWCOnCC: Creating... 
							local_file.InstallCWCOnCC: Creation complete after 0s [id=d99900de2c66cc5775053cdd04ad0665e7c6e497] 
							null_resource.UploadRequiredComponentsToCC2: Creating... 
							null_resource.UploadRequiredComponentsToCC1: Creating... 
							null_resource.UploadRequiredComponentsToCC2: Provisioning with 'file'... 
							local_file.RestartCC: Creating... 
							null_resource.UploadRequiredComponentsToCC1: Provisioning with 'file'... 
							local_file.RestartCC: Creation complete after 0s [id=f10c6e1edf7fde93b8d4474f79fe1c8d3238b90a] 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadRequiredComponentsToCC1: Provisioning with 'file'... 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadRequiredComponentsToCC2: Provisioning with 'file'... 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadRequiredComponentsToCC1: Provisioning with 'file'... 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadRequiredComponentsToCC2: Provisioning with 'file'... 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadRequiredComponentsToCC1: Creation complete after 2s [id=8438202072990759444] 
							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.10.111.21 
							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 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.CallRequiredScriptsOnCC1 (remote-exec): Connected! 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
							null_resource.UploadRequiredComponentsToCC2: Creation complete after 2s [id=2899576835186673368] 
							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.10.111.22 
							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! 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
							null_resource.CallRequiredScriptsOnCC1 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/DATA/InstallCWCOnCC.ps1 
							 
							null_resource.CallRequiredScriptsOnCC2 (remote-exec): C:\Users\Administrator&gt;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.CallRequiredScriptsOnCC2: Still creating... [40s elapsed] 
							null_resource.CallRequiredScriptsOnCC2: Still creating... [50s elapsed] 
							null_resource.CallRequiredScriptsOnCC2: Still creating... [1m0s elapsed] 
							null_resource.CallRequiredScriptsOnCC2: Still creating... [1m10s elapsed] 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
							null_resource.CallRequiredScriptsOnCC2: Creation complete after 1m16s [id=5398666356552144185] 
							null_resource.CallRequiredScriptsOnCC2: Creation complete after 1m42s [id=6704034117823029160]
						 

						
							 
						 

						
							Apply complete! Resources: 11 added, 0 changed, 0 destroyed. 
							 
							PS C:\_TACG\_CCRLOnXenServer\_CCRLOnXenServer-Creation&gt;
						 
					
				
			
		
	

	
		After installing the Cloud Connector software, the Cloud Connectivity Test runs successfully: 
		
	 

	
		The Resource Location is now available: 
		
	 

	
		The Cloud Connectors successfully communicate with Citrix Cloud Infrastructure: 
		
	 

	
		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 Connector-VMs as Cloud Connectors
		
		
			Registering the 2 Cloud Connectors in the newly created Resource Location
		
	

	
		The environment is ready to deploy the Citrix Cloud-related entities. 
		 
	 

	
		Module 4: Creating all Citrix Cloud-related entities
	

	
		This module is split into the following configuration parts:
	 

	
		
			Creating a dedicated Hypervisor Connection to XenServer 8
		
		
			Creating a dedicated Hypervisor Resource Pool
		
		
			Creating a Machine Catalog based on the referenced Master Image
		
		
			Creating a Delivery Group
		
		
			Creating AutoScale settings and applying these to the newly created Delivery Group
		
	

	
		The Terraform configuration contains some idle time slots to ensure that background operations can be completed before the following configuration steps occur. 
		We have seen different elapsed configuration times related to varying loads on XenServer 8. 
	 

	
		Before Terraform can create the Hosting Connection and the Hosting Connection Pool, It must retrieve the Site ID and Zone ID of the newly created Resource Location. 
		After retrieving the IDs, Terraform configures a Hosting Connection and a Hosting Connection Pool associated with the Hypervisor Connection. 
	 

	
		Before running Terraform, no Terraform-related entities are available.
	 

	
		
			Important:
		 

		
			Before starting the Terraform workflow, please ensure you have configured the variables according to your needs. 
			 
		 
	

	
		The configuration can be started by following the standard Terraform workflow:
	 

	
		
			terraform init,
		 

		
			terraform plan
		 
	

	
		and if no errors occur
	 

	
		
			terraform apply
		 
	

	
		 
	 

	
		
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}		
		
			
				
					
						
							PS C:\_TACG\_CCRLOnXenServer-CCStuff&gt; terraform init 
							Initializing the backend... 
							Initializing provider plugins... 
							- Finding latest version of hashicorp/local... 
							- Finding latest version of xenserver/xenserver... 
							- Finding citrix/citrix versions matching "1.0.1"... 
							- Finding latest version of hashicorp/time... 
							- Installing hashicorp/time v0.12.0... 
							- Installed hashicorp/time v0.12.0 (signed by HashiCorp) 
							- Installing hashicorp/local v2.5.1... 
							- Installed hashicorp/local v2.5.1 (signed by HashiCorp) 
							- Installing xenserver/xenserver v0.1.1... 
							- Installed xenserver/xenserver v0.1.1 (self-signed, key ID FABD99A0D7648275) 
							- Installing citrix/citrix v1.0.1... 
							- Installed citrix/citrix v1.0.1 (self-signed, key ID BD4BD0E690CB7D88) 
							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\_CCRLOnXenServer-CCStuff&gt; terraform plan 
							data.local_file.LoadZoneID: Reading... 
							data.local_file.LoadZoneID: Read complete after 0s [id=41344e97bd068db0232de8a843187afe792c7e9a] 
							 
							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" 
							          + off_peak_log_off_timeout_minutes                      = 0 
							          + peak_autoscale_assigned_power_on_idle_action          = "Nothing" 
							          + peak_autoscale_assigned_power_on_idle_timeout_minutes = 0 
							          + 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" 
							          + peak_log_off_timeout_minutes                          = 0 
							          + power_off_delay_minutes                               = 30 
							          + power_time_schemes                                    = [ 
							              + { 
							                  + days_of_week          = [ 
							                      + "Friday", 
							                      + "Monday", 
							                      + "Thursday", 
							                      + "Tuesday", 
							                      + "Wednesday", 
							                    ] 
							                  + display_name          = "TACG-TF-XS8-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 
							                }, 
							            ] 
							        } 
							      + description                 = "" 
							      + desktops                    = [ 
							          + { 
							              + description             = "Terraform-based Delivery Group running on XenServer" 
							              + enable_session_roaming  = true 
							              + enabled                 = true 
							              + published_name          = "DG-TACG-TF-XS8" 
							              + restricted_access_users = { 
							                  + allow_list = [ 
							                      + "TACG\\vdaallowed", 
							                    ] 
							                } 
							            }, 
							        ] 
							      + id                          = (known after apply) 
							      + minimum_functional_level    = "L7_20" 
							      + name                        = "DG-TACG-TF-XS8" 
							      + reboot_schedules            = [ 
							          + { 
							              + days_in_week            = [ 
							                  + "Sunday", 
							                ] 
							              + frequency               = "Weekly" 
							              + frequency_factor        = 1 
							              + ignore_maintenance_mode = true 
							              + name                    = "TACG-XS8-Reboot Schedule" 
							              + natural_reboot_schedule = false 
							              + reboot_duration_minutes = 0 
							              + reboot_schedule_enabled = true 
							              + start_date              = "2024-01-01" 
							              + start_time              = "02:00" 
							                # (1 unchanged attribute hidden) 
							            }, 
							        ] 
							      + restricted_access_users     = { 
							          + allow_list = [ 
							              + "TACG\\vdaallowed", 
							            ] 
							        } 
							      + scopes                      = [] 
							      + total_machines              = (known after apply) 
							    } 
							 
							  # citrix_machine_catalog.CreateXS8MCSCatalog will be created 
							  + resource "citrix_machine_catalog" "CreateXS8MCSCatalog" { 
							      + allocation_type          = "Random" 
							      + description              = "Terraform-based Machine Catalog" 
							      + id                       = (known after apply) 
							      + minimum_functional_level = "L7_20" 
							      + name                     = "MC-TACG-TF-ONP-XenServer" 
							      + provisioning_scheme      = { 
							          + hypervisor                     = (known after apply) 
							          + hypervisor_resource_pool       = (known after apply) 
							          + identity_type                  = "ActiveDirectory" 
							          + machine_account_creation_rules = { 
							              + naming_scheme      = "TACG-TF-XS8-#" 
							              + naming_scheme_type = "Numeric" 
							            } 
							          + machine_domain_identity        = { 
							              + domain                   = "the-austrian-citrix-guy.at" 
							              + domain_ou                = "CN=Computers,DC=the-austrian-citrix-guy,DC=at" 
							              + service_account          = "Administrator" 
							              + service_account_password = (sensitive value) 
							            } 
							          + number_of_total_machines       = 1 
							        } 
							      + provisioning_type        = "MCS" 
							      + scopes                   = [] 
							      + session_support          = "SingleSession" 
							      + zone                     = "1fec47a4-c220-45cc-94a8-32ede9d9d432" 
							    } 
							 
							  # citrix_xenserver_hypervisor.CreateHypervisorConnection will be created 
							  + resource "citrix_xenserver_hypervisor" "CreateHypervisorConnection" { 
							      + addresses                                = [ 
							          + "http://10.10.111.11", 
							        ] 
							      + id                                       = (known after apply) 
							      + max_absolute_active_actions              = 40 
							      + max_absolute_new_actions_per_minute      = 10 
							      + max_power_actions_percentage_of_machines = 20 
							      + name                                     = "TACG-TF-ONP-XenServer-HypConn" 
							      + password                                 = (sensitive value) 
							      + password_format                          = "PlainText" 
							      + scopes                                   = [] 
							      + username                                 = "root" 
							      + zone                                     = "1fec47a4-c220-45cc-94a8-32ede9d9d432" 
							    } 
							 
							  # citrix_xenserver_hypervisor_resource_pool.CreateHypervisorPool will be created 
							  + resource "citrix_xenserver_hypervisor_resource_pool" "CreateHypervisorPool" { 
							      + hypervisor                = (known after apply) 
							      + id                        = (known after apply) 
							      + name                      = "TACG-TF-ONP-XenServer-HypConnPool" 
							      + networks                  = [ 
							          + "Network 0", 
							        ] 
							      + storage                   = [ 
							          + { 
							              + storage_name = "Local storage" 
							              + superseded   = false 
							            }, 
							        ] 
							      + temporary_storage         = [ 
							          + { 
							              + storage_name = "Local storage" 
							              + superseded   = false 
							            }, 
							        ] 
							      + use_local_storage_caching = false 
							    } 
							 
							  # time_sleep.wait_30_seconds will be created 
							  + resource "time_sleep" "wait_30_seconds" { 
							      + create_duration = "30s" 
							      + id              = (known after apply) 
							    } 
							 
							  # time_sleep.wait_30_seconds1 will be created 
							  + resource "time_sleep" "wait_30_seconds1" { 
							      + create_duration = "30s" 
							      + id              = (known after apply) 
							    } 
							 
							  # time_sleep.wait_30_seconds_2 will be created 
							  + resource "time_sleep" "wait_30_seconds_2" { 
							      + create_duration = "30s" 
							      + id              = (known after apply) 
							    } 
							 
							Plan: 7 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\_CCRLOnXenServer-CCStuff&gt; terraform validate 
							Success! The configuration is valid. 
							 
							PS C:\_TACG\_CCRLOnXenServer-CCStuff&gt; terraform apply 
							data.local_file.LoadZoneID: Reading... 
							data.local_file.LoadZoneID: Read complete after 0s [id=41344e97bd068db0232de8a843187afe792c7e9a] 
							 
							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" 
							          + off_peak_log_off_timeout_minutes                      = 0 
							          + peak_autoscale_assigned_power_on_idle_action          = "Nothing" 
							          + peak_autoscale_assigned_power_on_idle_timeout_minutes = 0 
							          + 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" 
							          + peak_log_off_timeout_minutes                          = 0 
							          + power_off_delay_minutes                               = 30 
							          + power_time_schemes                                    = [ 
							              + { 
							                  + days_of_week          = [ 
							                      + "Friday", 
							                      + "Monday", 
							                      + "Thursday", 
							                      + "Tuesday", 
							                      + "Wednesday", 
							                    ] 
							                  + display_name          = "TACG-TF-XS8-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 
							                }, 
							            ] 
							        } 
							      + description                 = "" 
							      + desktops                    = [ 
							          + { 
							              + description             = "Terraform-based Delivery Group running on XenServer" 
							              + enable_session_roaming  = true 
							              + enabled                 = true 
							              + published_name          = "DG-TACG-TF-XS8" 
							              + restricted_access_users = { 
							                  + allow_list = [ 
							                      + "TACG\\vdaallowed", 
							                    ] 
							                } 
							            }, 
							        ] 
							      + id                          = (known after apply) 
							      + minimum_functional_level    = "L7_20" 
							      + name                        = "DG-TACG-TF-XS8" 
							      + reboot_schedules            = [ 
							          + { 
							              + days_in_week            = [ 
							                  + "Sunday", 
							                ] 
							              + frequency               = "Weekly" 
							              + frequency_factor        = 1 
							              + ignore_maintenance_mode = true 
							              + name                    = "TACG-XS8-Reboot Schedule" 
							              + natural_reboot_schedule = false 
							              + reboot_duration_minutes = 0 
							              + reboot_schedule_enabled = true 
							              + start_date              = "2024-01-01" 
							              + start_time              = "02:00" 
							                # (1 unchanged attribute hidden) 
							            }, 
							        ] 
							      + restricted_access_users     = { 
							          + allow_list = [ 
							              + "TACG\\vdaallowed", 
							            ] 
							        } 
							      + scopes                      = [] 
							      + total_machines              = (known after apply) 
							    } 
							 
							  # citrix_machine_catalog.CreateXS8MCSCatalog will be created 
							  + resource "citrix_machine_catalog" "CreateXS8MCSCatalog" { 
							      + allocation_type          = "Random" 
							      + description              = "Terraform-based Machine Catalog" 
							      + id                       = (known after apply) 
							      + minimum_functional_level = "L7_20" 
							      + name                     = "MC-TACG-TF-ONP-XenServer" 
							      + provisioning_scheme      = { 
							          + hypervisor                     = (known after apply) 
							          + hypervisor_resource_pool       = (known after apply) 
							          + identity_type                  = "ActiveDirectory" 
							          + machine_account_creation_rules = { 
							              + naming_scheme      = "TACG-TF-XS8-#" 
							              + naming_scheme_type = "Numeric" 
							            } 
							          + machine_domain_identity        = { 
							              + domain                   = "the-austrian-citrix-guy.at" 
							              + domain_ou                = "CN=Computers,DC=the-austrian-citrix-guy,DC=at" 
							              + service_account          = "Administrator" 
							              + service_account_password = (sensitive value) 
							            } 
							          + number_of_total_machines       = 1 
							        } 
							      + provisioning_type        = "MCS" 
							      + scopes                   = [] 
							      + session_support          = "SingleSession" 
							      + zone                     = "1fec47a4-c220-45cc-94a8-32ede9d9d432" 
							    } 
							 
							  # citrix_xenserver_hypervisor.CreateHypervisorConnection will be created 
							  + resource "citrix_xenserver_hypervisor" "CreateHypervisorConnection" { 
							      + addresses                                = [ 
							          + "http://10.10.111.11", 
							        ] 
							      + id                                       = (known after apply) 
							      + max_absolute_active_actions              = 40 
							      + max_absolute_new_actions_per_minute      = 10 
							      + max_power_actions_percentage_of_machines = 20 
							      + name                                     = "TACG-TF-ONP-XenServer-HypConn" 
							      + password                                 = (sensitive value) 
							      + password_format                          = "PlainText" 
							      + scopes                                   = [] 
							      + username                                 = "root" 
							      + zone                                     = "1fec47a4-c220-45cc-94a8-32ede9d9d432" 
							    } 
							 
							  # citrix_xenserver_hypervisor_resource_pool.CreateHypervisorPool will be created 
							  + resource "citrix_xenserver_hypervisor_resource_pool" "CreateHypervisorPool" { 
							      + hypervisor                = (known after apply) 
							      + id                        = (known after apply) 
							      + name                      = "TACG-TF-ONP-XenServer-HypConnPool" 
							      + networks                  = [ 
							          + "Network 0", 
							        ] 
							      + storage                   = [ 
							          + { 
							              + storage_name = "Local storage" 
							              + superseded   = false 
							            }, 
							        ] 
							      + temporary_storage         = [ 
							          + { 
							              + storage_name = "Local storage" 
							              + superseded   = false 
							            }, 
							        ] 
							      + use_local_storage_caching = false 
							    } 
							 
							  # time_sleep.wait_30_seconds will be created 
							  + resource "time_sleep" "wait_30_seconds" { 
							      + create_duration = "30s" 
							      + id              = (known after apply) 
							    } 
							 
							  # time_sleep.wait_30_seconds1 will be created 
							  + resource "time_sleep" "wait_30_seconds1" { 
							      + create_duration = "30s" 
							      + id              = (known after apply) 
							    } 
							 
							  # time_sleep.wait_30_seconds_2 will be created 
							  + resource "time_sleep" "wait_30_seconds_2" { 
							      + create_duration = "30s" 
							      + id              = (known after apply) 
							    } 
							 
							Plan: 7 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_xenserver_hypervisor.CreateHypervisorConnection: Creating... 
							citrix_xenserver_hypervisor.CreateHypervisorConnection: Still creating... [10s elapsed] 
							citrix_xenserver_hypervisor_resource_pool. CreateHypervisorConnection: Creation complete after 12s [id=98fe321a-de44-9881-3ffe-8de239ca3412] 
							time_sleep.wait_30_seconds1: Creating... 
							time_sleep.wait_30_seconds1: Still creating... [10s elapsed] 
							time_sleep.wait_30_seconds1: Still creating... [20s elapsed] 
							time_sleep.wait_30_seconds1: Creation complete after 30s [id=2024-09-04T10:04:21Z] 
							citrix_xenserver_hypervisor_resource_pool.CreateHypervisorPool: Creating... 
							citrix_xenserver_hypervisor_resource_pool.CreateHypervisorPool: Still creating... [10s elapsed] 
							citrix_xenserver_hypervisor_resource_pool.CreateHypervisorPool: Creation complete after 12s [id=523b3d3e-daf8-41d4-819f-254602bc47db] 
							time_sleep.wait_30_seconds1: Creating... 
							time_sleep.wait_30_seconds1: Still creating... [10s elapsed] 
							time_sleep.wait_30_seconds1: Still creating... [20s elapsed] 
							time_sleep.wait_30_seconds1: Creation complete after 30s [id=2024-09-04T10:06:53Z] 
							citrix_machine_catalog.CreateXS8MCSCatalog: Creating... 
							citrix_machine_catalog.CreateXS8MCSCatalog: Still creating... [10s elapsed] 
							citrix_machine_catalog.CreateXS8MCSCatalog: Still creating... [20s elapsed] 
							citrix_machine_catalog.CreateXS8MCSCatalog: Still creating... [30s elapsed] 
							citrix_machine_catalog.CreateXS8MCSCatalog: Still creating... [40s elapsed] 
							citrix_machine_catalog.CreateXS8MCSCatalog: Still creating... [50s elapsed] 
							citrix_machine_catalog.CreateXS8MCSCatalog: Still creating... [1m0s elapsed] 
							citrix_machine_catalog.CreateXS8MCSCatalog: Still creating... [1m10s elapsed] 
							citrix_machine_catalog.CreateXS8MCSCatalog: Still creating... [1m20s elapsed] 
							citrix_machine_catalog.CreateXS8MCSCatalog: Still creating... [1m30s elapsed] 
							citrix_machine_catalog.CreateXS8MCSCatalog: Still creating... [1m40s elapsed] 
							citrix_machine_catalog.CreateXS8MCSCatalog: Still creating... [1m50s elapsed] 
							citrix_machine_catalog.CreateXS8MCSCatalog: Still creating... [2m0s elapsed] 
							citrix_machine_catalog.CreateXS8MCSCatalog: Still creating... [2m10s elapsed] 
							citrix_machine_catalog.CreateXS8MCSCatalog: Still creating... [2m20s elapsed] 
							citrix_machine_catalog.CreateXS8MCSCatalog: Still creating... [2m30s elapsed] 
							citrix_machine_catalog.CreateXS8MCSCatalog: Still creating... [2m40s elapsed] 
							citrix_machine_catalog.CreateXS8MCSCatalog: Still creating... [2m50s elapsed] 
							citrix_machine_catalog.CreateXS8MCSCatalog: Still creating... [3m0s elapsed] 
							citrix_machine_catalog.CreateXS8MCSCatalog: Still creating... [3m10s elapsed] 
							citrix_machine_catalog.CreateXS8MCSCatalog: Still creating... [3m20s elapsed] 
							citrix_machine_catalog.CreateXS8MCSCatalog: Still creating... [3m30s elapsed] 
							citrix_machine_catalog.CreateXS8MCSCatalog: Creation complete after 3m34s [id=01b58785-ec35-4add-bb97-37b595972209] 
							time_sleep.wait_30_seconds_2: Creating... 
							time_sleep.wait_30_seconds_2: Still creating... [10s elapsed] 
							time_sleep.wait_30_seconds_2: Still creating... [20s elapsed] 
							time_sleep.wait_30_seconds_2: Still creating... [30s elapsed] 
							time_sleep.wait_30_seconds_2: Creation complete after 30s [id=2024-09-04T10:10:57Z] 
							citrix_delivery_group.CreateDG: Creating... 
							citrix_delivery_group.CreateDG: Creation complete after 8s [id=e23ce5c7-4d92-4d6a-b623-8063ef4770be] 
							 
							Apply complete! Resources: 7 added, 0 changed, 0 destroyed. 
							PS C:\_TACG\_CCRLOnXenServer-CCStuff&gt;
						 
					
				
			
		
	

	
		Terraform successfully created the Hosting Connection and a Hosting Connection Pool associated with the Hosting Connection: 
		 
		 
		
	 

	
		Terraform successfully created the Machine Catalog and a Worker VM: 
		
	 

	
		 
		
	 

	
		 
		
	 

	
		Terraform successfully created the Delivery Group: 
		
	 

	
		Terraform successfully created the AutoScale settings: 
		
	 

	
		The Delivery Group is ready to start the Worker VMs: 
		
	 

	
		That concludes the guide "Using Terraform to Deploy a Citrix DaaS Resource Location on XenServer 8.
	 

	
		 
	 

	
		Appendix
	

	
		
			Disclaimer
		 

		
			EXCEPT WHERE EXPRESSLY PROVIDED OTHERWISE BY CLOUD SOFTWARE GROUP, THE CONTENT IN THIS DOCUMENT IS PROVIDED “AS IS” AND CLOUD SOFTWARE GROUP HEREBY DISCLAIMS ALL EXPRESS OR IMPLIED REPRESENTATIONS, WARRANTIES, GUARANTIES, AND CONDITIONS, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES OR CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD TO BE LEGALLY INVALID. CLOUD SOFTWARE GROUP MAKES NO REPRESENTATIONS, WARRANTIES, GUARANTIES, OR CONDITIONS AS TO THE QUALITY, SUITABILITY, TRUTH, ACCURACY, OR COMPLETENESS OF ANY OF THE CONTENT CONTAINED ON THE WEBSITE.
		 

		
			The above disclaimer applies to any damages, liability, or injuries caused by any failure of performance, error, omission, interruption, deletion, defect, delay in operation or transmission, computer virus, communication line failure, theft or destruction of or unauthorized access to, alteration of, or use, whether for breach of contract, tort, negligence or any other cause of action. 
			 
		 
	

	
		Module 1: _CCRLOnXenServer-Creation
	

	
		These are the Terraform configuration files for Module 1 (excerpts):
	 

	
		CCRLOnXenServer-Create.tf
	

	
		
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}		
		
			
				
					
						
							
								
									# Terraform deployment of a Citrix DaaS Resource Location on XenServer 8
								

								
									## Definition of all required local variables
								

								
									### Get XenServer-related Data
								

								
									#### Get the SR object
								

								
									data "xenserver_sr" "sr" {
								

								
									  name_label = "Local storage"
								

								
									}
								
								 

								
									#### Get the Network object
								

								
									data "xenserver_network" "network" {
								

								
									}
								
								 

								
									#### Create a local ISO repository
								

								
									resource "xenserver_sr" "local" {
								

								
									  name_label       = "ISO on TACG-DCN"
								

								
									  name_description = "ISO repository on TACG-DC"
								

								
									  type             = "iso"
								

								
									  content_type     = "ISOs"
								

								
									  shared           = true
								

								
									  device_config = {
								

								
									    username = "************@the-austrian-citrix-guy.at"
								

								
									    cifspassword = "************"
								

								
									    location = "\\\\tacg-dc.the-austrian-citrix-guy.at\\_sw"
								

								
									    type = "cifs"
								

								
									    vers = "1.0" 
								

								
									  }
								

								
									  sm_config = {
								

								
									    "iso_type" = "cifs"
								

								
									  } 
								

								
									}
								
								 

								
									output "local_storage_output" {
								

								
									  depends_on = [ xenserver_sr.local ]
								

								
									  value = data.xenserver_sr.sr.data_items
								

								
									}
								
								 

								
									output "network_output" {
								

								
									    depends_on = [ xenserver_sr.local ]
								

								
									  value = data.xenserver_network.network.data_items
								

								
									}
								
								 

								
									#### Set local variables for creating multiple VMs
								

								
									locals {
								

								
									  virtual_machines = {
								

								
									    "windows-vm" = {
								

								
									      name_label       = var.CC1-VM-Name
								

								
									      description      = var.CC1-VM-Description
								

								
									      template_name    = var.XS-TemplateName
								

								
									      static_mem_max   = var.CC1-VM-RAM * 1024 * 1024
								

								
									      vcpus            = var.CC1-VM-CPU
								

								
									      cores_per_socket = 2
								

								
									      cdrom            = var.XS-Image-ISO
								

								
									      network_interface = [
								

								
									        {
								

								
									          network_uuid = data.xenserver_network.network.data_items[0].uuid,
								

								
									          device       = "0"
								

								
									        },
								

								
									      ]
								

								
									    }
								

								
									    "windows-vm1" = {
								

								
									      name_label       = var.CC2-VM-Name
								

								
									      description      = var.CC2-VM-Description
								

								
									      template_name    = var.XS-TemplateName
								

								
									      static_mem_max   = var.CC2-VM-RAM * 1024 * 1024
								

								
									      vcpus            = var.CC2-VM-CPU
								

								
									      cores_per_socket = 2
								

								
									      cdrom            = var.XS-Image-ISO
								

								
									      network_interface = [
								

								
									        {
								

								
									          network_uuid = data.xenserver_network.network.data_items[0].uuid,
								

								
									          device       = "0"
								
								 

								
									        },
								

								
									      ]
								

								
									    }
								

								
									    "windows-vm2" = {
								

								
									      name_label       = var.AVM-VM-Name
								

								
									      description      = var.AVM-VM-Description
								

								
									      template_name    = var.XS-TemplateName
								

								
									      static_mem_max   = var.AVM-VM-RAM * 1024 * 1024
								

								
									      vcpus            = var.AVM-VM-CPU
								

								
									      cores_per_socket = 2
								

								
									      cdrom            = var.XS-Image-ISO
								

								
									      network_interface = [
								

								
									        {
								

								
									          network_uuid = data.xenserver_network.network.data_items[0].uuid,
								

								
									          device       = "0"
								

								
									        },
								

								
									      ]
								

								
									    }
								

								
									  }
								

								
									}
								
								 

								
									resource "xenserver_vm" "vm" {
								

								
									    depends_on = [ xenserver_sr.local ]
								

								
									  for_each          = local.virtual_machines
								

								
									  name_label        = each.value.name_label
								

								
									  template_name     = each.value.template_name 
								

								
									  static_mem_max    = each.value.static_mem_max
								

								
									  vcpus             = each.value.vcpus
								

								
									  cores_per_socket  = 2
								

								
									  cdrom              = var.XS-Image-ISO
								

								
									  network_interface = each.value.network_interface
								

								
									}
								
								 

								
									output "vm_out" {
								

								
									  value = {
								

								
									    for vm in xenserver_vm.vm : vm.name_label =&gt; vm
								

								
									  }
								

								
									}
								
								 

								
									######################################################################################
								

								
									###  All VMs for creating a Resource Location on Nutanix should have been created  ###
								

								
									######################################################################################  
								
							
						
					
				
			
		
	

	
		 
	 

	
		Module 2: _CCRLOnXenServer-InitialConfiguration
	

	
		These are the Terraform configuration files for Module 2 (excerpts):
	 

	
		CCRLOnXenServer-InitialConfiguration.tf
	

	
		
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}		
		
			
				
					
						
							
								
									# Terraform deployment of a Citrix DaaS Resource Location on XenServer 8
								

								
									## Definition of all required local variables
								

								
									### Get XenServer-related Data
								

								
									#### Get the SR object
								

								
									data "xenserver_sr" "sr" {
								

								
									  name_label = "Local storage"
								

								
									}
								
								 

								
									#### Get the Network object
								

								
									data "xenserver_network" "network" {
								

								
									}
								
								 

								
									#### Get the VM objects
								

								
									data "xenserver_vm" "vm_data" {
								

								
									}
								
								 

								
									locals {
								

								
									#### Create further Configuration scripts
								

								
									##### Create Installation directory
								

								
									InstallLogPath     = &lt;&lt;-EOT
								

								
									New-Item -Path ${var.Install_LogPath} -ItemType Directory
								

								
									EOT
								
								 

								
									##### Create IP-Setting script for CC1
								

								
									SetIPForCC1     = &lt;&lt;-EOT
								

								
									$Adapter = Get-NetAdapter | ? {$_.Status -eq "up"}
								

								
									$IFIndx = $Adapter.InterfaceIndex
								

								
									$IpAddress = '${var.CC1-VM-IP-Final}'
								

								
									$Prefix = '${var.VM-IP-Prefix}'
								

								
									$GW = '${var.VM-IP-GW}'
								

								
									$DNS = '${var.VM-IP-DNS}'
								

								
									$IPType = '${var.VM-IP-Type}'
								
								 

								
									$adapter | New-NetIPAddress -AddressFamily $IPType -IPAddress $IpAddress -PrefixLength $Prefix -DefaultGateway $GW
								

								
									$adapter | Set-DnsClientServerAddress -ServerAddresses $DNS
								
								 

								
									Start-Sleep -Seconds 10
								
								 

								
									$ADN = '${var.DomainName}'
								

								
									$APSUsername = '${var.DomainAdmin-UN}'
								

								
									$APSPassword = '${var.DomainAdmin-PW}'
								

								
									$APSSecurePassword = ConvertTo-SecureString $APSPassword -AsPlainText -Force
								

								
									$APSCredential = New-Object System.Management.Automation.PSCredential ($APSUsername, $APSSecurePassword)
								

								
									Add-Computer -DomainName $ADN -Credential $APSCredential -Restart -Force
								

								
									EOT 
								
								 

								
									##### Create IP-Setting script for CC2
								

								
									SetIPForCC2     = &lt;&lt;-EOT
								

								
									$Adapter = Get-NetAdapter | ? {$_.Status -eq "up"}
								

								
									$IFIndx = $Adapter.InterfaceIndex
								

								
									$IpAddress = '${var.CC2-VM-IP-Final}'
								

								
									$Prefix = '${var.VM-IP-Prefix}'
								

								
									$GW = '${var.VM-IP-GW}'
								

								
									$DNS = '${var.VM-IP-DNS}'
								

								
									$IPType = '${var.VM-IP-Type}'
								
								 

								
									$adapter | New-NetIPAddress -AddressFamily $IPType -IPAddress $IpAddress -PrefixLength $Prefix -DefaultGateway $GW
								

								
									$adapter | Set-DnsClientServerAddress -ServerAddresses $DNS
								
								 

								
									Start-Sleep -Seconds 10
								
								 

								
									$ADN = '${var.DomainName}'
								

								
									$APSUsername = '${var.DomainAdmin-UN}'
								

								
									$APSPassword = '${var.DomainAdmin-PW}'
								

								
									$APSSecurePassword = ConvertTo-SecureString $APSPassword -AsPlainText -Force
								

								
									$APSCredential = New-Object System.Management.Automation.PSCredential ($APSUsername, $APSSecurePassword)
								

								
									Add-Computer -DomainName $ADN -Credential $APSCredential -Restart -Force
								

								
									EOT 
								
								 

								
									##### Create IP-Setting script for AVM
								

								
									SetIPForAVM     = &lt;&lt;-EOT
								

								
									$Adapter = Get-NetAdapter | ? {$_.Status -eq "up"}
								

								
									$IFIndx = $Adapter.InterfaceIndex
								

								
									$IpAddress = '${var.AVM-VM-IP-Final}'
								

								
									$Prefix = '${var.VM-IP-Prefix}'
								

								
									$GW = '${var.VM-IP-GW}'
								

								
									$DNS = '${var.VM-IP-DNS}'
								

								
									$IPType = '${var.VM-IP-Type}'
								
								 

								
									$adapter | New-NetIPAddress -AddressFamily $IPType -IPAddress $IpAddress -PrefixLength $Prefix -DefaultGateway $GW
								

								
									$adapter | Set-DnsClientServerAddress -ServerAddresses $DNS
								
								 

								
									Start-Sleep -Seconds 10
								
								 

								
									$ADN = '${var.DomainName}'
								

								
									$APSUsername = '${var.DomainAdmin-UN}'
								

								
									$APSPassword = '${var.DomainAdmin-PW}'
								

								
									$APSSecurePassword = ConvertTo-SecureString $APSPassword -AsPlainText -Force
								

								
									$APSCredential = New-Object System.Management.Automation.PSCredential ($APSUsername, $APSSecurePassword)
								

								
									Add-Computer -DomainName $ADN -Credential $APSCredential -Restart -Force
								

								
									EOT 
								
								 

								
									##### Create generic Reboot script
								

								
									RebootScript     = &lt;&lt;-EOT
								

								
									Restart-Computer -Force
								

								
									EOT
								
								 

								
									##### Create RenameComputer script for CC1
								

								
									RenameCC1     = &lt;&lt;-EOT
								

								
									$PSUsername = '${var.Provisioner_Admin-Username}'
								

								
									$PSPassword = '${var.Provisioner_Admin-Password}'
								

								
									$CN = '${var.CC1-VM-ADName}'
								

								
									$PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
								

								
									$PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
								

								
									Rename-Computer -NewName $CN -LocalCredential $PSCredential -Restart -Force
								

								
									EOT
								
								 

								
									##### Create RenameComputer script for CC2
								

								
									RenameCC2     = &lt;&lt;-EOT
								

								
									$PSUsername = '${var.Provisioner_Admin-Username}'
								

								
									$PSPassword = '${var.Provisioner_Admin-Password}'
								

								
									$CN = '${var.CC2-VM-ADName}'
								

								
									$PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
								

								
									$PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
								

								
									Rename-Computer -NewName $CN -LocalCredential $PSCredential -Restart -Force
								

								
									EOT
								
								 

								
									##### Create RenameComputer script for AVM
								

								
									RenameAVM     = &lt;&lt;-EOT
								

								
									$PSUsername = '${var.Provisioner_Admin-Username}'
								

								
									$PSPassword = '${var.Provisioner_Admin-Password}'
								

								
									$CN = '${var.AVM-VM-ADName}'
								

								
									$PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
								

								
									$PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
								

								
									Rename-Computer -NewName $CN -LocalCredential $PSCredential -Restart -Force
								

								
									EOT
								
								 

								
									##### Create generic AddToDomain script
								

								
									AddToDomain     = &lt;&lt;-EOT
								

								
									$DN = '${var.DomainName}'
								

								
									$PSUsername = '${var.DomainAdmin-UN}'
								

								
									$PSPassword = '${var.DomainAdmin-PW}'
								

								
									$PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
								

								
									$PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
								

								
									Add-Computer -DomainName $DN -Credential $PSCredential -Restart -Force
								

								
									EOT
								

								
									}
								
								 

								
									#### Write CC1-IP-Script into local data-directory
								

								
									resource "local_file" "WriteCC1IPScriptIntoDataDirectory" {
								

								
									  filename = "${path.module}/data/CC1-IP-Script.ps1"
								

								
									  content  = local.SetIPForCC1
								

								
									}
								
								 

								
									#### Write CC2-IP-Script into local data-directory
								

								
									resource "local_file" "WriteCC2IPScriptIntoDataDirectory" {
								

								
									  filename = "${path.module}/data/CC2-IP-Script.ps1"
								

								
									  content  = local.SetIPForCC2
								

								
									}
								
								 

								
									#### Write AVM-IP-Script into local data-directory
								

								
									resource "local_file" "WriteAVMIPScriptIntoDataDirectory" {
								

								
									  filename = "${path.module}/data/AVM-IP-Script.ps1"
								

								
									  content  = local.SetIPForAVM
								

								
									}
								
								 

								
									#### Write Reboot-Script into local data-directory
								

								
									resource "local_file" "WriteRebootScriptIntoDataDirectory" {
								

								
									  filename = "${path.module}/data/Reboot-Script.ps1"
								

								
									  content  = local.RebootScript
								

								
									}
								
								 

								
									#### Write RenameComputer-CC1-Script into local data-directory
								

								
									resource "local_file" "WriteRenameComputerCC1ScriptIntoDataDirectory" {
								

								
									  filename = "${path.module}/data/RenameComputer-CC1-Script.ps1"
								

								
									  content  = local.RenameCC1
								

								
									}
								
								 

								
									#### Write RenameComputer-CC2-Script into local data-directory
								

								
									resource "local_file" "WriteRenameComputerCC2ScriptIntoDataDirectory" {
								

								
									  filename = "${path.module}/data/RenameComputer-CC2-Script.ps1"
								

								
									  content  = local.RenameCC2
								

								
									}
								
								 

								
									#### Write RenameComputer-AVM-Script into local data-directory
								

								
									resource "local_file" "WriteRenameComputerAVMScriptIntoDataDirectory" {
								

								
									  filename = "${path.module}/data/RenameComputer-AVM-Script.ps1"
								

								
									  content  = local.RenameAVM
								

								
									}
								
								 

								
									#### Write generic Domain-Script into local data-directory
								

								
									resource "local_file" "WriteAddToDomainScriptIntoDataDirectory" {
								

								
									  filename = "${path.module}/data/AddToDomain-Script.ps1"
								

								
									  content  = local.AddToDomain
								

								
									}
								
								 

								
									#### Write InstallationPath-Script into local data-directory
								

								
									resource "local_file" "WriteInstallationScriptIntoDataDirectory" {
								

								
									  filename = "${path.module}/data/InstallationLogPath-Script.ps1"
								

								
									  content  = local.InstallLogPath
								

								
									}
								
								 

								
									#### Upload Initial Configuration scripts to CC1 and execute them
								

								
									###### Set the Provisioner-Connection
								

								
									resource "null_resource" "UploadAndExecuteScripts-CC1" {
								

								
									  depends_on = [ local_file.WriteCC1IPScriptIntoDataDirectory,  local_file.WriteRebootScriptIntoDataDirectory,  local_file.WriteAddToDomainScriptIntoDataDirectory, local_file.WriteInstallationScriptIntoDataDirectory, local_file.WriteRenameComputerCC1ScriptIntoDataDirectory ]
								

								
									  connection {
								

								
									    type            = var.Provisioner_Type
								

								
									    user            = var.Provisioner_Admin-Username
								

								
									    password        = var.Provisioner_Admin-Password
								

								
									    host            = var.CC1-VM-IP-PreConfig
								

								
									    timeout         = var.Provisioner_Timeout
								
								 

								
									  }
								
								 

								
									###### Upload Installation-Path script to CC1
								

								
									  provisioner "file" {
								

								
									    source      = "${path.module}/data/InstallationLogPath-Script.ps1"
								

								
									    destination = "C:/temp/InstallationLogPath-Script.ps1"
								

								
									    
								

								
									  }
								
								 

								
									###### Execute the Installation-Path script
								

								
									 provisioner "remote-exec" {
								

								
									    inline = [
								

								
									      "powershell -File C:/temp/InstallationLogPath-Script.ps1"
								

								
									    ]
								

								
									  }
								

								
									}
								
								 

								
									#### Upload Initial Configuration scripts to CC2 and execute them
								

								
									##### Set the Provisioner-Connection
								

								
									resource "null_resource" "UploadAndExecuteScripts-CC2" {
								

								
									 depends_on = [ local_file.WriteCC2IPScriptIntoDataDirectory,  local_file.WriteRebootScriptIntoDataDirectory,  local_file.WriteAddToDomainScriptIntoDataDirectory, local_file.WriteInstallationScriptIntoDataDirectory, local_file.WriteRenameComputerCC2ScriptIntoDataDirectory ]
								

								
									  connection {
								

								
									    type            = var.Provisioner_Type
								

								
									    user            = var.Provisioner_Admin-Username
								

								
									    password        = var.Provisioner_Admin-Password
								

								
									    host            = var.CC2-VM-IP-PreConfig
								

								
									    timeout         = var.Provisioner_Timeout
								
								 

								
									  }
								
								 

								
									##### Upload Installation-Path script to CC2
								

								
									  provisioner "file" {
								

								
									    source      = "${path.module}/data/InstallationLogPath-Script.ps1"
								

								
									    destination = "C:/temp/InstallationLogPath-Script.ps1"
								

								
									    
								

								
									  }
								
								 

								
									##### Execute the Installation-Path script
								

								
									 provisioner "remote-exec" {
								

								
									    inline = [
								

								
									      "powershell -File C:/temp/InstallationLogPath-Script.ps1"
								

								
									    ]
								

								
									  }
								

								
									}
								
								 

								
									#### Upload Initial Configuration scripts to AVM and execute them
								

								
									###### Set the Provisioner-Connection
								

								
									resource "null_resource" "UploadAndExecuteScripts-AVM" {
								

								
									 depends_on = [ local_file.WriteAVMIPScriptIntoDataDirectory,  local_file.WriteRebootScriptIntoDataDirectory,  local_file.WriteAddToDomainScriptIntoDataDirectory, local_file.WriteInstallationScriptIntoDataDirectory, local_file.WriteRenameComputerAVMScriptIntoDataDirectory ]
								

								
									  connection {
								

								
									    type            = var.Provisioner_Type
								

								
									    user            = var.Provisioner_Admin-Username
								

								
									    password        = var.Provisioner_Admin-Password
								

								
									    host            = var.AVM-VM-IP-PreConfig
								

								
									    timeout         = var.Provisioner_Timeout
								
								 

								
									  }
								
								 

								
									###### Upload Installation-Path script to AVM
								

								
									  provisioner "file" {
								

								
									    source      = "${path.module}/data/InstallationLogPath-Script.ps1"
								

								
									    destination = "C:/temp/InstallationLogPath-Script.ps1"
								

								
									    
								

								
									  }
								
								 

								
									###### Execute the Installation-Path script
								

								
									 provisioner "remote-exec" {
								

								
									    inline = [
								

								
									      "powershell -File C:/temp/InstallationLogPath-Script.ps1"
								

								
									    ]
								

								
									  }
								

								
									}
								
								 

								
									#### Upload RenameComputer script to CC1 and execute them
								

								
									###### Set the Provisioner-Connection
								

								
									resource "null_resource" "UploadAndExecuteRenameScripts-CC1" {
								

								
									  depends_on = [ null_resource.UploadAndExecuteScripts-CC1  ]
								

								
									  connection {
								

								
									    type            = var.Provisioner_Type
								

								
									    user            = var.Provisioner_Admin-Username
								

								
									    password        = var.Provisioner_Admin-Password
								

								
									    host            = var.CC1-VM-IP-PreConfig
								

								
									    timeout         = var.Provisioner_Timeout
								
								 

								
									  }
								
								 

								
									###### Upload RenameComputer script to CC1
								

								
									  provisioner "file" {
								

								
									    source      = "${path.module}/data/RenameComputer-CC1-Script.ps1"
								

								
									    destination = "${var.Install_LogPath}/RenameComputer-CC1-Script.ps1"
								

								
									    
								

								
									  }
								
								 

								
									###### Execute the RenameComputer script
								

								
									 provisioner "remote-exec" {
								

								
									    inline = [
								

								
									      "powershell -File ${var.Install_LogPath}/RenameComputer-CC1-Script.ps1"
								

								
									    ]
								

								
									  }
								

								
									}
								
								 

								
									#### Upload RenameComputer script to CC2 and execute them
								

								
									###### Set the Provisioner-Connection
								

								
									resource "null_resource" "UploadAndExecuteRenameScripts-CC2" {
								

								
									  depends_on = [ null_resource.UploadAndExecuteScripts-CC2  ]
								

								
									  connection {
								

								
									    type            = var.Provisioner_Type
								

								
									    user            = var.Provisioner_Admin-Username
								

								
									    password        = var.Provisioner_Admin-Password
								

								
									    host            = var.CC2-VM-IP-PreConfig
								

								
									    timeout         = var.Provisioner_Timeout
								
								 

								
									  }
								
								 

								
									###### Upload RenameComputer script to CC2
								

								
									  provisioner "file" {
								

								
									    source      = "${path.module}/data/RenameComputer-CC2-Script.ps1"
								

								
									    destination = "${var.Install_LogPath}/RenameComputer-CC2-Script.ps1"
								

								
									    
								

								
									  }
								
								 

								
									###### Execute the RenameComputer script
								

								
									 provisioner "remote-exec" {
								

								
									    inline = [
								

								
									      "powershell -File ${var.Install_LogPath}/RenameComputer-CC2-Script.ps1"
								

								
									    ]
								

								
									  }
								

								
									}
								
								 

								
									#### Upload RenameComputer script to AVM and execute them
								

								
									###### Set the Provisioner-Connection
								

								
									resource "null_resource" "UploadAndExecuteRenameScripts-AVM" {
								

								
									  depends_on = [ null_resource.UploadAndExecuteScripts-AVM  ]
								

								
									  connection {
								

								
									    type            = var.Provisioner_Type
								

								
									    user            = var.Provisioner_Admin-Username
								

								
									    password        = var.Provisioner_Admin-Password
								

								
									    host            = var.AVM-VM-IP-PreConfig
								

								
									    timeout         = var.Provisioner_Timeout
								
								 

								
									  }
								
								 

								
									###### Upload RenameComputer script to AVM
								

								
									  provisioner "file" {
								

								
									    source      = "${path.module}/data/RenameComputer-AVM-Script.ps1"
								

								
									    destination = "${var.Install_LogPath}/RenameComputer-AVM-Script.ps1"
								

								
									    
								

								
									  }
								
								 

								
									###### Execute the RenameComputer script
								

								
									 provisioner "remote-exec" {
								

								
									    inline = [
								

								
									      "powershell -File ${var.Install_LogPath}/RenameComputer-AVM-Script.ps1"
								

								
									    ]
								

								
									  }
								

								
									}
								
								 

								
									#### Wait for 1 minute to complete reboot and settling of all rename tasks on VMs
								

								
									resource "time_sleep" "WaitToRebootCC1AfterRename" {
								

								
									  depends_on = [ null_resource.UploadAndExecuteScripts-CC1 ]
								

								
									  create_duration = "60s"
								

								
									} 
								
								 

								
									resource "time_sleep" "WaitToRebootCC2AfterRename" {
								

								
									  depends_on = [ null_resource.UploadAndExecuteScripts-CC2 ]
								

								
									  create_duration = "60s"
								

								
									} 
								
								 

								
									resource "time_sleep" "WaitToRebootAVMAfterRename" {
								

								
									  depends_on = [ null_resource.UploadAndExecuteRenameScripts-AVM ]
								

								
									  create_duration = "60s"
								

								
									}
								
								 

								
									#### Upload Change-IP-Script to CC1 and execute them
								

								
									###### Set the Provisioner-Connection
								

								
									resource "null_resource" "UploadAndExecuteChangeIPScript-CC1" {
								

								
									  #depends_on = [ time_sleep.WaitToRebootCC1AfterRename  ]
								

								
									  connection {
								

								
									    type            = var.Provisioner_Type
								

								
									    user            = var.Provisioner_Admin-Username
								

								
									    password        = var.Provisioner_Admin-Password
								

								
									    host            = var.CC1-VM-IP-PreConfig
								

								
									    timeout         = var.Provisioner_Timeout
								
								 

								
									  }
								
								 

								
									###### Upload Change-IP-Script script to CC1
								

								
									  provisioner "file" {
								

								
									    source      = "${path.module}/data/CC1-IP-Script.ps1"
								

								
									    destination = "${var.Install_LogPath}/CC1-IP-Script.ps1"
								

								
									    
								

								
									  }
								
								 

								
									###### Execute the Change-IP-Script script
								

								
									 provisioner "remote-exec" {
								

								
									    inline = [
								

								
									      "powershell -File ${var.Install_LogPath}/CC1-IP-Script.ps1"
								

								
									    ]
								

								
									  }
								

								
									}
								
								 

								
									#### Upload Change-IP-Script to CC2 and execute them
								

								
									###### Set the Provisioner-Connection
								

								
									resource "null_resource" "UploadAndExecuteChangeIPScript-CC2" {
								

								
									  #depends_on = [ time_sleep.WaitToRebootCC2AfterRename ]
								

								
									  connection {
								

								
									    type            = var.Provisioner_Type
								

								
									    user            = var.Provisioner_Admin-Username
								

								
									    password        = var.Provisioner_Admin-Password
								

								
									    host            = var.CC2-VM-IP-PreConfig
								

								
									    timeout         = var.Provisioner_Timeout
								
								 

								
									  }
								
								 

								
									###### Upload Change-IP-Script script to CC2
								

								
									  provisioner "file" {
								

								
									    source      = "${path.module}/data/CC2-IP-Script.ps1"
								

								
									    destination = "${var.Install_LogPath}/CC2-IP-Script.ps1"
								

								
									    
								

								
									  }
								
								 

								
									###### Execute the Change-IP-Script script
								

								
									 provisioner "remote-exec" {
								

								
									    inline = [
								

								
									      "powershell -File ${var.Install_LogPath}/CC2-IP-Script.ps1"
								

								
									    ]
								

								
									  }
								

								
									}
								
								 

								
									#### Upload Change-IP-Script to AVM and execute them
								

								
									###### Set the Provisioner-Connection
								

								
									resource "null_resource" "UploadAndExecuteChangeIPScript-AVM" {
								

								
									  #depends_on = [ time_sleep.WaitToRebootAVMAfterRename  ]
								

								
									  connection {
								

								
									    type            = var.Provisioner_Type
								

								
									    user            = var.Provisioner_Admin-Username
								

								
									    password        = var.Provisioner_Admin-Password
								

								
									    host            = var.AVM-VM-IP-PreConfig
								

								
									    timeout         = var.Provisioner_Timeout
								
								 

								
									  }
								
								 

								
									###### Upload Change-IP-Script script to AVM
								

								
									  provisioner "file" {
								

								
									    source      = "${path.module}/data/AVM-IP-Script.ps1"
								

								
									    destination = "${var.Install_LogPath}/AVM-IP-Script.ps1"
								

								
									    
								

								
									  }
								
								 

								
									###### Execute the Change-IP-Script script
								

								
									 provisioner "remote-exec" {
								

								
									    inline = [
								

								
									      "powershell -File ${var.Install_LogPath}/AVM-IP-Script.ps1"
								

								
									    ]
								

								
									  }
								

								
									}
								
								 

								
									#### Wait for 1 minute to complete IP change tasks on VMs
								

								
									resource "time_sleep" "WaitToRebootCC1AfterIPChange" {
								

								
									  depends_on = [ null_resource.UploadAndExecuteChangeIPScript-CC1 ]
								

								
									  create_duration = "60s"
								

								
									} 
								
								 

								
									resource "time_sleep" "WaitToRebootCC2AfterIPChange" {
								

								
									  depends_on = [ null_resource.UploadAndExecuteChangeIPScript-CC2 ]
								

								
									  create_duration = "60s"
								

								
									} 
								
								 

								
									resource "time_sleep" "WaitToRebootAVMAfterIPChange" {
								

								
									  depends_on = [ null_resource.UploadAndExecuteChangeIPScript-AVM ]
								

								
									  create_duration = "60s"
								

								
									} 
								
								 
								 
								 
								
									########################################################################################
								

								
									###  All VMs for creating a Resource Location on XenServer should have been created  ###
								

								
									######################################################################################## 
								
							
						
					
				
			
		
	

	
		 
	 

	
		Module 3: _CCRLOnXenServer-CCConfiguration
	

	
		These are the Terraform configuration files for Module 3 (excerpts):
	 

	
		CCRLOnXenServer-CCConfiguration.tf
	

	
		
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}		
		
			
				
					
						
							
								
									# Terraform deployment of a Citrix DaaS Resource Location on XenServer 8
								

								
									## Definition of all required local variables
								

								
									### Create a Resource Location in Citrix Cloud
								

								
									resource "citrix_cloud_resource_location" "CreateCCRLForXenServer" {
								

								
									    name = var.CC-RL-Name
								

								
									}
								
								 

								
									### Create a Zone in the just created Resource Location in Citrix Cloud
								

								
									resource "citrix_zone" "CreateZoneInCCRLForXenServer" {
								

								
									  depends_on = [ citrix_cloud_resource_location.CreateCCRLForXenServer ]
								

								
									    resource_location_id = citrix_cloud_resource_location.CreateCCRLForXenServer.id
								

								
									}
								
								 

								
									### Create JSON-file for Cloud COnnector installer
								

								
									resource "local_file" "CWC-Configuration" {
								

								
									  depends_on = [ citrix_zone.CreateZoneInCCRLForXenServer ]
								

								
									  content  = jsonencode(
								

								
									        {
								

								
									        "customerName"          = var.CC_CustomerID,
								

								
									        "clientId"              = var.CC_APIKey-ClientID,
								

								
									        "clientSecret"          = var.CC_APIKey-ClientSecret,
								

								
									        "resourceLocationId"    = citrix_cloud_resource_location.CreateCCRLForXenServer.id
								

								
									        "acceptTermsOfService"  = true
								

								
									        }
								

								
									      )
								

								
									  filename = "${var.Install_LogPath}/DATA/cwc.json"
								

								
									}
								
								 

								
									### Create file with RLID
								

								
									resource "local_file" "WriteRLID" {
								

								
									  depends_on = [ citrix_zone.CreateZoneInCCRLForXenServer ]
								

								
									  content  = citrix_cloud_resource_location.CreateCCRLForXenServer.id
								

								
									  filename = "${var.Install_LogPath}/DATA/RLID.json"
								

								
									}
								
								 

								
									### Create file with ZoneID
								

								
									resource "local_file" "WriteZoneID" {
								

								
									  depends_on = [ citrix_zone.CreateZoneInCCRLForXenServer ]
								

								
									  content  = citrix_zone.CreateZoneInCCRLForXenServer.id
								

								
									  filename = "${var.Install_LogPath}/DATA/ZoneID.json"
								

								
									}
								
							
						
					
				
			
		
	

	
		 
	 

	
		Module 4: _CCRLOnXenServer-CCStuff
	

	
		These are the Terraform configuration files for Module 4 (excerpts):
	 

	
		CCRLOnXenServer-CCStuff.tf
	

	
		
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}		
		
			
				
					
						
							
								
									# Terraform deployment of a Citrix DaaS Resource Location on XenServer 8
								

								
									## Creating all Citrix Cloud-related entities
								

								
									### Creating a Hypervisor Connection
								

								
									#### Retrieving the ZoneID
								

								
									data "local_file" "LoadZoneID" {
								

								
									  filename = "${var.CC_Install_LogPath}/DATA/ZoneID.txt"
								

								
									}
								
								 

								
									data "citrix_hypervisor" "XS8" {
								

								
									  name = "TACG-TF-ONP-XenServer-HypConn"
								

								
									}
								
								 
								 
								 
								
									#### Creating the Hypervisor Connection
								

								
									resource "citrix_xenserver_hypervisor" "CreateHypervisorConnection" {
								

								
									  depends_on = [ data.local_file.LoadZoneID ]
								

								
									    name                        = var.CC_XenServer-HypConn-Name
								

								
									    zone                        = data.local_file.LoadZoneID.content
								

								
									    username                    = var.CC_XenServer-HypConn-UN
								

								
									    password                    = var.CC_XenServer-HypConn-PW
								

								
									    password_format             = var.CC_XenServer-HypConn-PWFormat
								

								
									    addresses                   = var.CC_XenServer-HypConn-IP
								

								
									} 
								
								 

								
									#### Sleep 30s to let Background processes settle
								

								
									resource "time_sleep" "wait_30_seconds" {
								

								
									  depends_on = [ citrix_xenserver_hypervisor.CreateHypervisorConnection ]
								

								
									  create_duration = "30s"
								

								
									}  
								
								 

								
									#### Creating the Hypervisor Resource Pool
								

								
									resource "citrix_xenserver_hypervisor_resource_pool" "CreateHypervisorPool" {
								

								
									  #depends_on = [ time_sleep.wait_30_seconds]
								

								
									    name                        = var.CC_XenServer-HypConnPool-Name
								

								
									    hypervisor                  = data.citrix_hypervisor.XS8.id
								

								
									    networks                    = var.CC_XenServer-HypConnPool-Networks  
								
								 

								
									    storage = [ {
								

								
									      storage_name = var.CC_XenServer-HypConnPool-Storage
								

								
									    } ]   
								
								 

								
									     temporary_storage = [ {
								

								
									      storage_name = var.CC_XenServer-HypConnPool-TempStorage
								

								
									    } ]   
								

								
									}  
								
								 

								
									#### Sleep 30s to let Background processes settle
								

								
									resource "time_sleep" "wait_30_seconds1" {
								

								
									  depends_on = [ citrix_xenserver_hypervisor_resource_pool.CreateHypervisorPool ]
								

								
									  create_duration = "30s"
								

								
									}
								
								 

								
									### Creating a Machine Catalog
								

								
									#### Create the Machine Catalog
								

								
									resource "citrix_machine_catalog" "CreateXS8MCSCatalog" {
								

								
									  depends_on            = [ time_sleep.wait_30_seconds1 ]
								

								
									    name                        = var.CC_XenServer-MC-Name
								

								
									    description                 = var.CC_XenServer-MC-Description
								

								
									    allocation_type             = var.CC_XenServer-MC-AllocationType
								

								
									    session_support             = var.CC_XenServer-MC-SessionType
								

								
									    provisioning_type           = "MCS"
								

								
									    zone                        = data.local_file.LoadZoneID.content
								

								
									    provisioning_scheme         =   {
								

								
									        hypervisor               = data.citrix_hypervisor.XS8.id
								

								
									        hypervisor_resource_pool = citrix_xenserver_hypervisor_resource_pool.CreateHypervisorPool.id
								

								
									        identity_type            = var.CC_XenServer-MC-IDPType
								
								 

								
									        machine_domain_identity  = {
								

								
									            domain                   = var.CC_XenServer-MC-Domain
								

								
									            domain_ou                = var.CC_XenServer-MC-DomainOU
								

								
									            service_account          = var.CC_XenServer-MC-DomainAdmin-Username-UPN
								

								
									            service_account_password = var.CC_XenServer-MC-DomainAdmin-Password
								

								
									        }
								

								
									        xenserver_machine_config = {
								

								
									            master_image_vm        = var.CC_XenServer-MC-MasterImage
								

								
									            cpu_count              = var.CC_XenServer-MC-CPUCount
								

								
									            memory_mb              = var.CC_XenServer-MC-Memory
								
								 

								
									           writeback_cache = {
								

								
									                  writeback_cache_disk_size_gb = 4
								

								
									                  writeback_cache_memory_size_mb = 1024
								

								
									                }
								
								 

								
									        }
								

								
									        number_of_total_machines =  var.CC_XenServer-MC-Machine_Count
								
								 

								
									        machine_account_creation_rules = {
								

								
									            naming_scheme      = var.CC_XenServer-MC-Naming_Scheme_Name
								

								
									            naming_scheme_type = var.CC_XenServer-MC-Naming_Scheme_Type
								

								
									        }
								

								
									    }
								

								
									}
								
								 

								
									#### Sleep 60s to let CC Background processes settle
								

								
									resource "time_sleep" "wait_30_seconds_2" {
								

								
									  depends_on = [ citrix_machine_catalog.CreateXS8MCSCatalog ]
								

								
									  create_duration = "30s"
								

								
									}   
								
								 

								
									#### Create the Delivery Group based on the Machine Catalog
								

								
									resource "citrix_delivery_group" "CreateDG" {
								

								
									  depends_on = [ time_sleep.wait_30_seconds_2]
								

								
									    name                                    = var.CC_XenServer-DG-Name
								

								
									    associated_machine_catalogs             = [
								

								
									        {
								

								
									            machine_catalog                 = citrix_machine_catalog.CreateXS8MCSCatalog.id
								

								
									            machine_count                   = var.CC_XenServer-MC-Machine_Count
								

								
									        }
								

								
									    ]
								

								
									    desktops                                = [
								

								
									        {
								

								
									            published_name                  = var.CC_XenServer-DG-PublishedDesktopName
								

								
									            description                     = var.CC_XenServer-DG-Description
								

								
									            restricted_access_users         = {
								

								
									                                               allow_list = [ "TACG\\vdaallowed" ]
								

								
									                                              }
								

								
									            enabled                         = true
								

								
									            enable_session_roaming          = var.CC_XenServer-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_XenServer-DG-AS-Name
								

								
									                display_name                = var.CC_XenServer-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\\vdaallowed" ]
								

								
									                                              }
								

								
									    reboot_schedules                        = [
								

								
									                                               {
								

								
									                                                 name = "TACG-XS8-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
								

								
									                                               }
								

								
									  ]
								

								
									}]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_09/xc-vmsready.png.1685b279e69b42b81caed6388d63f4b5.png" length="123740" type="image/png"/><pubDate>Thu, 05 Sep 2024 17:29:09 +0000</pubDate></item><item><title>Deployment Guide: Using Terraform to deploy Citrix Policies and Delegated Administration on a Citrix Virtual Apps and Desktops 2402 LTSR CU1 Site</title><link>https://community.stage.citrix.com/tech-zone/automation/cvad-terraform-policies/</link><description><![CDATA[Overview



	This guide will showcase the possibility of deploying Citrix Policies and Delegated Administration for a Citrix Virtual Apps and Desktops 2402 LTSR CU1 Site using Terraform. 
	This guide is being provided to reduce manual interventions to the absolute minimum. 
	 
 


	 
 


	
		Important:
	 

	
		Please ensure that the site is based on Citrix Virtual Apps and Desktops 2402 LTSR CU1 to be supported. 
		 
	 



	
		Important:
	 

	
		If you use the Terraform provider version 1.0.4 or higher, deploying Policy sets on Citrix Cloud-based environments is supported now. 
		 
	 



	 
	In the end, you will have created:
 


	
		An Administrative Scope
	
	
		An Administrative Role
	
	
		An Administrative User based on the created Role and Scope 
	
	
		A sample Policy Set with different policies and filters
	
	
		Importing a Delivery Group to showcase keeping an existing infrastructure on track 
		 
	



	Installation and Configuration of Terraform



	Please find a description of installing Terraform and initial configurations in our Tech Zone Deployment Guide: Installing Terraform and Configuring the Citrix Terraform Provider.
 


	All Terraform configuration files can be found later on GitHub.
 


	 
	Deployment overview



	This guide modifies a Citrix Virtual Apps and Desktops 2402 LTSR CU1 site running on a vSphere 8 cluster. Terraform will import all needed entities' GUIDs/IDs.
 


	Terraform uses only functionalities directly provided by the Citrix Terraform Provider, so no WinRM calls or PowerShell scripts are needed for configuration. 
	 
 


	
		Note: 
	 

	
		We did not use loop-constructs or count keywords —each resource is created explicitly—for easier reading and understanding. 
		 
	 



	 
	The Terraform flow is split into different steps:
 


	
		Importing existing resources
	
	
		Reading out GUIDs/IDs
	
	
		Creating the Administrative Scope
	
	
		Creating the Administrative Role
	
	
		Creating the Delegated Administrative User based on the Scope and Role
	
	
		Creating an example Policy Set containing different Policies and Filters
	
	
		Keeping track of the configuration of an existing Delivery Group
	



	 
 


	Configuration using variables



	All needed configuration settings are stored in the corresponding variables that must be set. Some configuration settings are propagated throughout the whole Terraform configuration...
 


	You must manually start 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. 
	 
 


	
		Important:
	 

	
		Each module/step must be completed successfully before the next module can be started. 
		 
	 



	 
	File System structure



	Root-Directory
 


	
		
			
				
					Filename                                                                                                       
				 
			
			
				
					Purpose                                                
				 
			
		
	
	
		
			
				
					_TFForScopesAndPolicies-Create.tf
				 
			
			
				
					Resource configuration and primary flow definition
				 
			
		
		
			
				
					_TFForScopesAndPolicies-Create-variables.tf
				 
			
			
				
					Definition of Variables
				 
			
		
		
			
				
					_TFForScopesAndPolicies-Create.auto.tfvars.json
				 
			
			
				
					Setting the values of the Variables
				 
			
		
		
			
				
					_TFForScopesAndPolicies-Provider.tf
				 
			
			
				
					Provider definition and configuration
				 
			
		
		
			
				
					_TFForScopesAndPolicies-Provider-variables.tf
				 
			
			
				
					Definition of Variables
				 
			
		
		
			
				
					_TFForScopesAndPolicies-Provider.auto.tfvars.json
				 
			
			
				
					Setting the values of Variables
				 
			
		
		
			
				
					_terraform.tfvars
				 
			
			
				
					Setting the values of Variables
				 
			
		
		
			
				
					_TRESOR directory
				 
			
			
				
					Containing the .tfstate-files
				 
			
		
	



	 
 


	
		Caution:
	 

	
		All Terraform-related directories and files (.terraform, terraform.lock.hcl, terraform.tfstate, terraform.tfstate) must not be changed or deleted - doing so might break the deployment. 
		 
	 



	Change the settings in the .json or .tfvars files to match your needs.
 


	To ensure a smooth and error-free build, the following prerequisites must be met before setting the corresponding settings or running the Terraform workflow.
 


	 



	Software Components for Configuration and Deployment



	The deployment of Citrix Policies and Delegated Administration is a feature built into the Citrix Terraform Provider. 
	 
 


	We need to know the internal policy names, as localized policy names and descriptions are unusable. 
	We also need to know the internal names of all Delegated Administration-related entities, such as Scope, Role, and Permission names. 
	Therefore, we use PowerShell scripts to determine all internal names. 
 


	The scripts require some prerequisites to work. 
	The Delivery Controller VMs should have all needed components already installed – if you run the scripts on a different machine, please make sure to have these components installed:
 


	
		Install the Citrix Supportability Pack
	
	
		Install the Citrix Group Policy Management - scroll down to Group Policy
	
	
		Install the Citrix Virtual Apps and Desktops SDK
	



	 
 


	Using Terraform to Create a Policy Set



	Determining the Available Policies and Filters and Retrieving the Policy Names 



	Citrix Web Studio and PowerShell scripts can determine policies already configured.
 


	Web Studio: 
	
 


	 
 


	An Unfiltered policy in both the User and Computer context is created by default.
 


	You can find more information and a more detailed description of Policies and Filters here:
 


	
		https://docs.citrix.com/en-us/citrix-virtual-apps-desktops/policies/reference.html
	
	
		https://github.com/citrix/terraform-provider-citrix/blob/main/internal/daas/policies/policy_set_resource.md#available-policy-settings
	
	
		https://github.com/citrix/terraform-provider-citrix/blob/main/internal/daas/policies/policy_set_resource.md#available-policy-filters
	



	PowerShell uses a PowerShell drive to access all information: 
	
 


	We can use Powershell to list the available Policies:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\_TACG&gt; Add-PSSnapin Citrix.* 
						PS C:\_TACG&gt; Add-PsSnapin Citrix.Common.GroupPolicy 
						PS C:\_TACG&gt; Get-PSDrive 
						 
						Name           Used (GB)     Free (GB) Provider      Root                                                                                                                             CurrentLocation 
						----           ---------     --------- --------      ----                                                                                                                             --------------- 
						Alias                                  Alias 
						C                  17,34         71,84 FileSystem    C:\                                                                                                                                        _TACG 
						Cert                                   Certificate   \ 
						D                   3,26          0,00 FileSystem    D:\ 
						Env                                    Environment 
						Function                               Function 
						HKCU                                   Registry      HKEY_CURRENT_USER 
						HKLM                                   Registry      HKEY_LOCAL_MACHINE 
						Templates                              CitrixGrou... Templates:\ 
						Variable                               Variable 
						WSMan                                  WSMan 
						XDHyp                                  Citrix.Hyp... XDHyp:\ 
						 
						 
						PS C:\_TACG&gt; New-PsDrive -PsProvider CitrixGroupPolicy -Name GP -Root \ -Controller localhost 
						WARNING: It is strongly recommended that the new Broker GPO commands be used to manage Citrix policies. This Citrix Group Policy PowerShell Provider will soon be deprecated. For more information on 
						 using the Citrix Broker GPO commands, see the help about_Broker_GroupPolicy. 
						 
						Name           Used (GB)     Free (GB) Provider      Root                                                                                                                             CurrentLocation 
						----           ---------     --------- --------      ----                                                                                                                             --------------- 
						GP                                     CitrixGrou... GP:\ 
						 
						 
						PS C:\_TACG&gt; cd GP: 
						PS GP:\&gt; dir 
						User 
						Computer 
						PS GP:\&gt; cd .\Computer\ 
						PS GP:\Computer\&gt; dir 
						 
						 
						Name           : Unfiltered 
						Description    : This is the system-created default policy, it cannot be deleted. Note that its settings will apply to all connections. 
						Enabled        : True 
						Priority       : 1 
						MergedPriority : 0 
						PSPath         : Citrix.Common.GroupPolicy\CitrixGroupPolicy::GP:\Computer\Unfiltered 
						PSParentPath   : Citrix.Common.GroupPolicy\CitrixGroupPolicy::GP:\Computer 
						PSChildName    : Unfiltered 
						PSDrive        : GP 
						PSProvider     : Citrix.Common.GroupPolicy\CitrixGroupPolicy 
						PSIsContainer  : True 
						 
						 
						 
						PS GP:\Computer\&gt; cd .. 
						PS GP:\&gt; cd .\User\ 
						PS GP:\User\&gt; dir 
						 
						 
						Name           : Unfiltered 
						Description    : This is the system-created default policy, it cannot be deleted. Note that its settings will apply to all connections. 
						Enabled        : True 
						Priority       : 1 
						MergedPriority : 0 
						PSPath         : Citrix.Common.GroupPolicy\CitrixGroupPolicy::GP:\User\Unfiltered 
						PSParentPath   : Citrix.Common.GroupPolicy\CitrixGroupPolicy::GP:\User 
						PSChildName    : Unfiltered 
						PSDrive        : GP 
						PSProvider     : Citrix.Common.GroupPolicy\CitrixGroupPolicy 
						PSIsContainer  : True 
						 
						 
						 
						PS GP:\User\&gt;
					 
				
			
		
	



	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0200b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0200b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}		
	 

	
		
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0200b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}		
	 

	
		
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}		
	

	
		 
		We can also use Powershell to list the available Policy templates:
	 

	
		
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}		
		
			
				
					
						
							PS C:\_TACG&gt; Get-PSDrive 
							 
							Name           Used (GB)     Free (GB) Provider      Root                                                                                                                             CurrentLocation 
							----           ---------     --------- --------      ----                                                                                                                             --------------- 
							Alias                                  Alias 
							C                  17,42         71,76 FileSystem    C:\                                                                                                                                        _TACG 
							Cert                                   Certificate   \ 
							D                   3,26          0,00 FileSystem    D:\ 
							Env                                    Environment 
							Function                               Function 
							GP                                     CitrixGrou... GP:\ 
							HKCU                                   Registry      HKEY_CURRENT_USER 
							HKLM                                   Registry      HKEY_LOCAL_MACHINE 
							Templates                              CitrixGrou... Templates:\ 
							Variable                               Variable 
							WSMan                                  WSMan 
							XDHyp                                  Citrix.Hyp... XDHyp:\ 
							 
							 
							PS C:\_TACG&gt; cd Templates: 
							PS Templates:\&gt; dir 
							 
							 
							TemplateName  : Security_and_Control 
							Description   : Apply this template to disable use of client-side peripheral devices (like USB drives), drive mapping, client-side rendering of media content, and more. Note: Applying this 
							                template may consume more bandwidth and/or reduce user density per server. 
							Type          : Both 
							ModifiedBy    : fred.liu 
							ModifiedOn    : 21.03.2023 19:10:11 
							ReadOnly      : True 
							PSPath        : Citrix.Common.GroupPolicy\CitrixGroupPolicy::Templates:\Security_and_Control 
							PSParentPath  : Citrix.Common.GroupPolicy\CitrixGroupPolicy::Templates: 
							PSChildName   : Security_and_Control 
							PSDrive       : Templates 
							PSProvider    : Citrix.Common.GroupPolicy\CitrixGroupPolicy 
							PSIsContainer : True 
							 
							TemplateName  : High_Server_Scalability 
							Description   : Apply this template to economize on server resources. This template balances user experience and server scalability. It offers a good user experience while increasing the number of 
							                users you can host on a single server.  See 'High Server Scalability-Legacy OS' template for older operating systems. 
							Type          : Both 
							ModifiedBy    : fred.liu 
							ModifiedOn    : 26.09.2023 20:21:56 
							ReadOnly      : True 
							PSPath        : Citrix.Common.GroupPolicy\CitrixGroupPolicy::Templates:\High_Server_Scalability 
							PSParentPath  : Citrix.Common.GroupPolicy\CitrixGroupPolicy::Templates: 
							PSChildName   : High_Server_Scalability 
							PSDrive       : Templates 
							PSProvider    : Citrix.Common.GroupPolicy\CitrixGroupPolicy 
							PSIsContainer : True 
							 
							TemplateName  : Optimized_for_WAN 
							Description   : Apply this template for task workers in branch offices (shared WAN connection) or remote locations with low bandwidth connections accessing applications with graphically simple 
							                user interfaces with little video content. This template trades off user experience quality for optimized bandwidth efficiency. See ‘Optimized for WAN-Legacy OS’ template for older 
							                operating systems. 
							Type          : Both 
							ModifiedBy    : fred.liu 
							ModifiedOn    : 26.09.2023 20:24:50 
							ReadOnly      : True 
							PSPath        : Citrix.Common.GroupPolicy\CitrixGroupPolicy::Templates:\Optimized_for_WAN 
							PSParentPath  : Citrix.Common.GroupPolicy\CitrixGroupPolicy::Templates: 
							PSChildName   : Optimized_for_WAN 
							PSDrive       : Templates 
							PSProvider    : Citrix.Common.GroupPolicy\CitrixGroupPolicy 
							PSIsContainer : True 
							 
							TemplateName  : Very_High_Definition_User_Experience 
							Description   : The default configuration is optimized to deliver a high quality user experience for rich graphics, audio, and video. Apply this template to deliver an even higher quality user 
							                experience.   Note: Applying this template may consume more bandwidth and reduce user density per server based on user tasks. 
							Type          : Both 
							ModifiedBy    : fred.liu 
							ModifiedOn    : 27.09.2023 22:02:03 
							ReadOnly      : True 
							PSPath        : Citrix.Common.GroupPolicy\CitrixGroupPolicy::Templates:\Very_High_Definition_User_Experience 
							PSParentPath  : Citrix.Common.GroupPolicy\CitrixGroupPolicy::Templates: 
							PSChildName   : Very_High_Definition_User_Experience 
							PSDrive       : Templates 
							PSProvider    : Citrix.Common.GroupPolicy\CitrixGroupPolicy 
							PSIsContainer : True 
							 
							TemplateName  : Client_Printers 
							Description   : The settings in this template are typically used for configuring client-redirected printers. To configure these settings in your environment, create a policy using this template. 
							                After the policy is created, you must change the value of each setting to the value suitable for your environment. 
							Type          : Both 
							ModifiedBy    : fred.liu 
							ModifiedOn    : 26.09.2023 20:40:25 
							ReadOnly      : True 
							PSPath        : Citrix.Common.GroupPolicy\CitrixGroupPolicy::Templates:\Client_Printers 
							PSParentPath  : Citrix.Common.GroupPolicy\CitrixGroupPolicy::Templates: 
							PSChildName   : Client_Printers 
							PSDrive       : Templates 
							PSProvider    : Citrix.Common.GroupPolicy\CitrixGroupPolicy 
							PSIsContainer : True 
							 
							TemplateName  : Universal_Printing 
							Description   : The settings in this template are typically used for configuring Citrix Universal printer drivers. To configure these settings in your environment, create a policy using this 
							                template. After the policy is created, you must change the value of each setting to the value suitable for your environment. 
							Type          : Both 
							ModifiedBy    : fred.liu 
							ModifiedOn    : 26.09.2023 20:45:14 
							ReadOnly      : True 
							PSPath        : Citrix.Common.GroupPolicy\CitrixGroupPolicy::Templates:\Universal_Printing 
							PSParentPath  : Citrix.Common.GroupPolicy\CitrixGroupPolicy::Templates: 
							PSChildName   : Universal_Printing 
							PSDrive       : Templates 
							PSProvider    : Citrix.Common.GroupPolicy\CitrixGroupPolicy 
							PSIsContainer : True 
							 
							TemplateName  : Universal_Print_Server 
							Description   : The settings in this template are typically used for configuring Citrix Universal Print Server. To configure these settings in your environment, create a policy using this 
							                template. After the policy is created, you must change the value of each setting to the value suitable for your environment. 
							Type          : Both 
							ModifiedBy    : fred.liu 
							ModifiedOn    : 26.09.2023 20:48:11 
							ReadOnly      : True 
							PSPath        : Citrix.Common.GroupPolicy\CitrixGroupPolicy::Templates:\Universal_Print_Server 
							PSParentPath  : Citrix.Common.GroupPolicy\CitrixGroupPolicy::Templates: 
							PSChildName   : Universal_Print_Server 
							PSDrive       : Templates 
							PSProvider    : Citrix.Common.GroupPolicy\CitrixGroupPolicy 
							PSIsContainer : True 
							 
							 
							 
							PS Templates:\&gt;
						 
					
				
			
		
	

	
		
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0200b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}		
		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0200b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
		 

		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0200b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
		 

		
			 
		

		
			To get a more detailed and comprehensive list of all Policy Names, you can use a PowerShell module created by Ryan Butler:
		
	

	
		https://github.com/ryancbutler/Citrix/blob/master/XenDesktop/GPO/Citrix.GroupPolicy.Commands.psm1 
		 
	 

	
		
			Important:
		 

		
			Please read the disclaimer at the end of the page before installing external content! 
			 
		 
	

	
		 
	 

	
		
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}		
		
			
				
					
						
							PS C:\_TACG&gt; Import-Module .\Citrix.GroupPolicy.Commands.psm1 
							 
							Security warning 
							Run only scripts that you trust. While scripts from the internet can be useful, this script can potentially harm your computer. If you trust this script, use the Unblock-File cmdlet to allow the 
							script to run without this warning message. Do you want to run C:\_TACG\Citrix.GroupPolicy.Commands.psm1? 
							[D] Do not run  [R] Run once  [S] Suspend  [?] Help (default is "D"): R
						 

						
							 
						 

						
							##### List all Policy Names: 
							PS C:\_TACG&gt; Get-CtxGroupPolicyConfiguration 
							 
							 
							PolicyName                                    : Unfiltered 
							Type                                          : Computer 
							WemCloudConnectorList                         : @{State=NotConfigured; path=WemConfiguration\WemCloudConnectorList} 
							VirtualLoopbackPortExclusion                  : @{State=NotConfigured; path=VirtualIP\VirtualLoopbackPortExclusion} 
							VirtualLoopbackPrograms                       : @{State=NotConfigured; path=VirtualIP\VirtualLoopbackPrograms} 
							VirtualLoopbackSupport                        : @{State=NotConfigured; path=VirtualIP\VirtualLoopbackSupport} 
							EnableAutoUpdateOfControllers                 : @{State=NotConfigured; path=VirtualDesktopAgent\EnableAutoUpdateOfControllers} 
							AppFailureExclusionList                       : @{State=NotConfigured; value=; path=VirtualDesktopAgent\Monitoring\AppFailureExclusionList} 
							EnableProcessMonitoring                       : @{State=NotConfigured; path=VirtualDesktopAgent\Monitoring\EnableProcessMonitoring} 
							EnableResourceMonitoring                      : @{State=NotConfigured; path=VirtualDesktopAgent\Monitoring\EnableResourceMonitoring} 
							EnableWorkstationVDAFaultMonitoring           : @{State=NotConfigured; path=VirtualDesktopAgent\Monitoring\EnableWorkstationVDAFaultMonitoring} 
							SelectedFailureLevel                          : @{State=NotConfigured; value=Fault; path=VirtualDesktopAgent\Monitoring\SelectedFailureLevel} 
							CPUUsageMonitoring_Enable                     : @{State=NotConfigured; path=VirtualDesktopAgent\CPUUsageMonitoring\CPUUsageMonitoring_Enable} 
							CPUUsageMonitoring_Period                     : @{State=NotConfigured; value=60; path=VirtualDesktopAgent\CPUUsageMonitoring\CPUUsageMonitoring_Period} 
							CPUUsageMonitoring_Threshold                  : @{State=NotConfigured; value=95; path=VirtualDesktopAgent\CPUUsageMonitoring\CPUUsageMonitoring_Threshold} 
							VdcPolicyEnable                               : @{State=NotConfigured; path=VdaDataCollection\VdcPolicyEnable} 
							EnableClipboardMetadataCollection             : @{State=NotConfigured; path=VdaDataCollection\Security\EnableClipboardMetadataCollection} 
							EnableVdaDiagnosticsCollection                : @{State=NotConfigured; path=VdaDataCollection\Performance\EnableVdaDiagnosticsCollection} 
							XenAppOptimizationDefinitionPathData          : @{State=NotConfigured; value=; path=UserProfileManager\XenAppOptimizationSettings\XenAppOptimizationDefinitionPathData} 
							XenAppOptimizationEnabled                     : @{State=NotConfigured; path=UserProfileManager\XenAppOptimizationSettings\XenAppOptimizationEnabled} 
							ExclusionList_Part                            : @{State=NotConfigured; path=UserProfileManager\RegistrySettings\ExclusionList_Part} 
							IncludeListRegistry_Part                      : @{State=NotConfigured; path=UserProfileManager\RegistrySettings\IncludeListRegistry_Part} 
							LastKnownGoodRegistry                         : @{State=NotConfigured; path=UserProfileManager\RegistrySettings\LastKnownGoodRegistry} 
							DefaultExclusionList                          : @{State=NotConfigured; path=UserProfileManager\RegistrySettings\DefaultREGExclusions\DefaultExclusionList} 
							ExclusionDefaultReg01                         : @{State=NotConfigured; path=UserProfileManager\RegistrySettings\DefaultREGExclusions\ExclusionDefaultReg01} 
							ExclusionDefaultReg02                         : @{State=NotConfigured; path=UserProfileManager\RegistrySettings\DefaultREGExclusions\ExclusionDefaultReg02} 
							ExclusionDefaultReg03                         : @{State=NotConfigured; path=UserProfileManager\RegistrySettings\DefaultREGExclusions\ExclusionDefaultReg03} 
							PSAlwaysCache                                 : @{State=NotConfigured; path=UserProfileManager\PSSettings\PSAlwaysCache} 
							PSAlwaysCache_Part                            : @{State=NotConfigured; value=0; path=UserProfileManager\PSSettings\PSAlwaysCache_Part} 
							PSEnabled                                     : @{State=NotConfigured; path=UserProfileManager\PSSettings\PSEnabled} 
							PSForFoldersEnabled                           : @{State=NotConfigured; path=UserProfileManager\PSSettings\PSForFoldersEnabled} 
							PSForPendingAreaEnabled                       : @{State=NotConfigured; path=UserProfileManager\PSSettings\PSForPendingAreaEnabled} 
							PSPendingLockTimeout                          : @{State=NotConfigured; value=1; path=UserProfileManager\PSSettings\PSPendingLockTimeout} 
							PSUserGroups_Part                             : @{State=NotConfigured; path=UserProfileManager\PSSettings\PSUserGroups_Part} 
							StreamingExclusionList_Part                   : @{State=NotConfigured; path=UserProfileManager\PSSettings\StreamingExclusionList_Part} 
							ApplicationProfilesAutoMigration              : @{State=NotConfigured; path=UserProfileManager\ProfileHandling\ApplicationProfilesAutoMigration} 
							DeleteCachedProfilesOnLogoff                  : @{State=NotConfigured; path=UserProfileManager\ProfileHandling\DeleteCachedProfilesOnLogoff} 
							LocalProfileConflictHandling_Part             : @{State=NotConfigured; value=Use; path=UserProfileManager\ProfileHandling\LocalProfileConflictHandling_Part} 
							MigrateWindowsProfilesToUserStore_Part        : @{State=NotConfigured; value=All; path=UserProfileManager\ProfileHandling\MigrateWindowsProfilesToUserStore_Part} 
							ProfileDeleteDelay_Part                       : @{State=NotConfigured; value=0; path=UserProfileManager\ProfileHandling\ProfileDeleteDelay_Part} 
							TemplateProfileIsMandatory                    : @{State=NotConfigured; path=UserProfileManager\ProfileHandling\TemplateProfileIsMandatory} 
							TemplateProfileOverridesLocalProfile          : @{State=NotConfigured; path=UserProfileManager\ProfileHandling\TemplateProfileOverridesLocalProfile} 
							TemplateProfileOverridesRoamingProfile        : @{State=NotConfigured; path=UserProfileManager\ProfileHandling\TemplateProfileOverridesRoamingProfile} 
							TemplateProfilePath                           : @{State=NotConfigured; value=; path=UserProfileManager\ProfileHandling\TemplateProfilePath} 
							DisableConcurrentAccessToOneDriveContainer    : @{State=NotConfigured; path=UserProfileManager\ProfileContainerSettings\DisableConcurrentAccessToOneDriveContainer} 
							DisableConcurrentAccessToProfileContainer     : @{State=NotConfigured; path=UserProfileManager\ProfileContainerSettings\DisableConcurrentAccessToProfileContainer} 
							EnableVHDAutoExtend                           : @{State=NotConfigured; path=UserProfileManager\ProfileContainerSettings\EnableVHDAutoExtend} 
							EnableVHDDiskCompaction                       : @{State=NotConfigured; path=UserProfileManager\ProfileContainerSettings\EnableVHDDiskCompaction} 
							GroupsToAccessProfileContainer_Part           : @{State=NotConfigured; path=UserProfileManager\ProfileContainerSettings\GroupsToAccessProfileContainer_Part} 
							PreventLoginWhenMountFailed_Part              : @{State=NotConfigured; value=; path=UserProfileManager\ProfileContainerSettings\PreventLoginWhenMountFailed_Part} 
							ProfileContainerExclusionListDir_Part         : @{State=NotConfigured; path=UserProfileManager\ProfileContainerSettings\ProfileContainerExclusionListDir_Part} 
							ProfileContainerExclusionListFile_Part        : @{State=NotConfigured; path=UserProfileManager\ProfileContainerSettings\ProfileContainerExclusionListFile_Part} 
							ProfileContainerInclusionListDir_Part         : @{State=NotConfigured; path=UserProfileManager\ProfileContainerSettings\ProfileContainerInclusionListDir_Part} 
							ProfileContainerInclusionListFile_Part        : @{State=NotConfigured; path=UserProfileManager\ProfileContainerSettings\ProfileContainerInclusionListFile_Part} 
							ProfileContainerLocalCache                    : @{State=NotConfigured; path=UserProfileManager\ProfileContainerSettings\ProfileContainerLocalCache} 
							DebugFilePath_Part                            : @{State=NotConfigured; value=; path=UserProfileManager\LogSettings\DebugFilePath_Part} 
							DebugMode                                     : @{State=NotConfigured; path=UserProfileManager\LogSettings\DebugMode} 
							LogLevel_ActiveDirectoryActions               : @{State=NotConfigured; path=UserProfileManager\LogSettings\LogLevel_ActiveDirectoryActions} 
							LogLevel_FileSystemActions                    : @{State=NotConfigured; path=UserProfileManager\LogSettings\LogLevel_FileSystemActions} 
							LogLevel_FileSystemNotification               : @{State=NotConfigured; path=UserProfileManager\LogSettings\LogLevel_FileSystemNotification} 
							LogLevel_Information                          : @{State=NotConfigured; path=UserProfileManager\LogSettings\LogLevel_Information} 
							LogLevel_Logoff                               : @{State=NotConfigured; path=UserProfileManager\LogSettings\LogLevel_Logoff} 
							LogLevel_Logon                                : @{State=NotConfigured; path=UserProfileManager\LogSettings\LogLevel_Logon} 
							LogLevel_PolicyUserLogon                      : @{State=NotConfigured; path=UserProfileManager\LogSettings\LogLevel_PolicyUserLogon} 
							LogLevel_RegistryActions                      : @{State=NotConfigured; path=UserProfileManager\LogSettings\LogLevel_RegistryActions} 
							LogLevel_RegistryDifference                   : @{State=NotConfigured; path=UserProfileManager\LogSettings\LogLevel_RegistryDifference} 
							LogLevel_UserName                             : @{State=NotConfigured; path=UserProfileManager\LogSettings\LogLevel_UserName} 
							LogLevel_Warnings                             : @{State=NotConfigured; path=UserProfileManager\LogSettings\LogLevel_Warnings} 
							MaxLogSize_Part                               : @{State=NotConfigured; value=10485760; path=UserProfileManager\LogSettings\MaxLogSize_Part} 
							LargeFileHandlingList_Part                    : @{State=NotConfigured; path=UserProfileManager\FileSystemSettings\LargeFileHandlingList_Part} 
							LogonExclusionCheck_Part                      : @{State=NotConfigured; value=Disable; path=UserProfileManager\FileSystemSettings\LogonExclusionCheck_Part} 
							AccelerateFolderMirroring                     : @{State=NotConfigured; path=UserProfileManager\FileSystemSettings\FSSynchronization\AccelerateFolderMirroring} 
							MirrorFoldersList_Part                        : @{State=NotConfigured; path=UserProfileManager\FileSystemSettings\FSSynchronization\MirrorFoldersList_Part} 
							ProfileContainer_Part                         : @{State=NotConfigured; path=UserProfileManager\FileSystemSettings\FSSynchronization\ProfileContainer_Part} 
							SyncDirList_Part                              : @{State=NotConfigured; path=UserProfileManager\FileSystemSettings\FSSynchronization\SyncDirList_Part} 
							SyncFileList_Part                             : @{State=NotConfigured; path=UserProfileManager\FileSystemSettings\FSSynchronization\SyncFileList_Part} 
							ExclusionListSyncDir_Part                     : @{State=NotConfigured; path=UserProfileManager\FileSystemSettings\FSExclusions\ExclusionListSyncDir_Part} 
							ExclusionListSyncFiles_Part                   : @{State=NotConfigured; path=UserProfileManager\FileSystemSettings\FSExclusions\ExclusionListSyncFiles_Part} 
							DefaultExclusionListSyncDir                   : @{State=NotConfigured; path=UserProfileManager\FileSystemSettings\DefaultFSExclusions\DefaultExclusionListSyncDir} 
							ExclusionDefaultDir01                         : @{State=NotConfigured; path=UserProfileManager\FileSystemSettings\DefaultFSExclusions\ExclusionDefaultDir01} 
							ExclusionDefaultDir02                         : @{State=NotConfigured; path=UserProfileManager\FileSystemSettings\DefaultFSExclusions\ExclusionDefaultDir02} 
							ExclusionDefaultDir03                         : @{State=NotConfigured; path=UserProfileManager\FileSystemSettings\DefaultFSExclusions\ExclusionDefaultDir03} 
							ExclusionDefaultDir04                         : @{State=NotConfigured; path=UserProfileManager\FileSystemSettings\DefaultFSExclusions\ExclusionDefaultDir04} 
							ExclusionDefaultDir05                         : @{State=NotConfigured; path=UserProfileManager\FileSystemSettings\DefaultFSExclusions\ExclusionDefaultDir05} 
							ExclusionDefaultDir06                         : @{State=NotConfigured; path=UserProfileManager\FileSystemSettings\DefaultFSExclusions\ExclusionDefaultDir06} 
							ExclusionDefaultDir07                         : @{State=NotConfigured; path=UserProfileManager\FileSystemSettings\DefaultFSExclusions\ExclusionDefaultDir07} 
							ExclusionDefaultDir08                         : @{State=NotConfigured; path=UserProfileManager\FileSystemSettings\DefaultFSExclusions\ExclusionDefaultDir08} 
							ExclusionDefaultDir09                         : @{State=NotConfigured; path=UserProfileManager\FileSystemSettings\DefaultFSExclusions\ExclusionDefaultDir09} 
							ExclusionDefaultDir10                         : @{State=NotConfigured; path=UserProfileManager\FileSystemSettings\DefaultFSExclusions\ExclusionDefaultDir10} 
							ExclusionDefaultDir11                         : @{State=NotConfigured; path=UserProfileManager\FileSystemSettings\DefaultFSExclusions\ExclusionDefaultDir11} 
							ExclusionDefaultDir12                         : @{State=NotConfigured; path=UserProfileManager\FileSystemSettings\DefaultFSExclusions\ExclusionDefaultDir12} 
							ExclusionDefaultDir13                         : @{State=NotConfigured; path=UserProfileManager\FileSystemSettings\DefaultFSExclusions\ExclusionDefaultDir13} 
							ExclusionDefaultDir14                         : @{State=NotConfigured; path=UserProfileManager\FileSystemSettings\DefaultFSExclusions\ExclusionDefaultDir14} 
							ExclusionDefaultDir15                         : @{State=NotConfigured; path=UserProfileManager\FileSystemSettings\DefaultFSExclusions\ExclusionDefaultDir15} 
							ExclusionDefaultDir16                         : @{State=NotConfigured; path=UserProfileManager\FileSystemSettings\DefaultFSExclusions\ExclusionDefaultDir16} 
							ExclusionDefaultDir17                         : @{State=NotConfigured; path=UserProfileManager\FileSystemSettings\DefaultFSExclusions\ExclusionDefaultDir17} 
							ExclusionDefaultDir18                         : @{State=NotConfigured; path=UserProfileManager\FileSystemSettings\DefaultFSExclusions\ExclusionDefaultDir18} 
							ExclusionDefaultDir19                         : @{State=NotConfigured; path=UserProfileManager\FileSystemSettings\DefaultFSExclusions\ExclusionDefaultDir19} 
							ExclusionDefaultDir20                         : @{State=NotConfigured; path=UserProfileManager\FileSystemSettings\DefaultFSExclusions\ExclusionDefaultDir20} 
							ExclusionDefaultDir21                         : @{State=NotConfigured; path=UserProfileManager\FileSystemSettings\DefaultFSExclusions\ExclusionDefaultDir21} 
							ExclusionDefaultDir22                         : @{State=NotConfigured; path=UserProfileManager\FileSystemSettings\DefaultFSExclusions\ExclusionDefaultDir22} 
							ExclusionDefaultDir23                         : @{State=NotConfigured; path=UserProfileManager\FileSystemSettings\DefaultFSExclusions\ExclusionDefaultDir23} 
							ExclusionDefaultDir24                         : @{State=NotConfigured; path=UserProfileManager\FileSystemSettings\DefaultFSExclusions\ExclusionDefaultDir24} 
							ExclusionDefaultDir25                         : @{State=NotConfigured; path=UserProfileManager\FileSystemSettings\DefaultFSExclusions\ExclusionDefaultDir25} 
							ExclusionDefaultDir26                         : @{State=NotConfigured; path=UserProfileManager\FileSystemSettings\DefaultFSExclusions\ExclusionDefaultDir26} 
							ExclusionDefaultDir27                         : @{State=NotConfigured; path=UserProfileManager\FileSystemSettings\DefaultFSExclusions\ExclusionDefaultDir27} 
							ExclusionDefaultDir28                         : @{State=NotConfigured; path=UserProfileManager\FileSystemSettings\DefaultFSExclusions\ExclusionDefaultDir28} 
							ExclusionDefaultDir29                         : @{State=NotConfigured; path=UserProfileManager\FileSystemSettings\DefaultFSExclusions\ExclusionDefaultDir29} 
							ExclusionDefaultDir30                         : @{State=NotConfigured; path=UserProfileManager\FileSystemSettings\DefaultFSExclusions\ExclusionDefaultDir30} 
							SharedStoreFileExclusionList_Part             : @{State=NotConfigured; path=UserProfileManager\FileDeduplicationSettings\SharedStoreFileExclusionList_Part} 
							SharedStoreFileInclusionList_Part             : @{State=NotConfigured; path=UserProfileManager\FileDeduplicationSettings\SharedStoreFileInclusionList_Part} 
							SharedStoreProfileContainerFileSizeLimit_Part : @{State=NotConfigured; value=256; path=UserProfileManager\FileDeduplicationSettings\SharedStoreProfileContainerFileSizeLimit_Part} 
							CPEnable                                      : @{State=NotConfigured; path=UserProfileManager\CPSettings\CPEnable} 
							CPMigrationFromBaseProfileToCPStore           : @{State=NotConfigured; path=UserProfileManager\CPSettings\CPMigrationFromBaseProfileToCPStore} 
							CPPathData                                    : @{State=NotConfigured; value=; path=UserProfileManager\CPSettings\CPPathData} 
							CPSchemaPathData                              : @{State=NotConfigured; value=; path=UserProfileManager\CPSettings\CPSchemaPathData} 
							CPUserGroups_Part                             : @{State=NotConfigured; path=UserProfileManager\CPSettings\CPUserGroups_Part} 
							DATPath_Part                                  : @{State=NotConfigured; value=Windows; path=UserProfileManager\BasicSettings\DATPath_Part} 
							ExcludedGroups_Part                           : @{State=NotConfigured; path=UserProfileManager\BasicSettings\ExcludedGroups_Part} 
							MigrateUserStore_Part                         : @{State=NotConfigured; value=; path=UserProfileManager\BasicSettings\MigrateUserStore_Part} 
							OfflineSupport                                : @{State=NotConfigured; path=UserProfileManager\BasicSettings\OfflineSupport} 
							ProcessAdmins                                 : @{State=NotConfigured; path=UserProfileManager\BasicSettings\ProcessAdmins} 
							ProcessedGroups_Part                          : @{State=NotConfigured; path=UserProfileManager\BasicSettings\ProcessedGroups_Part} 
							PSMidSessionWriteBack                         : @{State=NotConfigured; path=UserProfileManager\BasicSettings\PSMidSessionWriteBack} 
							PSMidSessionWriteBackReg                      : @{State=NotConfigured; path=UserProfileManager\BasicSettings\PSMidSessionWriteBackReg} 
							PSMidSessionWriteBackSessionLock              : @{State=NotConfigured; path=UserProfileManager\BasicSettings\PSMidSessionWriteBackSessionLock} 
							ServiceActive                                 : @{State=NotConfigured; path=UserProfileManager\BasicSettings\ServiceActive} 
							AppAccessControl_Part                         : @{State=NotConfigured; value=; path=UserProfileManager\AppAccessControlSettings\AppAccessControl_Part} 
							CEIPEnabled                                   : @{State=NotConfigured; path=UserProfileManager\AdvancedSettings\CEIPEnabled} 
							CredBasedAccessEnabled                        : @{State=NotConfigured; path=UserProfileManager\AdvancedSettings\CredBasedAccessEnabled} 
							DisableDynamicConfig                          : @{State=NotConfigured; path=UserProfileManager\AdvancedSettings\DisableDynamicConfig} 
							EnableVolumeReattach                          : @{State=NotConfigured; path=UserProfileManager\AdvancedSettings\EnableVolumeReattach} 
							FreeRatio4Compaction_Part                     : @{State=NotConfigured; value=20; path=UserProfileManager\AdvancedSettings\FreeRatio4Compaction_Part} 
							FSLogixProfileContainerSupport                : @{State=NotConfigured; path=UserProfileManager\AdvancedSettings\FSLogixProfileContainerSupport} 
							LoadRetries_Part                              : @{State=NotConfigured; value=5; path=UserProfileManager\AdvancedSettings\LoadRetries_Part} 
							LogoffRatherThanTempProfile                   : @{State=NotConfigured; path=UserProfileManager\AdvancedSettings\LogoffRatherThanTempProfile} 
							MultiSiteReplication_Part                     : @{State=NotConfigured; path=UserProfileManager\AdvancedSettings\MultiSiteReplication_Part} 
							NDefrag4Compaction                            : @{State=NotConfigured; path=UserProfileManager\AdvancedSettings\NDefrag4Compaction} 
							NLogoffs4Compaction_Part                      : @{State=NotConfigured; value=5; path=UserProfileManager\AdvancedSettings\NLogoffs4Compaction_Part} 
							OneDriveContainer_Part                        : @{State=NotConfigured; path=UserProfileManager\AdvancedSettings\OneDriveContainer_Part} 
							OrderedGroups_Part                            : @{State=NotConfigured; value=; path=UserProfileManager\AdvancedSettings\OrderedGroups_Part} 
							OutlookEdbBackupEnabled                       : @{State=NotConfigured; path=UserProfileManager\AdvancedSettings\OutlookEdbBackupEnabled} 
							OutlookSearchRoamingConcurrentSession         : @{State=NotConfigured; path=UserProfileManager\AdvancedSettings\OutlookSearchRoamingConcurrentSession} 
							OutlookSearchRoamingConcurrentSession_Part    : @{State=NotConfigured; value=2; path=UserProfileManager\AdvancedSettings\OutlookSearchRoamingConcurrentSession_Part} 
							OutlookSearchRoamingEnabled                   : @{State=NotConfigured; path=UserProfileManager\AdvancedSettings\OutlookSearchRoamingEnabled} 
							ProcessCookieFiles                            : @{State=NotConfigured; path=UserProfileManager\AdvancedSettings\ProcessCookieFiles} 
							ProfileContainerFailOver                      : @{State=NotConfigured; path=UserProfileManager\AdvancedSettings\ProfileContainerFailOver} 
							SyncGpoStateEnabled                           : @{State=NotConfigured; path=UserProfileManager\AdvancedSettings\SyncGpoStateEnabled} 
							UserGroupLevelConfigEnabled                   : @{State=NotConfigured; path=UserProfileManager\AdvancedSettings\UserGroupLevelConfigEnabled} 
							UserStoreSelection_Part                       : @{State=NotConfigured; value=Config; path=UserProfileManager\AdvancedSettings\UserStoreSelection_Part} 
							UwpAppsRoaming                                : @{State=NotConfigured; path=UserProfileManager\AdvancedSettings\UwpAppsRoaming} 
							VhdAutoExpansionIncrement_Part                : @{State=NotConfigured; value=10; path=UserProfileManager\AdvancedSettings\VhdAutoExpansionIncrement_Part} 
							VhdAutoExpansionLimit_Part                    : @{State=NotConfigured; value=80; path=UserProfileManager\AdvancedSettings\VhdAutoExpansionLimit_Part} 
							VhdAutoExpansionThreshold_Part                : @{State=NotConfigured; value=90; path=UserProfileManager\AdvancedSettings\VhdAutoExpansionThreshold_Part} 
							VhdContainerCapacity_Part                     : @{State=NotConfigured; value=50; path=UserProfileManager\AdvancedSettings\VhdContainerCapacity_Part} 
							VhdStorePath_Part                             : @{State=NotConfigured; value=; path=UserProfileManager\AdvancedSettings\VhdStorePath_Part} 
							UplCustomizedUserLayerSizeInGb                : @{State=NotConfigured; value=10; path=UplConfiguration\UplCustomizedUserLayerSizeInGb} 
							UplGroupsUsingCustomizedUserLayerSize         : @{State=NotConfigured; path=UplConfiguration\UplGroupsUsingCustomizedUserLayerSize} 
							UplRepositoryPath                             : @{State=NotConfigured; value=\\server\share\path; path=UplConfiguration\UplRepositoryPath} 
							UplUserExclusions                             : @{State=NotConfigured; path=UplConfiguration\UplUserExclusions} 
							UplUserLayerSizeInGb                          : @{State=NotConfigured; value=10; path=UplConfiguration\UplUserLayerSizeInGb} 
							UserLayerCompactionEnabled                    : @{State=NotConfigured; path=UplConfiguration\UserLayerCompactionEnabled} 
							ConcurrentLogonsTolerance                     : @{State=NotConfigured; value=2; path=LoadManagement\ConcurrentLogonsTolerance} 
							CPUUsage                                      : @{State=NotConfigured; value=-1; path=LoadManagement\CPUUsage} 
							CPUUsageExcludedProcessPriority               : @{State=NotConfigured; value=BelowNormalOrLow; path=LoadManagement\CPUUsageExcludedProcessPriority} 
							DiskUsage                                     : @{State=NotConfigured; value=-1; path=LoadManagement\DiskUsage} 
							MaximumNumberOfSessions                       : @{State=NotConfigured; value=250; path=LoadManagement\MaximumNumberOfSessions} 
							MemoryUsage                                   : @{State=NotConfigured; value=-1; path=LoadManagement\MemoryUsage} 
							MemoryUsageBaseLoad                           : @{State=NotConfigured; value=768; path=LoadManagement\MemoryUsageBaseLoad} 
							ApplicationLaunchWaitTimeout                  : @{State=NotConfigured; value=60000; path=ICA\ApplicationLaunchWaitTimeout} 
							DynamicVirtualChannelAllowList                : @{State=NotConfigured; path=ICA\DynamicVirtualChannelAllowList} 
							HDXAdaptiveTransport                          : @{State=NotConfigured; value=Preferred; path=ICA\HDXAdaptiveTransport} 
							HDXDirect                                     : @{State=NotConfigured; path=ICA\HDXDirect} 
							HDXDirectMode                                 : @{State=NotConfigured; value=InternalOnly; path=ICA\HDXDirectMode} 
							HDXDirectPortRange                            : @{State=NotConfigured; value=55000,55250; path=ICA\HDXDirectPortRange} 
							IcaListenerPortNumber                         : @{State=NotConfigured; value=1494; path=ICA\IcaListenerPortNumber} 
							IcaListenerTimeout                            : @{State=NotConfigured; value=120000; path=ICA\IcaListenerTimeout} 
							LogoffCheckerStartupDelay                     : @{State=NotConfigured; value=0; path=ICA\LogoffCheckerStartupDelay} 
							RemoteCredentialGuard                         : @{State=NotConfigured; path=ICA\RemoteCredentialGuard} 
							RendezvousProtocol                            : @{State=NotConfigured; path=ICA\RendezvousProtocol} 
							RendezvousProxy                               : @{State=NotConfigured; value=; path=ICA\RendezvousProxy} 
							SecureHDX                                     : @{State=NotConfigured; value=Disabled; path=ICA\SecureHDX} 
							UsageDataCollectionThroughClient              : @{State=NotConfigured; path=ICA\UsageDataCollectionThroughClient} 
							VdaUpgradeProxy                               : @{State=NotConfigured; value=; path=ICA\VdaUpgradeProxy} 
							VirtualChannelWhiteList                       : @{State=NotConfigured; path=ICA\VirtualChannelWhiteList} 
							VirtualChannelWhiteListLogging                : @{State=NotConfigured; value=LogWarningsOnly; path=ICA\VirtualChannelWhiteListLogging} 
							VirtualChannelWhiteListLogThrottling          : @{State=NotConfigured; value=2; path=ICA\VirtualChannelWhiteListLogThrottling} 
							AcceptWebSocketsConnections                   : @{State=NotConfigured; path=ICA\WebSockets\AcceptWebSocketsConnections} 
							WebSocketsPort                                : @{State=NotConfigured; value=8008; path=ICA\WebSockets\WebSocketsPort} 
							WSTrustedOriginServerList                     : @{State=NotConfigured; value=*; path=ICA\WebSockets\WSTrustedOriginServerList} 
							SessionReliabilityConnections                 : @{State=NotConfigured; path=ICA\SessionReliability\SessionReliabilityConnections} 
							SessionReliabilityPort                        : @{State=NotConfigured; value=2598; path=ICA\SessionReliability\SessionReliabilityPort} 
							SessionReliabilityTimeout                     : @{State=NotConfigured; value=180; path=ICA\SessionReliability\SessionReliabilityTimeout} 
							IdleTimerInterval                             : @{State=NotConfigured; value=0; path=ICA\ServerLimits\IdleTimerInterval} 
							LoadBalancedPrintServers                      : @{State=NotConfigured; path=ICA\Printing\UniversalPrintServer\LoadBalancedPrintServers} 
							PrintServersOutOfServiceThreshold             : @{State=NotConfigured; value=180; path=ICA\Printing\UniversalPrintServer\PrintServersOutOfServiceThreshold} 
							UpcHttpConnectTimeout                         : @{State=NotConfigured; value=10; path=ICA\Printing\UniversalPrintServer\UpcHttpConnectTimeout} 
							UpcHttpReceiveTimeout                         : @{State=NotConfigured; value=10; path=ICA\Printing\UniversalPrintServer\UpcHttpReceiveTimeout} 
							UpcHttpSendTimeout                            : @{State=NotConfigured; value=10; path=ICA\Printing\UniversalPrintServer\UpcHttpSendTimeout} 
							UpcSslCgpPort                                 : @{State=NotConfigured; value=443; path=ICA\Printing\UniversalPrintServer\UpcSslCgpPort} 
							UpcSslCipherSuite                             : @{State=NotConfigured; value=ALL; path=ICA\Printing\UniversalPrintServer\UpcSslCipherSuite} 
							UpcSslComplianceMode                          : @{State=NotConfigured; value=OPEN; path=ICA\Printing\UniversalPrintServer\UpcSslComplianceMode} 
							UpcSslEnable                                  : @{State=NotConfigured; path=ICA\Printing\UniversalPrintServer\UpcSslEnable} 
							UpcSslFips                                    : @{State=NotConfigured; path=ICA\Printing\UniversalPrintServer\UpcSslFips} 
							UpcSslHttpsPort                               : @{State=NotConfigured; value=8443; path=ICA\Printing\UniversalPrintServer\UpcSslHttpsPort} 
							UpcSslProtocolVersion                         : @{State=NotConfigured; value=TLS12; path=ICA\Printing\UniversalPrintServer\UpcSslProtocolVersion} 
							UpsCgpPort                                    : @{State=NotConfigured; value=7229; path=ICA\Printing\UniversalPrintServer\UpsCgpPort} 
							UpsEnable                                     : @{State=NotConfigured; value=UpsDisabled; path=ICA\Printing\UniversalPrintServer\UpsEnable} 
							UpsHttpPort                                   : @{State=NotConfigured; value=8080; path=ICA\Printing\UniversalPrintServer\UpsHttpPort} 
							HTML5VideoRedirection                         : @{State=NotConfigured; path=ICA\Multimedia\HTML5VideoRedirection} 
							MultimediaAcceleration                        : @{State=NotConfigured; path=ICA\Multimedia\MultimediaAcceleration} 
							MultimediaAccelerationDefaultBufferSize       : @{State=NotConfigured; value=5; path=ICA\Multimedia\MultimediaAccelerationDefaultBufferSize} 
							MultimediaAccelerationEnableCSF               : @{State=NotConfigured; path=ICA\Multimedia\MultimediaAccelerationEnableCSF} 
							MultimediaAccelerationUseDefaultBufferSize    : @{State=NotConfigured; path=ICA\Multimedia\MultimediaAccelerationUseDefaultBufferSize} 
							MultimediaConferencing                        : @{State=NotConfigured; path=ICA\Multimedia\MultimediaConferencing} 
							WebBrowserRedirection                         : @{State=NotConfigured; path=ICA\Multimedia\WebBrowserRedirection} 
							MultiPortPolicy                               : @{State=NotConfigured; value=; path=ICA\MSI\MultiPortPolicy} 
							MultiStreamAssignment                         : @{State=NotConfigured; value=CTXCAM ,0;CTXEUEM,1;CTXCTL ,1;CTXIME ,1;CTXLIC,1;CTXMTOP,1;CTXMOB,1;CTXMTCH,1;CTXTWI,1;CTXSENS,1;CTXSCRD,1;CTXTW 
							                                                ,1;CTXDND,1;CTXCSB,2;CTXCDM ,2;CTXCLIP,2;CTXFILE,2;CTXGDT ,2;CTXPFWD,2;CTXMM  ,2;CTXTUI,2;CTXTWN ,2;CTXGUSB,2;CTXZLFK,2;CTXZLC ,2;CTXCCM ,3;CTXCPM 
							                                                ,3;CTXCOM1,3;CTXCOM2,3;CTXLPT1,3;CTXLPT2,3; path=ICA\MSI\MultiStreamAssignment} 
							MultiStreamPolicy                             : @{State=NotConfigured; path=ICA\MSI\MultiStreamPolicy} 
							RtpAudioPortRange                             : @{State=NotConfigured; value=16500,16509; path=ICA\MSI\RtpAudioPortRange} 
							UDPAudioOnServer                              : @{State=NotConfigured; path=ICA\MSI\UDPAudioOnServer} 
							AllowLocalAppAccess                           : @{State=NotConfigured; path=ICA\LocalAppAccess\AllowLocalAppAccess} 
							URLRedirectionBlackList                       : @{State=NotConfigured; path=ICA\LocalAppAccess\URLRedirectionBlackList} 
							URLRedirectionWhiteList                       : @{State=NotConfigured; path=ICA\LocalAppAccess\URLRedirectionWhiteList} 
							IcaKeepAlives                                 : @{State=NotConfigured; value=DoNotSendKeepAlives; path=ICA\KeepAlive\IcaKeepAlives} 
							IcaKeepAliveTimeout                           : @{State=NotConfigured; value=60; path=ICA\KeepAlive\IcaKeepAliveTimeout} 
							AllowWindowsScreenLock                        : @{State=NotConfigured; path=ICA\Graphics\AllowWindowsScreenLock} 
							DisplayDegradePreference                      : @{State=NotConfigured; value=ColorDepth; path=ICA\Graphics\DisplayDegradePreference} 
							DisplayDegradeUserNotification                : @{State=NotConfigured; path=ICA\Graphics\DisplayDegradeUserNotification} 
							DisplayMemoryLimit                            : @{State=NotConfigured; value=0; path=ICA\Graphics\DisplayMemoryLimit} 
							DynamicPreview                                : @{State=NotConfigured; path=ICA\Graphics\DynamicPreview} 
							ImageCaching                                  : @{State=NotConfigured; path=ICA\Graphics\ImageCaching} 
							LegacyGraphicsMode                            : @{State=NotConfigured; path=ICA\Graphics\LegacyGraphicsMode} 
							MaximumColorDepth                             : @{State=NotConfigured; value=BitsPerPixel32; path=ICA\Graphics\MaximumColorDepth} 
							QueueingAndTossing                            : @{State=NotConfigured; path=ICA\Graphics\QueueingAndTossing} 
							FramehawkDisplayChannelPortRange              : @{State=NotConfigured; value=3224,3324; path=ICA\Graphics\Framehawk\FramehawkDisplayChannelPortRange} 
							PersistentCache                               : @{State=NotConfigured; value=3000000; path=ICA\Graphics\Caching\PersistentCache} 
							EnhancedDesktopExperience                     : @{State=NotConfigured; path=ICA\EnhancedDesktopExperience\EnhancedDesktopExperience} 
							EndpointMetricsCheckPeriod                    : @{State=NotConfigured; value=60; path=ICA\EndUserMonitoring\EndpointMetricsCheckPeriod} 
							IcaRoundTripCalculation                       : @{State=NotConfigured; path=ICA\EndUserMonitoring\IcaRoundTripCalculation} 
							IcaRoundTripCalculationInterval               : @{State=NotConfigured; value=15; path=ICA\EndUserMonitoring\IcaRoundTripCalculationInterval} 
							IcaRoundTripCalculationWhenIdle               : @{State=NotConfigured; path=ICA\EndUserMonitoring\IcaRoundTripCalculationWhenIdle} 
							ACRTimeout                                    : @{State=NotConfigured; value=120; path=ICA\AutoClientReconnect\ACRTimeout} 
							AutoClientReconnect                           : @{State=NotConfigured; path=ICA\AutoClientReconnect\AutoClientReconnect} 
							AutoClientReconnectAuthenticationRequired     : @{State=NotConfigured; value=DoNotRequireAuthentication; path=ICA\AutoClientReconnect\AutoClientReconnectAuthenticationRequired} 
							AutoClientReconnectLogging                    : @{State=NotConfigured; value=DoNotLogAutoReconnectEvents; path=ICA\AutoClientReconnect\AutoClientReconnectLogging} 
							ReconnectionUiTransparencyLevel               : @{State=NotConfigured; value=80; path=ICA\AutoClientReconnect\ReconnectionUiTransparencyLevel} 
							AppProtectionPostureCheck                     : @{State=NotConfigured; path=ICA\AppProtection\AppProtectionPostureCheck} 
							AdvanceWarningFrequency                       : @{State=NotConfigured; value=01:00:00; path=ConfigMgr\AdvanceWarningFrequency} 
							AdvanceWarningMessageBody                     : @{State=NotConfigured; value={TIMESTAMP} 
							 
							                                                Please save your work. The system will go offline for maintenance in {TIMELEFT}; path=ConfigMgr\AdvanceWarningMessageBody} 
							AdvanceWarningMessageTitle                    : @{State=NotConfigured; value=Upcoming Maintenance; path=ConfigMgr\AdvanceWarningMessageTitle} 
							AdvanceWarningPeriod                          : @{State=NotConfigured; value=16:00:00; path=ConfigMgr\AdvanceWarningPeriod} 
							AgentTaskInterval                             : @{State=NotConfigured; value=00:05:00; path=ConfigMgr\AgentTaskInterval} 
							FinalForceLogoffMessageBody                   : @{State=NotConfigured; value=The system is currently going offline for maintenance; path=ConfigMgr\FinalForceLogoffMessageBody} 
							FinalForceLogoffMessageTitle                  : @{State=NotConfigured; value=Notification From IT Staff; path=ConfigMgr\FinalForceLogoffMessageTitle} 
							ForceLogoffGracePeriod                        : @{State=NotConfigured; value=00:05:00; path=ConfigMgr\ForceLogoffGracePeriod} 
							ForceLogoffMessageBody                        : @{State=NotConfigured; value={TIMESTAMP} 
							 
							                                                Please save your work. The system will go offline for maintenance in {TIMELEFT}; path=ConfigMgr\ForceLogoffMessageBody} 
							ForceLogoffMessageTitle                       : @{State=NotConfigured; value=Notification From IT Staff; path=ConfigMgr\ForceLogoffMessageTitle} 
							ImageProviderIntegrationEnabled               : @{State=NotConfigured; path=ConfigMgr\ImageProviderIntegrationEnabled} 
							RebootMessageBody                             : @{State=NotConfigured; value=The system is currently going offline for maintenance; path=ConfigMgr\RebootMessageBody} 
							 
							PolicyName                               : Unfiltered 
							Type                                     : User 
							ProfileLoadTimeMonitoring_Enable         : @{State=NotConfigured; path=VirtualDesktopAgent\ProfileLoadTimeMonitoring\ProfileLoadTimeMonitoring_Enable} 
							ProfileLoadTimeMonitoring_Threshold      : @{State=NotConfigured; value=60; path=VirtualDesktopAgent\ProfileLoadTimeMonitoring\ProfileLoadTimeMonitoring_Threshold} 
							ICALatencyMonitoring_Enable              : @{State=NotConfigured; path=VirtualDesktopAgent\ICALatencyMonitoring\ICALatencyMonitoring_Enable} 
							ICALatencyMonitoring_Period              : @{State=NotConfigured; value=30; path=VirtualDesktopAgent\ICALatencyMonitoring\ICALatencyMonitoring_Period} 
							ICALatencyMonitoring_Threshold           : @{State=NotConfigured; value=200; path=VirtualDesktopAgent\ICALatencyMonitoring\ICALatencyMonitoring_Threshold} 
							EnableLossless                           : @{State=NotConfigured; path=VirtualDesktopAgent\HDX3DPro\EnableLossless} 
							ProGraphics                              : @{State=NotConfigured; value=6553600; path=VirtualDesktopAgent\HDX3DPro\ProGraphics} 
							FRVideos_Part                            : @{State=NotConfigured; value=RedirectUncPath; path=UserProfileManager\FolderRedirection\FRVideos\FRVideos_Part} 
							FRVideosMoveContentDisabled_Part         : @{State=NotConfigured; path=UserProfileManager\FolderRedirection\FRVideos\FRVideosMoveContentDisabled_Part} 
							FRVideosPath_Part                        : @{State=NotConfigured; value=; path=UserProfileManager\FolderRedirection\FRVideos\FRVideosPath_Part} 
							FRStartMenu_Part                         : @{State=NotConfigured; value=RedirectUncPath; path=UserProfileManager\FolderRedirection\FRStartMenu\FRStartMenu_Part} 
							FRStartMenuMoveContentDisabled_Part      : @{State=NotConfigured; path=UserProfileManager\FolderRedirection\FRStartMenu\FRStartMenuMoveContentDisabled_Part} 
							FRStartMenuPath_Part                     : @{State=NotConfigured; value=; path=UserProfileManager\FolderRedirection\FRStartMenu\FRStartMenuPath_Part} 
							FRSearches_Part                          : @{State=NotConfigured; value=RedirectUncPath; path=UserProfileManager\FolderRedirection\FRSearches\FRSearches_Part} 
							FRSearchesMoveContentDisabled_Part       : @{State=NotConfigured; path=UserProfileManager\FolderRedirection\FRSearches\FRSearchesMoveContentDisabled_Part} 
							FRSearchesPath_Part                      : @{State=NotConfigured; value=; path=UserProfileManager\FolderRedirection\FRSearches\FRSearchesPath_Part} 
							FRSavedGames_Part                        : @{State=NotConfigured; value=RedirectUncPath; path=UserProfileManager\FolderRedirection\FRSavedGames\FRSavedGames_Part} 
							FRSavedGamesMoveContentDisabled_Part     : @{State=NotConfigured; path=UserProfileManager\FolderRedirection\FRSavedGames\FRSavedGamesMoveContentDisabled_Part} 
							FRSavedGamesPath_Part                    : @{State=NotConfigured; value=; path=UserProfileManager\FolderRedirection\FRSavedGames\FRSavedGamesPath_Part} 
							FRPictures_Part                          : @{State=NotConfigured; value=RedirectUncPath; path=UserProfileManager\FolderRedirection\FRPictures\FRPictures_Part} 
							FRPicturesMoveContentDisabled_Part       : @{State=NotConfigured; path=UserProfileManager\FolderRedirection\FRPictures\FRPicturesMoveContentDisabled_Part} 
							FRPicturesPath_Part                      : @{State=NotConfigured; value=; path=UserProfileManager\FolderRedirection\FRPictures\FRPicturesPath_Part} 
							FRMusic_Part                             : @{State=NotConfigured; value=RedirectUncPath; path=UserProfileManager\FolderRedirection\FRMusic\FRMusic_Part} 
							FRMusicMoveContentDisabled_Part          : @{State=NotConfigured; path=UserProfileManager\FolderRedirection\FRMusic\FRMusicMoveContentDisabled_Part} 
							FRMusicPath_Part                         : @{State=NotConfigured; value=; path=UserProfileManager\FolderRedirection\FRMusic\FRMusicPath_Part} 
							FRLinks_Part                             : @{State=NotConfigured; value=RedirectUncPath; path=UserProfileManager\FolderRedirection\FRLinks\FRLinks_Part} 
							FRLinksMoveContentDisabled_Part          : @{State=NotConfigured; path=UserProfileManager\FolderRedirection\FRLinks\FRLinksMoveContentDisabled_Part} 
							FRLinksPath_Part                         : @{State=NotConfigured; value=; path=UserProfileManager\FolderRedirection\FRLinks\FRLinksPath_Part} 
							FRFavorites_Part                         : @{State=NotConfigured; value=RedirectUncPath; path=UserProfileManager\FolderRedirection\FRFavorites\FRFavorites_Part} 
							FRFavoritesMoveContentDisabled_Part      : @{State=NotConfigured; path=UserProfileManager\FolderRedirection\FRFavorites\FRFavoritesMoveContentDisabled_Part} 
							FRFavoritesPath_Part                     : @{State=NotConfigured; value=; path=UserProfileManager\FolderRedirection\FRFavorites\FRFavoritesPath_Part} 
							FRDownloads_Part                         : @{State=NotConfigured; value=RedirectUncPath; path=UserProfileManager\FolderRedirection\FRDownloads\FRDownloads_Part} 
							FRDownloadsMoveContentDisabled_Part      : @{State=NotConfigured; path=UserProfileManager\FolderRedirection\FRDownloads\FRDownloadsMoveContentDisabled_Part} 
							FRDownloadsPath_Part                     : @{State=NotConfigured; value=; path=UserProfileManager\FolderRedirection\FRDownloads\FRDownloadsPath_Part} 
							FRDocuments_Part                         : @{State=NotConfigured; value=RedirectUncPath; path=UserProfileManager\FolderRedirection\FRDocuments\FRDocuments_Part} 
							FRDocumentsMoveContentDisabled_Part      : @{State=NotConfigured; path=UserProfileManager\FolderRedirection\FRDocuments\FRDocumentsMoveContentDisabled_Part} 
							FRDocumentsPath_Part                     : @{State=NotConfigured; value=; path=UserProfileManager\FolderRedirection\FRDocuments\FRDocumentsPath_Part} 
							FRDesktop_Part                           : @{State=NotConfigured; value=RedirectUncPath; path=UserProfileManager\FolderRedirection\FRDesktop\FRDesktop_Part} 
							FRDesktopMoveContentDisabled_Part        : @{State=NotConfigured; path=UserProfileManager\FolderRedirection\FRDesktop\FRDesktopMoveContentDisabled_Part} 
							FRDesktopPath_Part                       : @{State=NotConfigured; value=; path=UserProfileManager\FolderRedirection\FRDesktop\FRDesktopPath_Part} 
							FRContacts_Part                          : @{State=NotConfigured; value=RedirectUncPath; path=UserProfileManager\FolderRedirection\FRContacts\FRContacts_Part} 
							FRContactsMoveContentDisabled_Part       : @{State=NotConfigured; path=UserProfileManager\FolderRedirection\FRContacts\FRContactsMoveContentDisabled_Part} 
							FRContactsPath_Part                      : @{State=NotConfigured; value=; path=UserProfileManager\FolderRedirection\FRContacts\FRContactsPath_Part} 
							FRAdminAccess_Part                       : @{State=NotConfigured; path=UserProfileManager\FolderRedirection\FRCommonSettings\FRAdminAccess_Part} 
							FRIncDomainName_Part                     : @{State=NotConfigured; path=UserProfileManager\FolderRedirection\FRCommonSettings\FRIncDomainName_Part} 
							FRAppData_Part                           : @{State=NotConfigured; value=RedirectUncPath; path=UserProfileManager\FolderRedirection\FRAppData\FRAppData_Part} 
							FRAppDataMoveContentDisabled_Part        : @{State=NotConfigured; path=UserProfileManager\FolderRedirection\FRAppData\FRAppDataMoveContentDisabled_Part} 
							FRAppDataPath_Part                       : @{State=NotConfigured; value=; path=UserProfileManager\FolderRedirection\FRAppData\FRAppDataPath_Part} 
							StorefrontAccountsList                   : @{State=NotConfigured; path=Receiver\StorefrontAccountsList} 
							AllowFidoRedirection                     : @{State=NotConfigured; path=ICA\AllowFidoRedirection} 
							AllowScannerSANERedirection              : @{State=NotConfigured; path=ICA\AllowScannerSANERedirection} 
							AllowWIARedirection                      : @{State=NotConfigured; path=ICA\AllowWIARedirection} 
							ClientClipboardWriteAllowedFormats       : @{State=NotConfigured; path=ICA\ClientClipboardWriteAllowedFormats} 
							ClipboardRedirection                     : @{State=NotConfigured; path=ICA\ClipboardRedirection} 
							ClipboardSelectionUpdateMode             : @{State=NotConfigured; value=AllUpdatesAllowed; path=ICA\ClipboardSelectionUpdateMode} 
							DesktopLaunchForNonAdmins                : @{State=NotConfigured; path=ICA\DesktopLaunchForNonAdmins} 
							DragDrop                                 : @{State=NotConfigured; path=ICA\DragDrop} 
							LimitClipboardTransferC2H                : @{State=NotConfigured; value=0; path=ICA\LimitClipboardTransferC2H} 
							LimitClipboardTransferH2C                : @{State=NotConfigured; value=0; path=ICA\LimitClipboardTransferH2C} 
							LossTolerantModeAvailable                : @{State=NotConfigured; path=ICA\LossTolerantModeAvailable} 
							NonPublishedProgramLaunching             : @{State=NotConfigured; path=ICA\NonPublishedProgramLaunching} 
							PrimarySelectionUpdateMode               : @{State=NotConfigured; value=AllUpdatesAllowed; path=ICA\PrimarySelectionUpdateMode} 
							ReadonlyClipboard                        : @{State=NotConfigured; path=ICA\ReadonlyClipboard} 
							RestrictClientClipboardWrite             : @{State=NotConfigured; path=ICA\RestrictClientClipboardWrite} 
							RestrictSessionClipboardWrite            : @{State=NotConfigured; path=ICA\RestrictSessionClipboardWrite} 
							SessionClipboardWriteAllowedFormats      : @{State=NotConfigured; path=ICA\SessionClipboardWriteAllowedFormats} 
							VirtualChannelPluginManager              : @{State=NotConfigured; path=ICA\VirtualChannelPluginManager} 
							FramesPerSecond                          : @{State=NotConfigured; value=30; path=ICA\VisualDisplay\FramesPerSecond} 
							PreferredColorDepthForSimpleGraphics     : @{State=NotConfigured; value=ColorDepth24Bit; path=ICA\VisualDisplay\PreferredColorDepthForSimpleGraphics} 
							VisualQuality                            : @{State=NotConfigured; value=Medium; path=ICA\VisualDisplay\VisualQuality} 
							ExtraColorCompression                    : @{State=NotConfigured; path=ICA\VisualDisplay\StillImages\ExtraColorCompression} 
							ExtraColorCompressionThreshold           : @{State=NotConfigured; value=8192; path=ICA\VisualDisplay\StillImages\ExtraColorCompressionThreshold} 
							LossyCompressionLevel                    : @{State=NotConfigured; value=Medium; path=ICA\VisualDisplay\StillImages\LossyCompressionLevel} 
							LossyCompressionThreshold                : @{State=NotConfigured; value=2147483647; path=ICA\VisualDisplay\StillImages\LossyCompressionThreshold} 
							ProgressiveHeavyweightCompression        : @{State=NotConfigured; path=ICA\VisualDisplay\StillImages\ProgressiveHeavyweightCompression} 
							MinimumAdaptiveDisplayJpegQuality        : @{State=NotConfigured; value=Normal; path=ICA\VisualDisplay\MovingImages\MinimumAdaptiveDisplayJpegQuality} 
							MovingImageCompressionConfiguration      : @{State=NotConfigured; path=ICA\VisualDisplay\MovingImages\MovingImageCompressionConfiguration} 
							ProgressiveCompressionLevel              : @{State=NotConfigured; value=None; path=ICA\VisualDisplay\MovingImages\ProgressiveCompressionLevel} 
							ProgressiveCompressionThreshold          : @{State=NotConfigured; value=2147483647; path=ICA\VisualDisplay\MovingImages\ProgressiveCompressionThreshold} 
							TargetedMinimumFramesPerSecond           : @{State=NotConfigured; value=10; path=ICA\VisualDisplay\MovingImages\TargetedMinimumFramesPerSecond} 
							ClientUsbDeviceOptimizationRules         : @{State=NotConfigured; path=ICA\USBDevices\ClientUsbDeviceOptimizationRules} 
							UsbConnectExistingDevices                : @{State=NotConfigured; value=Ask; path=ICA\USBDevices\UsbConnectExistingDevices} 
							UsbConnectNewDevices                     : @{State=NotConfigured; value=Ask; path=ICA\USBDevices\UsbConnectNewDevices} 
							UsbDeviceRedirection                     : @{State=NotConfigured; path=ICA\USBDevices\UsbDeviceRedirection} 
							UsbDeviceRedirectionRules                : @{State=NotConfigured; path=ICA\USBDevices\UsbDeviceRedirectionRules} 
							USBDeviceRulesV2                         : @{State=NotConfigured; Values=System.Object[]; path=ICA\USBDevices\USBDeviceRulesV2} 
							UsbPlugAndPlayRedirection                : @{State=NotConfigured; path=ICA\USBDevices\UsbPlugAndPlayRedirection} 
							TwainCompressionLevel                    : @{State=NotConfigured; value=Medium; path=ICA\TWAINDevices\TwainCompressionLevel} 
							TwainRedirection                         : @{State=NotConfigured; path=ICA\TWAINDevices\TwainRedirection} 
							LocalTimeEstimation                      : @{State=NotConfigured; path=ICA\TimeZoneControl\LocalTimeEstimation} 
							RestoreServerTime                        : @{State=NotConfigured; path=ICA\TimeZoneControl\RestoreServerTime} 
							SessionTimeZone                          : @{State=NotConfigured; value=UseServerTimeZone; path=ICA\TimeZoneControl\SessionTimeZone} 
							EnableSessionWatermark                   : @{State=NotConfigured; path=ICA\SessionWatermark\EnableSessionWatermark} 
							WatermarkStyle                           : @{State=NotConfigured; value=StyleMultiple; path=ICA\SessionWatermark\WatermarkStyle\WatermarkStyle} 
							WatermarkTransparency                    : @{State=NotConfigured; value=17; path=ICA\SessionWatermark\WatermarkStyle\WatermarkTransparency} 
							WatermarkCustomText                      : @{State=NotConfigured; value=; path=ICA\SessionWatermark\WatermarkContent\WatermarkCustomText} 
							WatermarkIncludeClientIPAddress          : @{State=NotConfigured; path=ICA\SessionWatermark\WatermarkContent\WatermarkIncludeClientIPAddress} 
							WatermarkIncludeConnectTime              : @{State=NotConfigured; path=ICA\SessionWatermark\WatermarkContent\WatermarkIncludeConnectTime} 
							WatermarkIncludeLogonUsername            : @{State=NotConfigured; path=ICA\SessionWatermark\WatermarkContent\WatermarkIncludeLogonUsername} 
							WatermarkIncludeVDAHostName              : @{State=NotConfigured; path=ICA\SessionWatermark\WatermarkContent\WatermarkIncludeVDAHostName} 
							WatermarkIncludeVDAIPAddress             : @{State=NotConfigured; path=ICA\SessionWatermark\WatermarkContent\WatermarkIncludeVDAIPAddress} 
							EnableRemotePCDisconnectTimer            : @{State=NotConfigured; path=ICA\SessionLimits\EnableRemotePCDisconnectTimer} 
							SessionConnectionTimer                   : @{State=NotConfigured; path=ICA\SessionLimits\SessionConnectionTimer} 
							SessionConnectionTimerInterval           : @{State=NotConfigured; value=1440; path=ICA\SessionLimits\SessionConnectionTimerInterval} 
							SessionDisconnectTimer                   : @{State=NotConfigured; path=ICA\SessionLimits\SessionDisconnectTimer} 
							SessionDisconnectTimerInterval           : @{State=NotConfigured; value=1440; path=ICA\SessionLimits\SessionDisconnectTimerInterval} 
							SessionIdleTimer                         : @{State=NotConfigured; path=ICA\SessionLimits\SessionIdleTimer} 
							SessionIdleTimerInterval                 : @{State=NotConfigured; value=1440; path=ICA\SessionLimits\SessionIdleTimerInterval} 
							LossTolerantThresholds                   : @{State=NotConfigured; value=loss,5;latency,300;; path=ICA\SessionInteractivity\LossTolerantThresholds} 
							EnableServerConnectionTimer              : @{State=NotConfigured; path=ICA\ServerLimits\EnableServerConnectionTimer} 
							EnableServerDisconnectionTimer           : @{State=NotConfigured; path=ICA\ServerLimits\EnableServerDisconnectionTimer} 
							EnableServerIdleTimer                    : @{State=NotConfigured; path=ICA\ServerLimits\EnableServerIdleTimer} 
							ServerConnectionTimerInterval            : @{State=NotConfigured; value=1440; path=ICA\ServerLimits\ServerConnectionTimerInterval} 
							ServerDisconnectionTimerInterval         : @{State=NotConfigured; value=1440; path=ICA\ServerLimits\ServerDisconnectionTimerInterval} 
							ServerIdleTimerInterval                  : @{State=NotConfigured; value=1440; path=ICA\ServerLimits\ServerIdleTimerInterval} 
							MinimumEncryptionLevel                   : @{State=NotConfigured; value=Basic; path=ICA\Security\MinimumEncryptionLevel} 
							AutoCreationEventLogPreference           : @{State=NotConfigured; value=LogErrorsAndWarnings; path=ICA\Printing\AutoCreationEventLogPreference} 
							ClientPrinterRedirection                 : @{State=NotConfigured; path=ICA\Printing\ClientPrinterRedirection} 
							DefaultClientPrinter                     : @{State=NotConfigured; value=ClientDefault; path=ICA\Printing\DefaultClientPrinter} 
							PrinterAssignments                       : @{State=NotConfigured; path=ICA\Printing\PrinterAssignments} 
							SessionPrinters                          : @{State=NotConfigured; path=ICA\Printing\SessionPrinters} 
							WaitForPrintersToBeCreated               : @{State=NotConfigured; path=ICA\Printing\WaitForPrintersToBeCreated} 
							UpsPrintStreamInputBandwidthLimit        : @{State=NotConfigured; value=0; path=ICA\Printing\UniversalPrintServer\UpsPrintStreamInputBandwidthLimit} 
							DPILimit                                 : @{State=NotConfigured; value=Unlimited; path=ICA\Printing\UniversalPrinting\DPILimit} 
							EMFProcessingMode                        : @{State=NotConfigured; value=SpoolDirectlyToPrinter; path=ICA\Printing\UniversalPrinting\EMFProcessingMode} 
							ImageCompressionLimit                    : @{State=NotConfigured; value=LosslessCompression; path=ICA\Printing\UniversalPrinting\ImageCompressionLimit} 
							UniversalPrintingPreviewPreference       : @{State=NotConfigured; value=NoPrintPreview; path=ICA\Printing\UniversalPrinting\UniversalPrintingPreviewPreference} 
							UPDCompressionDefaults                   : @{State=NotConfigured; 
							                                           value=ImageCompression=StandardQuality,HeavyweightCompression=False,ImageCaching=True,FontCaching=True,AllowNonAdminsToModify=False; 
							                                           path=ICA\Printing\UniversalPrinting\UPDCompressionDefaults} 
							InboxDriverAutoInstallation              : @{State=NotConfigured; path=ICA\Printing\Drivers\InboxDriverAutoInstallation} 
							UniversalDriverPriority                  : @{State=NotConfigured; Values=System.Object[]; value=EMF;XPS;PCL5c;PCL4;PS;PDF; path=ICA\Printing\Drivers\UniversalDriverPriority} 
							UniversalPrintDriverUsage                : @{State=NotConfigured; value=FallbackToUpd; path=ICA\Printing\Drivers\UniversalPrintDriverUsage} 
							AutoCreatePDFPrinter                     : @{State=NotConfigured; path=ICA\Printing\ClientPrinters\AutoCreatePDFPrinter} 
							ClientPrinterAutoCreation                : @{State=NotConfigured; value=AllPrinters; path=ICA\Printing\ClientPrinters\ClientPrinterAutoCreation} 
							ClientPrinterNames                       : @{State=NotConfigured; value=StandardPrinterNames; path=ICA\Printing\ClientPrinters\ClientPrinterNames} 
							DirectConnectionsToPrintServers          : @{State=NotConfigured; path=ICA\Printing\ClientPrinters\DirectConnectionsToPrintServers} 
							GenericUniversalPrinterAutoCreation      : @{State=NotConfigured; path=ICA\Printing\ClientPrinters\GenericUniversalPrinterAutoCreation} 
							PrinterDriverMappings                    : @{State=NotConfigured; Values=System.Object[]; path=ICA\Printing\ClientPrinters\PrinterDriverMappings} 
							PrinterPropertiesRetention               : @{State=NotConfigured; value=FallbackToProfile; path=ICA\Printing\ClientPrinters\PrinterPropertiesRetention} 
							ClientComPortRedirection                 : @{State=NotConfigured; path=ICA\Ports\ClientComPortRedirection} 
							ClientComPortsAutoConnection             : @{State=NotConfigured; path=ICA\Ports\ClientComPortsAutoConnection} 
							ClientLptPortRedirection                 : @{State=NotConfigured; path=ICA\Ports\ClientLptPortRedirection} 
							ClientLptPortsAutoConnection             : @{State=NotConfigured; path=ICA\Ports\ClientLptPortsAutoConnection} 
							MaxSpeexQuality                          : @{State=NotConfigured; value=5; path=ICA\Multimedia\MaxSpeexQuality} 
							MSTeamsRedirection                       : @{State=NotConfigured; path=ICA\Multimedia\MSTeamsRedirection} 
							MultimediaOptimization                   : @{State=NotConfigured; path=ICA\Multimedia\MultimediaOptimization} 
							UseGPUForMultimediaOptimization          : @{State=NotConfigured; path=ICA\Multimedia\UseGPUForMultimediaOptimization} 
							VideoLoadManagement                      : @{State=NotConfigured; value=Unconfigured; path=ICA\Multimedia\VideoLoadManagement} 
							VideoQuality                             : @{State=NotConfigured; value=Unconfigured; path=ICA\Multimedia\VideoQuality} 
							WebBrowserRedirectionAcl                 : @{State=NotConfigured; Values=System.Object[]; path=ICA\Multimedia\WebBrowserRedirectionAcl} 
							WebBrowserRedirectionAuthenticationSites : @{State=NotConfigured; path=ICA\Multimedia\WebBrowserRedirectionAuthenticationSites} 
							WebBrowserRedirectionBlacklist           : @{State=NotConfigured; path=ICA\Multimedia\WebBrowserRedirectionBlacklist} 
							WebBrowserRedirectionIwaSupport          : @{State=NotConfigured; path=ICA\Multimedia\WebBrowserRedirectionIwaSupport} 
							WebBrowserRedirectionProxy               : @{State=NotConfigured; value=; path=ICA\Multimedia\WebBrowserRedirectionProxy} 
							WebBrowserRedirectionProxyAuth           : @{State=NotConfigured; path=ICA\Multimedia\WebBrowserRedirectionProxyAuth} 
							MultiStream                              : @{State=NotConfigured; path=ICA\MSI\MultiStream} 
							AutoKeyboardPopUp                        : @{State=NotConfigured; path=ICA\MobileExperience\AutoKeyboardPopUp} 
							ComboboxRemoting                         : @{State=NotConfigured; path=ICA\MobileExperience\ComboboxRemoting} 
							MobileDesktop                            : @{State=NotConfigured; path=ICA\MobileExperience\MobileDesktop} 
							TabletModeToggle                         : @{State=NotConfigured; path=ICA\MobileExperience\TabletModeToggle} 
							ClientKeyboardLayoutSyncAndIME           : @{State=NotConfigured; value=NotSupport; path=ICA\KeyboardAndIME\ClientKeyboardLayoutSyncAndIME} 
							EnableUnicodeKeyboardLayoutMapping       : @{State=NotConfigured; path=ICA\KeyboardAndIME\EnableUnicodeKeyboardLayoutMapping} 
							HideKeyboardLayoutSwitchPopupMessageBox  : @{State=NotConfigured; path=ICA\KeyboardAndIME\HideKeyboardLayoutSwitchPopupMessageBox} 
							AllowVisuallyLosslessCompression         : @{State=NotConfigured; path=ICA\Graphics\AllowVisuallyLosslessCompression} 
							DisplayLosslessIndicator                 : @{State=NotConfigured; path=ICA\Graphics\DisplayLosslessIndicator} 
							OptimizeFor3dWorkload                    : @{State=NotConfigured; path=ICA\Graphics\OptimizeFor3dWorkload} 
							ScreenSharing                            : @{State=NotConfigured; path=ICA\Graphics\ScreenSharing} 
							UseHardwareEncodingForVideoCodec         : @{State=NotConfigured; path=ICA\Graphics\UseHardwareEncodingForVideoCodec} 
							UseVideoCodecForCompression              : @{State=NotConfigured; value=UseVideoCodecIfPreferred; path=ICA\Graphics\UseVideoCodecForCompression} 
							EnableFramehawkDisplayChannel            : @{State=NotConfigured; path=ICA\Graphics\Framehawk\EnableFramehawkDisplayChannel} 
							AllowFileDownload                        : @{State=NotConfigured; path=ICA\FileRedirection\AllowFileDownload} 
							AllowFileTransfer                        : @{State=NotConfigured; path=ICA\FileRedirection\AllowFileTransfer} 
							AllowFileUpload                          : @{State=NotConfigured; path=ICA\FileRedirection\AllowFileUpload} 
							AsynchronousWrites                       : @{State=NotConfigured; path=ICA\FileRedirection\AsynchronousWrites} 
							AutoConnectDrives                        : @{State=NotConfigured; path=ICA\FileRedirection\AutoConnectDrives} 
							ClientDriveLetterPreservation            : @{State=NotConfigured; path=ICA\FileRedirection\ClientDriveLetterPreservation} 
							ClientDriveRedirection                   : @{State=NotConfigured; path=ICA\FileRedirection\ClientDriveRedirection} 
							ClientFixedDrives                        : @{State=NotConfigured; path=ICA\FileRedirection\ClientFixedDrives} 
							ClientFloppyDrives                       : @{State=NotConfigured; path=ICA\FileRedirection\ClientFloppyDrives} 
							ClientNetworkDrives                      : @{State=NotConfigured; path=ICA\FileRedirection\ClientNetworkDrives} 
							ClientOpticalDrives                      : @{State=NotConfigured; path=ICA\FileRedirection\ClientOpticalDrives} 
							ClientRemoveableDrives                   : @{State=NotConfigured; path=ICA\FileRedirection\ClientRemoveableDrives} 
							HostToClientRedirection                  : @{State=NotConfigured; path=ICA\FileRedirection\HostToClientRedirection} 
							ReadOnlyMappedDrive                      : @{State=NotConfigured; path=ICA\FileRedirection\ReadOnlyMappedDrive} 
							SpecialFolderRedirection                 : @{State=NotConfigured; path=ICA\FileRedirection\SpecialFolderRedirection} 
							AeroRedirection                          : @{State=NotConfigured; path=ICA\DesktopUI\AeroRedirection} 
							DesktopWallpaper                         : @{State=NotConfigured; path=ICA\DesktopUI\DesktopWallpaper} 
							GraphicsQuality                          : @{State=NotConfigured; value=Medium; path=ICA\DesktopUI\GraphicsQuality} 
							MenuAnimation                            : @{State=NotConfigured; path=ICA\DesktopUI\MenuAnimation} 
							WindowContentsVisibleWhileDragging       : @{State=NotConfigured; path=ICA\DesktopUI\WindowContentsVisibleWhileDragging} 
							AllowLocationServices                    : @{State=NotConfigured; path=ICA\ClientSensors\Location\AllowLocationServices} 
							AllowBidirectionalContentRedirection     : @{State=NotConfigured; path=ICA\BidirectionalContentRedirection\AllowBidirectionalContentRedirection} 
							BidirectionalRedirectionConfig           : @{State=NotConfigured; value=; path=ICA\BidirectionalContentRedirection\BidirectionalRedirectionConfig} 
							ClientURLs                               : @{State=NotConfigured; value=; path=ICA\BidirectionalContentRedirection\ClientURLs} 
							VDAURLs                                  : @{State=NotConfigured; value=; path=ICA\BidirectionalContentRedirection\VDAURLs} 
							AudioBandwidthLimit                      : @{State=NotConfigured; value=0; path=ICA\Bandwidth\AudioBandwidthLimit} 
							AudioBandwidthPercent                    : @{State=NotConfigured; value=0; path=ICA\Bandwidth\AudioBandwidthPercent} 
							ClipboardBandwidthLimit                  : @{State=NotConfigured; value=0; path=ICA\Bandwidth\ClipboardBandwidthLimit} 
							ClipboardBandwidthPercent                : @{State=NotConfigured; value=0; path=ICA\Bandwidth\ClipboardBandwidthPercent} 
							ComPortBandwidthLimit                    : @{State=NotConfigured; value=0; path=ICA\Bandwidth\ComPortBandwidthLimit} 
							ComPortBandwidthPercent                  : @{State=NotConfigured; value=0; path=ICA\Bandwidth\ComPortBandwidthPercent} 
							FileRedirectionBandwidthLimit            : @{State=NotConfigured; value=0; path=ICA\Bandwidth\FileRedirectionBandwidthLimit} 
							FileRedirectionBandwidthPercent          : @{State=NotConfigured; value=0; path=ICA\Bandwidth\FileRedirectionBandwidthPercent} 
							HDXMultimediaBandwidthLimit              : @{State=NotConfigured; value=0; path=ICA\Bandwidth\HDXMultimediaBandwidthLimit} 
							HDXMultimediaBandwidthPercent            : @{State=NotConfigured; value=0; path=ICA\Bandwidth\HDXMultimediaBandwidthPercent} 
							LptBandwidthLimit                        : @{State=NotConfigured; value=0; path=ICA\Bandwidth\LptBandwidthLimit} 
							LptBandwidthLimitPercent                 : @{State=NotConfigured; value=0; path=ICA\Bandwidth\LptBandwidthLimitPercent} 
							OverallBandwidthLimit                    : @{State=NotConfigured; value=0; path=ICA\Bandwidth\OverallBandwidthLimit} 
							PrinterBandwidthLimit                    : @{State=NotConfigured; value=0; path=ICA\Bandwidth\PrinterBandwidthLimit} 
							PrinterBandwidthPercent                  : @{State=NotConfigured; value=0; path=ICA\Bandwidth\PrinterBandwidthPercent} 
							TwainBandwidthLimit                      : @{State=NotConfigured; value=0; path=ICA\Bandwidth\TwainBandwidthLimit} 
							TwainBandwidthPercent                    : @{State=NotConfigured; value=0; path=ICA\Bandwidth\TwainBandwidthPercent} 
							USBBandwidthLimit                        : @{State=NotConfigured; value=0; path=ICA\Bandwidth\USBBandwidthLimit} 
							USBBandwidthPercent                      : @{State=NotConfigured; value=0; path=ICA\Bandwidth\USBBandwidthPercent} 
							AllowRtpAudio                            : @{State=NotConfigured; path=ICA\Audio\AllowRtpAudio} 
							AudioPlugNPlay                           : @{State=NotConfigured; path=ICA\Audio\AudioPlugNPlay} 
							AudioQuality                             : @{State=NotConfigured; value=High; path=ICA\Audio\AudioQuality} 
							ClientAudioRedirection                   : @{State=NotConfigured; path=ICA\Audio\ClientAudioRedirection} 
							EnableAdaptiveAudio                      : @{State=NotConfigured; path=ICA\Audio\EnableAdaptiveAudio} 
							LossTolerantAudio                        : @{State=NotConfigured; path=ICA\Audio\LossTolerantAudio} 
							MicrophoneRedirection                    : @{State=NotConfigured; path=ICA\Audio\MicrophoneRedirection} 
							FlashAcceleration                        : @{State=NotConfigured; path=ICA\AdobeFlashDelivery\HDXMediaStreamForFlash\FlashAcceleration} 
							FlashBackwardsCompatibility              : @{State=NotConfigured; path=ICA\AdobeFlashDelivery\HDXMediaStreamForFlash\FlashBackwardsCompatibility} 
							FlashDefaultBehavior                     : @{State=NotConfigured; value=Enable; path=ICA\AdobeFlashDelivery\HDXMediaStreamForFlash\FlashDefaultBehavior} 
							FlashEventLogging                        : @{State=NotConfigured; path=ICA\AdobeFlashDelivery\HDXMediaStreamForFlash\FlashEventLogging} 
							FlashIntelligentFallback                 : @{State=NotConfigured; path=ICA\AdobeFlashDelivery\HDXMediaStreamForFlash\FlashIntelligentFallback} 
							FlashLatencyThreshold                    : @{State=NotConfigured; value=30; path=ICA\AdobeFlashDelivery\HDXMediaStreamForFlash\FlashLatencyThreshold} 
							FlashServerSideContentFetchingWhitelist  : @{State=NotConfigured; path=ICA\AdobeFlashDelivery\HDXMediaStreamForFlash\FlashServerSideContentFetchingWhitelist} 
							FlashUrlColorList                        : @{State=NotConfigured; path=ICA\AdobeFlashDelivery\HDXMediaStreamForFlash\FlashUrlColorList} 
							FlashUrlCompatibilityList                : @{State=NotConfigured; path=ICA\AdobeFlashDelivery\HDXMediaStreamForFlash\FlashUrlCompatibilityList} 
							HDXFlashLoadManagement                   : @{State=NotConfigured; value=Unconfigured; path=ICA\AdobeFlashDelivery\HDXMediaStreamForFlash\HDXFlashLoadManagement} 
							HDXFlashLoadManagementErrorSwf           : @{State=NotConfigured; value=http://domainName.tld/samplePath/error.swf; 
							                                           path=ICA\AdobeFlashDelivery\HDXMediaStreamForFlash\HDXFlashLoadManagementErrorSwf} 
							 
							 
							PS C:\_TACG&gt; 
						 
					
				
			
		
	



	 
	If you create a policy, you must filter it based on specific needs. 
	There are different Policy Filters available – you can find more information at https://github.com/citrix/terraform-provider-citrix/blob/main/internal/daas/policies/policy_set_resource.md#available-policy-filters
 


	As an example for filtering, we need to determine the SID of an AD group and the SID of an AD user using PowerShell:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\_TACG&gt; Import-Module ActiveDirectory                                                                
					 

					
						PS C:\_TACG&gt; Get-ADGroup -Identity CtxAdmins | Select-Object Name, SID
					 

					
						Name      SID
					 

					
						----      ---
					 

					
						CtxAdmins S-1-5-21-3919795815-764310115-1491410927-2134
					 

					
						 
						PS C:\_TACG&gt;
					 
				
			
		
	



	 
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\_TACG&gt; Import-Module ActiveDirectory                                                                
					 

					
						PS C:\_TACG&gt; Get-ADUser -Identity CVADAdmin | Select-Object Name, SID
					 

					
						Name      SID
					 

					
						----      ---
					 

					
						CVAD Admin S-1-5-21-3919795815-764310115-1491410927-2133
					 

					
						 
						PS C:\_TACG&gt;
					 
				
			
		
	



	 
 


	
		Important:
	 

	
		Please ensure that the site is based on Citrix Virtual Apps and Desktops 2402 LTSR CU1 to be supported. 
		Please ensure a valid site backup before running the Terraform scripts, as these will modify your site deployment. 
		 
	 



	
		Important:
	 

	
		If you use the Terraform provider version 1.0.4 or higher, deploying Policy sets on Citrix Cloud-based environments is also supported now. 
		 
	 



	We have now determined all prerequisites needed to create policies using Terraform.
 


	Creating the Policy Set



	We have all the prerequisites and can use Terraform to create a Policy Set in our Citrix Virtual Apps and Desktops site. With Policy Sets, you can group Policies together for simplified, role-based access control. 
	 
 


	
		Important:
	 

	
		A specific setting in Web Studio must be enabled to show Policy Sets in the Policies node.  
		 
	 



	
 


	 
 


	
		Important:
	 

	
		Before starting the Terraform workflow, please ensure you have configured the variables according to your needs. 
		 
	 



	The configuration can be started by following the standard Terraform workflow:
 


	
		terraform init, 
		terraform plan
	 



	and if no errors occur
 


	
		terraform apply
	 



	 
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\_TACG\_TFForScopesAndPolicies&gt; terraform init 
						Initializing the backend... 
						Initializing provider plugins... 
						- Finding citrix/citrix versions matching "1.0.1"... 
						- Installing citrix/citrix v1.0.1... 
						- Installed citrix/citrix v1.0.1 (self-signed, key ID BD4BD0E690CB7D88) 
						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\_TFForScopesAndPolicies&gt; terraform plan 
						data.citrix_admin_scope.GetDefaultAdminScope: Reading... 
						data.citrix_delivery_group.GetDeliveryGroupNameForFiltering: Reading... 
						data.citrix_machine_catalog.GetMachineCatalog: Reading... 
						data.citrix_admin_scope.GetDefaultAdminScope: Read complete after 0s [id=00000000-0000-0000-0000-000000000000] 
						data.citrix_delivery_group.GetDeliveryGroupNameForFiltering: Read complete after 1s [id=e2b86f9f-cf26-432d-9a09-0364f9d12787] 
						data.citrix_machine_catalog.GetMachineCatalog: Read complete after 1s [id=df6e86e3-1941-4bdd-9a2d-677605822c92] 
						 
						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_policy_set.CreatePolicyExampleSet will be created 
						  + resource "citrix_policy_set" "CreatePolicyExampleSet" { 
						      + assigned    = (known after apply) 
						      + description = "Terraform-created Policy Set example" 
						      + id          = (known after apply) 
						      + name        = "TF-PolSet-IPS" 
						      + policies    = [ 
						          + { 
						              + access_control_filters      = (known after apply) 
						              + client_ip_filters           = (known after apply) 
						              + client_name_filters         = (known after apply) 
						              + delivery_group_filters      = [ 
						                  + { 
						                      + allowed           = true 
						                      + delivery_group_id = "e2b86f9f-cf26-432d-9a09-0364f9d12787" 
						                      + enabled           = true 
						                    }, 
						                ] 
						              + delivery_group_type_filters = (known after apply) 
						              + description                 = "Example of Printer-related policy set" 
						              + enabled                     = true 
						              + name                        = "Printing" 
						              + ou_filters                  = (known after apply) 
						              + policy_settings             = [ 
						                  + { 
						                      + enabled     = (known after apply) 
						                      + name        = "ClientPrinterAutoCreation" 
						                      + use_default = false 
						                      + value       = "DefaultPrinterOnly" 
						                    }, 
						                  + { 
						                      + enabled     = (known after apply) 
						                      + name        = "UniversalPrintDriverUsage" 
						                      + use_default = false 
						                      + value       = "FallbackToSpecific" 
						                    }, 
						                ] 
						              + tag_filters                 = (known after apply) 
						              + user_filters                = [ 
						                  + { 
						                      + allowed = true 
						                      + enabled = true 
						                      + sid     = "S-1-5-21-3919795815-764310115-1491410927-2134" 
						                    }, 
						                ] 
						            }, 
						          + { 
						              + access_control_filters      = (known after apply) 
						              + client_ip_filters           = [ 
						                  + { 
						                      + allowed    = true 
						                      + enabled    = true 
						                      + ip_address = "10.10.0.0" 
						                    }, 
						                ] 
						              + client_name_filters         = (known after apply) 
						              + delivery_group_filters      = [ 
						                  + { 
						                      + allowed           = true 
						                      + delivery_group_id = "e2b86f9f-cf26-432d-9a09-0364f9d12787" 
						                      + enabled           = true 
						                    }, 
						                ] 
						              + delivery_group_type_filters = (known after apply) 
						              + description                 = "Example of HDX Graphics-related policy set" 
						              + enabled                     = true 
						              + name                        = "HDX Graphics" 
						              + ou_filters                  = (known after apply) 
						              + policy_settings             = [ 
						                  + { 
						                      + enabled     = true 
						                      + name        = "AllowVisuallyLosslessCompression" 
						                      + use_default = false 
						                      + value       = (known after apply) 
						                    }, 
						                  + { 
						                      + enabled     = (known after apply) 
						                      + name        = "UseVideoCodecForCompression" 
						                      + use_default = false 
						                      + value       = "UseVideoCodecIfPreferred" 
						                    }, 
						                  + { 
						                      + enabled     = true 
						                      + name        = "UseHardwareEncodingForVideoCodec" 
						                      + use_default = false 
						                      + value       = (known after apply) 
						                    }, 
						                ] 
						              + tag_filters                 = (known after apply) 
						              + user_filters                = [ 
						                  + { 
						                      + allowed = true 
						                      + enabled = true 
						                      + sid     = "S-1-5-21-3919795815-764310115-1491410927-2134" 
						                    }, 
						                ] 
						            }, 
						          + { 
						              + access_control_filters      = (known after apply) 
						              + client_ip_filters           = [ 
						                  + { 
						                      + allowed    = true 
						                      + enabled    = true 
						                      + ip_address = "10.10.0.0" 
						                    }, 
						                ] 
						              + client_name_filters         = (known after apply) 
						              + delivery_group_filters      = [ 
						                  + { 
						                      + allowed           = true 
						                      + delivery_group_id = "e2b86f9f-cf26-432d-9a09-0364f9d12787" 
						                      + enabled           = true 
						                    }, 
						                ] 
						              + delivery_group_type_filters = (known after apply) 
						              + description                 = "Example of Client Drive-related policy set" 
						              + enabled                     = true 
						              + name                        = "Client Drives" 
						              + ou_filters                  = (known after apply) 
						              + policy_settings             = [ 
						                  + { 
						                      + enabled     = true 
						                      + name        = "AutoConnectDrives" 
						                      + use_default = false 
						                      + value       = (known after apply) 
						                    }, 
						                  + { 
						                      + enabled     = true 
						                      + name        = "ClientDriveRedirection" 
						                      + use_default = false 
						                      + value       = (known after apply) 
						                    }, 
						                  + { 
						                      + enabled     = true 
						                      + name        = "ClientFixedDrives" 
						                      + use_default = false 
						                      + value       = (known after apply) 
						                    }, 
						                  + { 
						                      + enabled     = false 
						                      + name        = "ClientFloppyDrives" 
						                      + use_default = false 
						                      + value       = (known after apply) 
						                    }, 
						                  + { 
						                      + enabled     = false 
						                      + name        = "ClientOpticalDrives" 
						                      + use_default = false 
						                      + value       = (known after apply) 
						                    }, 
						                  + { 
						                      + enabled     = false 
						                      + name        = "ClientNetworkDrives" 
						                      + use_default = false 
						                      + value       = (known after apply) 
						                    }, 
						                ] 
						              + tag_filters                 = (known after apply) 
						              + user_filters                = [ 
						                  + { 
						                      + allowed = true 
						                      + enabled = true 
						                      + sid     = "S-1-5-21-3919795815-764310115-1491410927-2134" 
						                    }, 
						                ] 
						            }, 
						        ] 
						      + scopes      = [] 
						      + type        = "DeliveryGroupPolicies" 
						    } 
						 
						Plan: 1 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\_TFForScopesAndPolicies&gt; terraform validate 
						Success! The configuration is valid. 
						 
						PS C:\_TACG\_TFForScopesAndPolicies&gt; terraform apply 
						data.citrix_admin_scope.GetDefaultAdminScope: Reading... 
						data.citrix_machine_catalog.GetMachineCatalog: Reading... 
						data.citrix_delivery_group.GetDeliveryGroupNameForFiltering: Reading... 
						data.citrix_admin_scope.GetDefaultAdminScope: Read complete after 0s [id=00000000-0000-0000-0000-000000000000] 
						data.citrix_delivery_group.GetDeliveryGroupNameForFiltering: Read complete after 0s [id=e2b86f9f-cf26-432d-9a09-0364f9d12787] 
						data.citrix_machine_catalog.GetMachineCatalog: Read complete after 0s [id=df6e86e3-1941-4bdd-9a2d-677605822c92] 
						 
						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_policy_set.CreatePolicyExampleSet will be created 
						  + resource "citrix_policy_set" "CreatePolicyExampleSet" { 
						      + assigned    = (known after apply) 
						      + description = "Terraform-created Policy Set example" 
						      + id          = (known after apply) 
						      + name        = "TF-PolSet-IPS" 
						      + policies    = [ 
						          + { 
						              + access_control_filters      = (known after apply) 
						              + client_ip_filters           = (known after apply) 
						              + client_name_filters         = (known after apply) 
						              + delivery_group_filters      = [ 
						                  + { 
						                      + allowed           = true 
						                      + delivery_group_id = "e2b86f9f-cf26-432d-9a09-0364f9d12787" 
						                      + enabled           = true 
						                    }, 
						                ] 
						              + delivery_group_type_filters = (known after apply) 
						              + description                 = "Example of Printer-related policy set" 
						              + enabled                     = true 
						              + name                        = "Printing" 
						              + ou_filters                  = (known after apply) 
						              + policy_settings             = [ 
						                  + { 
						                      + enabled     = (known after apply) 
						                      + name        = "ClientPrinterAutoCreation" 
						                      + use_default = false 
						                      + value       = "DefaultPrinterOnly" 
						                    }, 
						                  + { 
						                      + enabled     = (known after apply) 
						                      + name        = "UniversalPrintDriverUsage" 
						                      + use_default = false 
						                      + value       = "FallbackToSpecific" 
						                    }, 
						                ] 
						              + tag_filters                 = (known after apply) 
						              + user_filters                = [ 
						                  + { 
						                      + allowed = true 
						                      + enabled = true 
						                      + sid     = "S-1-5-21-3919795815-764310115-1491410927-2134" 
						                    }, 
						                ] 
						            }, 
						          + { 
						              + access_control_filters      = (known after apply) 
						              + client_ip_filters           = [ 
						                  + { 
						                      + allowed    = true 
						                      + enabled    = true 
						                      + ip_address = "10.10.0.0" 
						                    }, 
						                ] 
						              + client_name_filters         = (known after apply) 
						              + delivery_group_filters      = [ 
						                  + { 
						                      + allowed           = true 
						                      + delivery_group_id = "e2b86f9f-cf26-432d-9a09-0364f9d12787" 
						                      + enabled           = true 
						                    }, 
						                ] 
						              + delivery_group_type_filters = (known after apply) 
						              + description                 = "Example of HDX Graphics-related policy set" 
						              + enabled                     = true 
						              + name                        = "HDX Graphics" 
						              + ou_filters                  = (known after apply) 
						              + policy_settings             = [ 
						                  + { 
						                      + enabled     = true 
						                      + name        = "AllowVisuallyLosslessCompression" 
						                      + use_default = false 
						                      + value       = (known after apply) 
						                    }, 
						                  + { 
						                      + enabled     = (known after apply) 
						                      + name        = "UseVideoCodecForCompression" 
						                      + use_default = false 
						                      + value       = "UseVideoCodecIfPreferred" 
						                    }, 
						                  + { 
						                      + enabled     = true 
						                      + name        = "UseHardwareEncodingForVideoCodec" 
						                      + use_default = false 
						                      + value       = (known after apply) 
						                    }, 
						                ] 
						              + tag_filters                 = (known after apply) 
						              + user_filters                = [ 
						                  + { 
						                      + allowed = true 
						                      + enabled = true 
						                      + sid     = "S-1-5-21-3919795815-764310115-1491410927-2134" 
						                    }, 
						                ] 
						            }, 
						          + { 
						              + access_control_filters      = (known after apply) 
						              + client_ip_filters           = [ 
						                  + { 
						                      + allowed    = true 
						                      + enabled    = true 
						                      + ip_address = "10.10.0.0" 
						                    }, 
						                ] 
						              + client_name_filters         = (known after apply) 
						              + delivery_group_filters      = [ 
						                  + { 
						                      + allowed           = true 
						                      + delivery_group_id = "e2b86f9f-cf26-432d-9a09-0364f9d12787" 
						                      + enabled           = true 
						                    }, 
						                ] 
						              + delivery_group_type_filters = (known after apply) 
						              + description                 = "Example of Client Drive-related policy set" 
						              + enabled                     = true 
						              + name                        = "Client Drives" 
						              + ou_filters                  = (known after apply) 
						              + policy_settings             = [ 
						                  + { 
						                      + enabled     = true 
						                      + name        = "AutoConnectDrives" 
						                      + use_default = false 
						                      + value       = (known after apply) 
						                    }, 
						                  + { 
						                      + enabled     = true 
						                      + name        = "ClientDriveRedirection" 
						                      + use_default = false 
						                      + value       = (known after apply) 
						                    }, 
						                  + { 
						                      + enabled     = true 
						                      + name        = "ClientFixedDrives" 
						                      + use_default = false 
						                      + value       = (known after apply) 
						                    }, 
						                  + { 
						                      + enabled     = false 
						                      + name        = "ClientFloppyDrives" 
						                      + use_default = false 
						                      + value       = (known after apply) 
						                    }, 
						                  + { 
						                      + enabled     = false 
						                      + name        = "ClientOpticalDrives" 
						                      + use_default = false 
						                      + value       = (known after apply) 
						                    }, 
						                  + { 
						                      + enabled     = false 
						                      + name        = "ClientNetworkDrives" 
						                      + use_default = false 
						                      + value       = (known after apply) 
						                    }, 
						                ] 
						              + tag_filters                 = (known after apply) 
						              + user_filters                = [ 
						                  + { 
						                      + allowed = true 
						                      + enabled = true 
						                      + sid     = "S-1-5-21-3919795815-764310115-1491410927-2134" 
						                    }, 
						                ] 
						            }, 
						        ] 
						      + scopes      = [] 
						      + type        = "DeliveryGroupPolicies" 
						    } 
						 
						Plan: 1 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_policy_set.CreatePolicyExampleSet: Creating... 
						citrix_policy_set.CreatePolicyExampleSet: Still creating... [10s elapsed] 
						citrix_policy_set.CreatePolicyExampleSet: Still creating... [20s elapsed] 
						citrix_policy_set.CreatePolicyExampleSet: Still creating... [30s elapsed] 
						citrix_policy_set.CreatePolicyExampleSet: Creation complete after 40s [id=478defe1-1778-aee3-980b-8721ed1a980] 
						 
						PS C:\_TACG\_TFForScopesAndPolicies&gt;
					 
				
			
		
	



	We can now see the successfully created entities in Web Studio:
 


	Web Studio - Policies: 
	
 


	
 


	
 


	This configuration completes the creation and configuration of all wanted resources:
 


	
		A sample Policy Set with different policies and filters
	



	 
 


	Creating Delegated Administration



	Determining the Available Scopes, Roles, Users, and Permissions



	This part of the guide covers the creation of Delegated Administrators with Terraform by using Administrative Scopes, Roles, and Users. 
 


	The delegated administration model offers the flexibility to match how your organization wants to delegate administration activities, using role- and object-based control. It accommodates deployments of all sizes and allows you to configure more permission granularity as your deployment grows in complexity. Delegated administration uses three concepts: Scopes, Roles, and Administrators.
 


	
		Administrators: An administrator represents a person or a group of people identified by their Active Directory account. Each administrator is associated with one or more role and scope pairs.
	
	
		Roles: A role represents a job function and has defined permissions associated with it. 
		For example, the Delivery Group Administrator role has permissions such as ‘Create Delivery Group’ and ‘Remove Desktop from Delivery Group.’ An administrator can have multiple roles for a site, such as a Delivery Group Administrator and a Machine Catalog Administrator. Roles can be built-in or customizable. 
	
	
		Scopes: A scope represents a collection of objects. Scopes are used to group objects in a way that is relevant to your organization (for example, the set of Delivery Groups used by the Sales team). Objects can be in more than one scope; you can think of objects being labeled with one or more scopes. There is one built-in scope: ‘All,’ which contains all objects. The Full Administrator role is always paired with the All scope. 
	



	Citrix Web Studio shows all configured entities:
 


	Administrative Users/Administrators: 
	
 


	 
	Administrative Scopes: 
	
 


	 
	Administrative Roles: 
	
 


	 
	You can use PowerShell to determine the current Administrators, Roles, and Scopes:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					 
					PS C:\_TACG&gt; Add-PSSnapin Citrix.* 
					PS C:\_TACG&gt; Get-AdminAdministrator | FT Name, Rights 
					 
					Name               Rights 
					----               ------ 
					TACG\Administrator {Full Administrator:All} 
					 
					 
					PS C:\_TACG&gt;
				
			
		
	



	 
	Enumerate all configured Scopes:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						 
						PS C:\_TACG&gt; Add-PSSnapin Citrix.* 
						PS C:\_TACG&gt; Get-AdminScope | FT Name, Description 
						 
						Name              Description 
						----              ----------- 
						All               All objects 
						 
						 
						PS C:\_TACG&gt;
					 
				
			
		
	

	
		 
		Enumerate all configured Roles:
	 

	
		
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}		
		
			
				
					
						
							 
							PS C:\_TACG&gt; Add-PSSnapin Citrix.* 
							PS C:\_TACG&gt; Get-AdminRole | FT Name, Permissions 
							 
							Name                          Permissions 
							----                          ----------- 
							Delivery Group Administrator  {AppGroupApplications_ChangeTags, AppGroupApplications_ChangeUserAssignment, AppGroupApplications_Create, AppGroupApplications_CreateFolder...} 
							Full Administrator            {Admin_FullControl, Admin_Read, Admin_RoleControl, Admin_ScopeControl...} 
							Help Desk Administrator       {ApplicationGroup_Read, Applications_Read, DesktopGroup_ChangeMachineMaintenanceMode, DesktopGroup_PowerOperations_VDI...} 
							Host Administrator            {Global_Read, Global_Write, Hosts_AddStorage, Hosts_ChangeMaintenanceMode...} 
							Machine Catalog Administrator {AutoTagRule_Create, AutoTagRule_Delete, AutoTagRule_Edit, AutoTagRule_Read...} 
							Read Only Administrator       {Admin_Read, AppLib_Read, ApplicationGroup_Read, Applications_Read...} 
							 
							 
							PS C:\_TACG&gt;
						 
					
				
			
		

		
			 
			To configure the correct role, we need to determine the available permissions using PowerShell:
		 

		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
		 

		
			
				
					
						
							 
							PS C:\_TACG&gt; Add-PSSnapin Citrix.* 
							PS C:\_TACG&gt; Get-AdminPermission | Sort-Object -Property id | FT Id, Name 
							 
							Id                                        Name 
							--                                        ---- 
							Admin_FullControl                         Manage Administrators 
							Admin_Read                                View Administrators 
							Admin_RoleControl                         Manage Administrator Custom Roles 
							Admin_ScopeControl                        Manage Administrator Scopes 
							AppGroupApplications_ChangeTags           Edit Application tags (Application Group) 
							AppGroupApplications_ChangeUserAssignment Change users assigned to an application (Application Group) 
							AppGroupApplications_Create               Create Application (Application Group) 
							AppGroupApplications_CreateFolder         Create Application Folder (Application Group) 
							AppGroupApplications_Delete               Delete Application (Application Group) 
							AppGroupApplications_EditFolder           Edit Application Folder (Application Group) 
							AppGroupApplications_EditProperties       Edit Application Properties (Application Group) 
							AppGroupApplications_MoveFolder           Move Application Folder (Application Group) 
							AppGroupApplications_Read                 View Applications (Application Group) 
							AppGroupApplications_RemoveFolder         Remove Application Folder (Application Group) 
							AppLib_AddApplication                     Add Packaged Applications 
							AppLib_AddPackage                         Add Application Libraries and Packages 
							AppLib_IsolationGroup_Create              Create App-V Isolation Group 
							AppLib_IsolationGroup_Remove              Remove App-V Isolation Groups 
							AppLib_PackageDiscovery_Create            Create Application Package Discovery Sessions 
							AppLib_PackageDiscoveryProfile_Create     Create Application Package Discovery Profiles 
							AppLib_PackageDiscoveryProfile_Remove     Remove Application Package Discovery Profiles 
							AppLib_Read                               Read Application Libraries and Packages 
							AppLib_RemoveApplication                  Remove Packaged Applications 
							AppLib_RemovePackage                      Remove Application Libraries and Packages 
							ApplicationGroup_AddApplication           Add Application to Application Group 
							ApplicationGroup_AddScope                 Add Application Group to Scope 
							ApplicationGroup_AddToDesktopGroup        Add Delivery Group to Application Group 
							ApplicationGroup_ChangeTags               Change Tags on Application Group 
							ApplicationGroup_ChangeUserAssignment     Edit User Assignment on Application Group 
							ApplicationGroup_Create                   Create Application Group 
							ApplicationGroup_CreateFolder             Create Application Group Folder 
							ApplicationGroup_Delete                   Delete Application Group 
							ApplicationGroup_EditFolder               Edit Application Group Folder 
							ApplicationGroup_EditProperties           Edit Application Group Properties 
							ApplicationGroup_MoveFolder               Move Application Group Folder 
							ApplicationGroup_Read                     View Application Groups 
							ApplicationGroup_RemoveApplication        Remove Application from Application Group 
							ApplicationGroup_RemoveFolder             Remove Application Group Folder 
							ApplicationGroup_RemoveFromDesktopGroup   Remove Delivery Group from Application Group 
							ApplicationGroup_RemoveScope              Remove Application Group from Scop 
							Applications_ChangeTags                   Edit Application tags 
							Applications_ChangeUserAssignment         Change users assigned to an application 
							Applications_Create                       Create Application 
							Applications_CreateFolder                 Create Application Folder 
							Applications_Delete                       Delete Application 
							Applications_EditFolder                   Edit Application Folder 
							Applications_EditProperties               Edit Application Properties 
							Applications_MoveFolder                   Move Application Folder 
							Applications_Read                         View Applications 
							Applications_RemoveFolder                 Remove Application Folder 
							AppV_AddServer                            Add App-V publishing server 
							AppV_DeleteServer                         Delete App-V publishing server 
							AppV_Read                                 Read App-V servers 
							AutoTagRule_Create                        Create AutoTagRule 
							AutoTagRule_Delete                        Delete AutoTagRule 
							AutoTagRule_Edit                          Edit AutoTagRule 
							AutoTagRule_Read                          Read AutoTagRule 
							Catalog_AddMachines                       Add Machines to Machine Catalog 
							Catalog_AddScope                          Add Machine Catalog to Scope 
							Catalog_CancelProvTask                    Cancel Provisioning Task 
							Catalog_ChangeMachineMaintenanceMode      Enable/disable maintenance mode of a machine via Machine Catalog membership 
							Catalog_ChangeMaintenanceMode             Enable/disable maintenance mode on Desktop via Machine Catalog membership 
							Catalog_ChangeTags                        Edit Catalog tags 
							Catalog_ChangeUserAssignment              Change users assigned to a machine 
							Catalog_ConsumeMachines                   Allow machines to be consumed by a Delivery Group 
							Catalog_Create                            Create Machine Catalog 
							Catalog_CreateFolder                      Create Machine Catalog Folder 
							Catalog_Delete                            Delete Machine Catalog 
							Catalog_EditFolder                        Edit Machine Catalog Folder 
							Catalog_EditProperties                    Edit Machine Catalog Properties 
							Catalog_Machine_ChangeTags                Edit Catalog machine tags 
							Catalog_ManageAccounts                    Manage Active Directory Accounts 
							Catalog_MoveFolder                        Move Machine Catalog Folder 
							Catalog_PowerOperations_RDS               Perform power operations on Windows Server machines via Machine Catalog membership 
							Catalog_PowerOperations_VDI               Perform power operations on Windows Desktop machines via Machine Catalog membership 
							Catalog_Read                              View Machine Catalogs 
							Catalog_RemoveFolder                      Remove Machine Catalog Folder 
							Catalog_RemoveMachine                     Remove Machines from Machine Catalog 
							Catalog_RemoveScope                       Remove Machine Catalog from Scope 
							Catalog_SessionManagement                 Perform session management on machines via Machine Catalog membership 
							Catalog_UpdateMasterImage                 Perform Machine update 
							Configuration_Read                        Read Site Configuration 
							Configuration_Write                       Update Site Configuration 
							Controller_EditProperties                 Edit Controller 
							Controllers_Remove                        Remove Delivery Controller 
							DesktopGroup_AddApplication               Add Application to Delivery Group 
							DesktopGroup_AddApplicationGroup          Add Application Group to Delivery Group 
							DesktopGroup_AddMachines                  Add Machines to Delivery Group 
							DesktopGroup_AddScope                     Add Delivery Group to Scope 
							DesktopGroup_AddWebhook                   Add Webhooks to Delivery Group 
							DesktopGroup_ChangeMachineMaintenanceMode Enable/disable maintenance mode of a machine via Delivery Group membership 
							DesktopGroup_ChangeMaintenanceMode        Enable/disable maintenance mode of a Delivery Group 
							DesktopGroup_ChangeTags                   Edit Delivery Group tags 
							DesktopGroup_ChangeUserAssignment         Change users assigned to a desktop 
							DesktopGroup_Create                       Create Delivery Group 
							DesktopGroup_CreateFolder                 Create Delivery Group Folder 
							DesktopGroup_Delete                       Delete Delivery Group 
							DesktopGroup_EditFolder                   Edit Delivery Group Folder 
							DesktopGroup_EditProperties               Edit Delivery Group Properties 
							DesktopGroup_Machine_ChangeTags           Edit Delivery Group machine tags 
							DesktopGroup_MoveFolder                   Move Delivery Group Folder 
							DesktopGroup_PowerOperations_RDS          Perform power operations on Windows Server machines via Delivery Group membership 
							DesktopGroup_PowerOperations_VDI          Perform power operations on Windows Desktop machines via Delivery Group membership 
							DesktopGroup_Read                         View Delivery Groups 
							DesktopGroup_RemoveApplication            Remove Application from Delivery Group 
							DesktopGroup_RemoveApplicationGroup       Remove Application Group from Delivery Group 
							DesktopGroup_RemoveDesktop                Remove Desktop from Delivery Group 
							DesktopGroup_RemoveFolder                 Remove Delivery Group Folder 
							DesktopGroup_RemoveScope                  Remove Delivery Group from Scope 
							DesktopGroup_SessionManagement            Perform session management on machines via Delivery Group membership 
							Director_AlertPolicy_Edit                 Create\Edit\Enable\Delete Alert Policies 
							Director_AlertPolicy_Read                 View Alert Policies 
							Director_Alerts_Read                      View Alerts 
							Director_ApplicationDashboard             View Applications page 
							Director_ClientDetails_Read               View Client Details page 
							Director_ClientHelpDesk_Read              View Client Activity Manager page 
							Director_CloudAnalyticsConfiguration      Create\Edit\Remove Cloud Analytics Configurations 
							Director_Configuration                    View Configurations page 
							Director_Dashboard_Read                   View Dashboard page 
							Director_DesktopHardwareInformation_Edit  Edit Machine Hardware related Broker machine command properties 
							Director_DiskMetrics_Edit                 Edit Disk metrics related Broker machine command properties 
							Director_DismissAlerts                    Dismiss Alerts 
							Director_EmailserverConfiguration_Edit    Create\Edit\Remove Alert Email Server Configuration 
							Director_GPOData_Edit                     Edit GPO Data related Broker machine command properties 
							Director_GpuMetrics_Edit                  Edit Gpu metrics related Broker machine command properties 
							Director_HDXInformation_Edit              Edit HDX related Broker machine command properties 
							Director_HDXProtocol_Edit                 Edit HDX Protocol related Broker machine command properties 
							Director_HelpDesk_Read                    View Activity Manager page 
							Director_KillApplication                  Perform Kill Application running on a machine 
							Director_KillApplication_Edit             Edit Kill Application related Broker machine command properties 
							Director_KillProcess                      Perform Kill Process running on a machine 
							Director_KillProcess_Edit                 Edit Kill Process related Broker machine command properties 
							Director_LatencyInformation_Edit          Edit Latency related Broker machine command properties 
							Director_MachineDetails_Read              View Machine Details page 
							Director_MachineMetricValues_Edit         Edit Machine metric related Broker machine command properties 
							Director_MTOPInformation_Edit             Edit MTOP related Broker machine command properties 
							Director_PersonalizationInformation_Edit  Edit Personalization related Broker machine command properties 
							Director_PoliciesInformation_Edit         Edit Policies related Broker machine command properties 
							Director_ProbeConfigurationActions        Create\Edit\Remove Probe Configurations 
							Director_ProfileLoadData_Edit             Edit Profile Load Data related Broker machine command properties 
							Director_ResetVDisk                       Perform Reset VDisk operation 
							Director_ResetVDisk_Edit                  Edit Reset VDisk related Broker machine command properties 
							Director_RoundTripInformation_Edit        Edit Roundtrip Time related Broker machine command properties 
							Director_SCOM_Read                        View SCOM Notifications 
							Director_ShadowSession                    Perform Remote Assistance on a machine 
							Director_ShadowSession_Edit               Edit Remote Assistance related Broker machine command properties 
							Director_SliceAndDice_Read                View Filters page 
							Director_StartupMetrics_Edit              Edit Startup related Broker machine command properties 
							Director_TaskManagerInformation_Edit      Edit TaskManager related Broker machine command properties 
							Director_Trends_Read                      View Trends page 
							Director_UserDetails_Read                 View User Details page 
							Director_WindowsSessionId_Edit            Edit Windows SessionId related Broker machine command properties 
							EnvTest                                   Run environment tests 
							Global_Read                               Read Service status information 
							Global_Write                              Essential write operations needed by every write permission 
							Hosts_AddScope                            Add Host Connection to Scope 
							Hosts_AddStorage                          Add storage to Resources 
							Hosts_ChangeMaintenanceMode               Enable/Disable maintenance mode of a Host Connection 
							Hosts_Consume                             Use Host Connection or Resources to Create Catalog 
							Hosts_CreateHost                          Add Host Connection or Resources 
							Hosts_DeleteConnection                    Delete Host Connection 
							Hosts_DeleteHost                          Delete Resources 
							Hosts_EditConnectionProperties            Edit Host Connection properties 
							Hosts_EditHostProperties                  Edit Resources 
							Hosts_Read                                View Host Connections and Resources 
							Hosts_RemoveScope                         Remove Host Connection from Scope 
							Image_Create                              Create Images 
							Image_Delete                              Delete Images 
							Image_EditProperties                      Edit Images 
							Image_Read                                Read Images 
							Licensing_ChangeLicenseServer             Change licensing server 
							Licensing_EditLicensingProperties         Edit product edition 
							Licensing_Read                            View Licensing 
							Logging_Delete                            Delete Configuration Logs 
							Logging_EditPreferences                   Edit Logging Preferences 
							Logging_Read                              View Configuration Logs 
							Machine_ChangeTagsBase                    Edit machine tags (hidden) 
							Manage_ServiceConfigurationData           Manage Service Settings 
							Orchestration_RestApi                     Manage Orchestration Service REST API 
							Policies_Manage                           Manage Policies 
							Policies_Read                             View Policies 
							PolicySets_AddScope                       Add Policy Set to Scope 
							PolicySets_RemoveScope                    Remove Policy Set from Scope 
							Setting_Edit                              Edit Settings 
							Setting_Read                              View Settings 
							Storefront_Create                         Create a new StoreFront definition 
							Storefront_Delete                         Delete a StoreFront definition 
							Storefront_Read                           Read StoreFront definitions 
							Storefront_Update                         Update a StoreFront definition 
							Tag_Create                                Create tags 
							Tag_Delete                                Delete tags 
							Tag_Edit                                  Edit tags 
							Tag_Read                                  Read tags 
							Trust_ServiceKeys                         Manage Trust Service Keys 
							Trust_VdaEnrollment 
							UPM_Reset_Profiles                        Reset user profiles 
							UPM_Reset_Profiles_Edit                   Edit Reset User Profiles related Broker machine command properties 
							VdaUpgrade_CatalogManage                  Manage VDA Upgrade Catalog Schedules 
							VdaUpgrade_MachineManage                  Manage VDA Upgrade Machine Schedules 
							Zone_Create                               Create Zone 
							Zone_Delete                               Delete Zone 
							Zone_EditProperties                       Edit Zone 
							Zone_Read                                 View Zones 
							 
							 
							PS C:\_TACG&gt;
						 
					
				
			
		

		
			 
		 

		
			
				Important:
			 

			
				Please take note of the ID(s) of the permission(s) you want to use in the Terraform script. 
				 
			 
		

		
			We have now determined all prerequisites needed to create Delegated Administrators using Terraform. 
			 
		 

		
			Using Terraform for Configuration of the Delegated Administrators
		

		
			We have all the prerequisites and can use Terraform to alter the configuration of our Citrix Virtual Apps and Desktops site. 
			 
		 

		
			
				Important:
			 

			
				Before starting the Terraform workflow, please ensure you have configured the variables according to your needs. 
				 
			 
		

		
			The configuration can be started by following the standard Terraform workflow:
		 

		
			
				terraform init, 
				terraform plan
			 
		

		
			and if no errors occur
		 

		
			
				terraform apply
			 
		

		
			 
		 

		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
			
				
					
						
							
								PS C:\_TACG\_TFForScopesAndPolicies\_TFForScopesAndPolicies&gt; terraform init 
								Initializing the backend... 
								Initializing provider plugins... 
								- Finding citrix/citrix versions matching "1.0.1"... 
								- Using previously-installed citrix/citrix v1.0.1 
								 
								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\_TFForScopesAndPolicies\_TFForScopesAndPolicies&gt; terraform plan 
								data.citrix_admin_scope.GetDefaultAdminScope: Reading... 
								data.citrix_admin_scope.GetDefaultAdminScope: Read complete after 0s [id=00000000-0000-0000-0000-000000000000] 
								 
								 
								─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 
								 
								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_admin_role.CreateAdminRoleExample will be created 
								  + resource "citrix_admin_role" "CreateAdminRoleExample" { 
								      + can_launch_manage  = true 
								      + can_launch_monitor = true 
								      + description        = "Terraform-created Admin Role example" 
								      + id                 = (known after apply) 
								      + is_built_in        = (known after apply) 
								      + name               = "TF-AdminRole-IPS" 
								      + permissions        = [ 
								          + "Global_Read", 
								          + "Policies_Manage", 
								          + "Policies_Read", 
								          + "Setting_Edit", 
								        ] 
								    } 
								 
								  # citrix_admin_scope.CreateAdminScopeExample will be created 
								  + resource "citrix_admin_scope" "CreateAdminScopeExample" { 
								      + description = "Terraform-created Admin Scope example" 
								      + id          = (known after apply) 
								      + name        = "TF-AdminScope-IPS" 
								    } 
								 
								  # citrix_admin_user.CreateAdminUserExample will be created 
								  + resource "citrix_admin_user" "CreateAdminUserExample" { 
								      + domain_name = "TACG" 
								      + id          = (known after apply) 
								      + is_enabled  = true 
								      + name        = "CVADAdmin" 
								      + rights      = [ 
								          + { 
								              + role  = "TF-AdminRole-IPS" 
								              + scope = "TF-AdminScope-IPS" 
								            }, 
								        ] 
								    } 
								 
								Plan: 3 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\_TFForScopesAndPolicies\_TFForScopesAndPolicies&gt; terraform apply 
								 
								data.citrix_admin_scope.GetDefaultAdminScope: Reading... 
								citrix_admin_scope.CreateAdminScopeExample: Refreshing state... [id=79ea9a99-e43f-47fc-b000-2ef609c50e92] 
								data.citrix_admin_scope.GetDefaultAdminScope: Read complete after 0s [id=00000000-0000-0000-0000-000000000000]
							 

							
								  
								──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 
								 
								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_admin_role.CreateAdminRoleExample will be created 
								  + resource "citrix_admin_role" "CreateAdminRoleExample" { 
								      + can_launch_manage  = true 
								      + can_launch_monitor = true 
								      + description        = "Terraform-created Admin Role example" 
								      + id                 = (known after apply) 
								      + is_built_in        = (known after apply) 
								      + name               = "TF-AdminRole-IPS" 
								      + permissions        = [ 
								          + "Global_Read", 
								          + "Policies_Manage", 
								          + "Policies_Read", 
								          + "Setting_Edit", 
								        ] 
								    } 
								 
								  # citrix_admin_scope.CreateAdminScopeExample will be created 
								  + resource "citrix_admin_scope" "CreateAdminScopeExample" { 
								      + description = "Terraform-created Admin Scope example" 
								      + id          = (known after apply) 
								      + name        = "TF-AdminScope-IPS" 
								    } 
								 
								  # citrix_admin_user.CreateAdminUserExample will be created 
								  + resource "citrix_admin_user" "CreateAdminUserExample" { 
								      + domain_name = "TACG" 
								      + id          = (known after apply) 
								      + is_enabled  = true 
								      + name        = "CVADAdmin" 
								      + rights      = [ 
								          + { 
								              + role  = "TF-AdminRole-IPS" 
								              + scope = "TF-AdminScope-IPS" 
								            }, 
								        ] 
								    } 
								 
								Plan: 3 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_admin_scope.CreateAdminScopeExample: Creating... 
								citrix_admin_role.CreateAdminRoleExample: Creating... 
								citrix_admin_scope.CreateAdminScopeExample: Creation complete after 0s [id=09d39ed0-d582-467d-aa1d-35ba0de49016] 
								citrix_admin_role.CreateAdminRoleExample: Creation complete after 0s [id=c69eaeee-c772-48c7-8b0e-559ca35dff2f] 
								citrix_admin_user.CreateAdminUserExample: Creating... 
								citrix_admin_user.CreateAdminUserExample: Creation complete after 0s [id=S-1-5-21-3919795815-764310115-1491410927-2133] 
								 
								Apply complete! Resources: 3 added, 0 changed, 0 destroyed.
							 

							
								PS C:\_TACG\_TFForScopesAndPolicies\_TFForScopesAndPolicies&gt;
							 
						
					
				
			
		

		
			We can now see the successfully created entities in Web Studio:
		 

		
			Web Studio- Administrators: 
			
		 

		
			Web Studio – Scopes: 
			
		 

		
			PowerShell also reflects the newly created entities:
		 

		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
			
				
					
						
							
								PS C:\_TACG&gt; Add-PSSnapin Citrix.* 
								PS C:\_TACG&gt; Get-AdminAdministrator | FT Name, Rights 
								 
								Name               Rights 
								----               ------ 
								TACG\Administrator {Full Administrator:All} 
								TACG\CVADAdmin     {TF-AdminRole-IPS:TF-AdminScope-IPS} 
								 
								 
								PS C:\_TACG&gt;
							 
						
					
				
			
		

		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
			
				
					
						
							
								PS C:\_TACG&gt; Add-PSSnapin Citrix.* 
								PS C:\_TACG&gt; Get-AdminRole | FT Name, Permissions 
								 
								Name                          Permissions 
								----                          ----------- 
								Delivery Group Administrator  {AppGroupApplications_ChangeTags, AppGroupApplications_ChangeUserAssignment, AppGroupApplications_Create, AppGroupApplicatio... 
								Full Administrator            {Admin_FullControl, Admin_Read, Admin_RoleControl, Admin_ScopeControl...} 
								Help Desk Administrator       {ApplicationGroup_Read, Applications_Read, DesktopGroup_ChangeMachineMaintenanceMode, DesktopGroup_PowerOperations_VDI...} 
								Host Administrator            {Global_Read, Global_Write, Hosts_AddStorage, Hosts_ChangeMaintenanceMode...} 
								Machine Catalog Administrator {AutoTagRule_Create, AutoTagRule_Delete, AutoTagRule_Edit, AutoTagRule_Read...} 
								Read Only Administrator       {Admin_Read, AppLib_Read, ApplicationGroup_Read, Applications_Read...} 
								TF-AdminRole-IPS              {Global_Read, Policies_Manage, Policies_Read, Setting_Edit} 
								 
								 
								PS C:\_TACG&gt;
							 
						
					
				
			
		

		
			This configuration completes the creation and configuration of all wanted resources:
		 

		
			
				An Administrative Scope
			
			
				An Administrative Role
			
			
				An Administrative User based on the created Role and Scope 
			
		

		
			 
		 

		
			Using Terraform for Keeping the Infrastructure on a Desired State
		

		
			 
			This part of the guide covers Terraform´s ability to keep the Infrastructure in a desired state. 
			 
		 

		
			
				Terraform configuration files are declarative, meaning they describe your infrastructure's end state. 
				It also keeps track of your actual infrastructure in a state file, which acts as a source of truth for your environment. Terraform uses the state file to determine the changes to make to your infrastructure so that it will match your configuration.
			 

			
				This state is used by Terraform to map real world resources to your configuration, keep track of metadata, and to improve performance for large infrastructures.
			 

			
				This state is stored by default in a local file named "terraform.tfstate", but we recommend storing it in HCP Terraform to version, encrypt, and securely share it with your team.
			 

			
				Terraform uses the state to determine which changes to make to your infrastructure. Prior to any operation, Terraform does a refresh to update the state with the real infrastructure.
			 

			
				The primary purpose of the Terraform state is to store bindings between objects in a remote system and resource instances declared in your configuration. When Terraform creates a remote object in response to a change of configuration, it will record the identity of that remote object against a particular resource instance and then potentially update or delete that object in response to future configuration changes. 
				 
			 
		

		
			(Excerpt from the HashiCorp page https://developer.hashicorp.com/terraform/language/state) 
		 

		
			
				Important:
			 

			
				It is of utmost importance to keep the .tfstate files safely and securely if you want to have the possibility to (re)set your environment to the initial state you planned! 
				 
			 
		

		
			 
			You can find more information about state here: https://developer.hashicorp.com/terraform/language/state 
		 

		
			As an example of state, the Terraform script in this Guide checks and sets the AutoScale settings of a particular Delivery Group. 
			In the script, it is configured to have AutoScale turned off:
		 

		
			
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}			
			
				
					
						
							
								
									#### Create AutoScale settings for the Delivery Group 
								 

								
									resource "citrix_delivery_group" "CreateAutoScaleSettingsInDG" { 
									    name                                    = data.citrix_delivery_group.GetDeliveryGroupNameForFiltering.name 
									    associated_machine_catalogs = [ 
									        { 
									            machine_catalog = data.citrix_machine_catalog.GetMachineCatalog.id 
									            machine_count = 1 
									        } 
									    ] 
									    session_support                         = "SingleSession"  
									    sharing_kind                            = "Shared" 
									    minimum_functional_level                = "L7_34" 
									    desktops                                = [ { 
									                                                    published_name = var.GetDeliveryGroupName 
									                                                    enable_session_roaming = true    
									                                                    enabled = true 
									                                            } ] 
									    autoscale_settings                      = { 
									            autoscale_enabled                               = false 
									            disconnect_peak_idle_session_after_seconds      = 300 
									            log_off_peak_disconnected_session_after_seconds = 300 
									            peak_log_off_action                             = "Nothing"
								 

								
									...
								 

								
									}
								 
							
						
					
				
			
		

		
			Web Studio reflects this setting: 
			
		 

		
			If you alter the AutoScale settings using, e.g., Web Studio and rerun the Terraform script, Terraform will recognize the change and reset it to the setting you provided in the script. 
			Let´s assume someone has switched AutoScale for the Delivery Group, but your IaC wants it to be switched off.
		 

		
			
		 

		
			You only need to rerun the Terraform script, and Terraform will detect the change and alter it to your initial IaC setting: 
			You can see the changes Terraform will make in the terraform plan phase:
		 

		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
			
				
					
						
							
								 PS C:\_TACG\_TFForScopesAndPolicies\_TFForScopesAndPolicies&gt; terraform plan 
								... 
								citrix_delivery_group.CreateAutoScaleSettingsInDG: Refreshing state... [id=e2b86f9f-cf26-432d-9a09-0364f9d12787] 
								 
								Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: 
								  + create 
								  ~ update in-place 
								 
								Terraform will perform the following actions: 
								... 
								 
								  # citrix_delivery_group.CreateAutoScaleSettingsInDG will be updated in-place 
								  ~ resource "citrix_delivery_group" "CreateAutoScaleSettingsInDG" { 
								      ~ autoscale_settings          = { 
								          ~ autoscale_enabled                                     = true -&gt; false  
								            # (22 unchanged attributes hidden) 
								        } 
								...
							 

							
								   
								Plan: 1 to change, 0 to destroy.
							 

							
								 
								 PS C:\_TACG\_TFForScopesAndPolicies\_TFForScopesAndPolicies&gt; 
							 
						
					
				
			
		

		
			Terraform explicitly shows that it would change the autoscale_enabled setting of the Delivery Group to false.
		 

		
			
				Important:
			 

			
				Running the Terraform scripts will alter or even recreate (if necessary) all entities to the configuration you wrote. 
				Please be aware of the consequences that could arise due to the alteration/recreation. 
				 
			 
		

		
			Using Terraform, you can ensure that your Infrastructure is kept in the state you desire.
		 

		
			That concludes the guide "Using Terraform to Deploy Citrix Policies and Delegated Administration on a Citrix Virtual Apps and Desktops 2402 LTSR CU1 Site”.
		 

		
			 
		 

		
			Appendix
		

		
			
				Disclaimer
			 

			
				EXCEPT WHERE EXPRESSLY PROVIDED OTHERWISE BY CLOUD SOFTWARE GROUP, THE CONTENT IN THIS DOCUMENT IS PROVIDED “AS IS” AND CLOUD SOFTWARE GROUP HEREBY DISCLAIMS ALL EXPRESS OR IMPLIED REPRESENTATIONS, WARRANTIES, GUARANTIES, AND CONDITIONS, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES OR CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD TO BE LEGALLY INVALID. CLOUD SOFTWARE GROUP MAKES NO REPRESENTATIONS, WARRANTIES, GUARANTIES, OR CONDITIONS AS TO THE QUALITY, SUITABILITY, TRUTH, ACCURACY, OR COMPLETENESS OF ANY OF THE CONTENT CONTAINED ON THE WEBSITE.
			 

			
				The above disclaimer applies to any damages, liability, or injuries caused by any failure of performance, error, omission, interruption, deletion, defect, delay in operation or transmission, computer virus, communication line failure, theft or destruction of or unauthorized access to, alteration of, or use, whether for breach of contract, tort, negligence or any other cause of action. 
				 
			 
		

		
			_TFForScopesAndPolicies-CreateEntities.tf
		 
	

	
		
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}		
		
			
				
					
						
							
								
									# Terraform deployment of Policies and Delegated Administration for Citrix Virtual Apps and Desktops 2402 CU1
								

								
									# Keeping track of an Infrastructure using state
								

								
									## Creating all Citrix Cloud-related entities
								

								
									### Import an existing Infrastructure component - a Delivery Group
								

								
									import {
								

								
									  to = citrix_delivery_group.CreateAutoScaleSettingsInDG
								

								
									  id = "e1b2abe0-fb9e-47e1-9ce5-fe78e26c8785"
								

								
									} 
								
								 

								
									### Retrieve default Admin Scope id for later use
								

								
									data "citrix_admin_scope" "GetDefaultAdminScope" {
								

								
									    name = "All"
								

								
									}
								
								 

								
									### retrieve Machine Catalog Group ID for later use
								

								
									data "citrix_machine_catalog" "GetMachineCatalog" {
								

								
									    name = var.GetMachineCatalogName
								

								
									}
								
								 

								
									### retrieve Delivery Group ID for later use
								

								
									data "citrix_delivery_group" "GetDeliveryGroupNameForFiltering" {
								

								
									    name = var.GetDeliveryGroupName
								

								
									}
								
								 

								
									/* output "MC" {
								

								
									  value = data.citrix_machine_catalog.GetMachineCatalog.id
								

								
									}
								
								 

								
									output "DG" {
								

								
									  value = data.citrix_delivery_group.GetDeliveryGroupNameForFiltering.id
								

								
									} */
								
								 

								
									### Creating Admin examples
								

								
									#### Create an Admin Scope example
								

								
									resource "citrix_admin_scope" "CreateAdminScopeExample" {
								

								
									    name        = var.CreateAdminScopeExample-Name
								

								
									    description = var.CreateAdminScopeExample-Description
								

								
									}
								
								 

								
									#### Create an Admin Role example
								

								
									resource "citrix_admin_role" "CreateAdminRoleExample" {
								

								
									    name        = var.CreateAdminRoleExample-Name
								

								
									    description = var.CreateAdminRoleExample-Description
								

								
									    permissions = var.CreateAdminRoleExample-Permissions
								

								
									}
								
								 

								
									#### Create a new Admin based on the created Scope and Role
								

								
									resource "citrix_admin_user" "CreateAdminUserExample" {
								

								
									    depends_on = [ citrix_admin_role.CreateAdminRoleExample, citrix_admin_scope.CreateAdminScopeExample ]
								

								
									    name        = var.CreateAdminUserExample-Name
								

								
									    domain_name = var.CreateAdminUserExample-DomainName
								

								
									    rights      = [
								

								
									        {
								

								
									            role  = citrix_admin_role.CreateAdminRoleExample.name
								

								
									            scope = citrix_admin_scope.CreateAdminScopeExample.name
								

								
									        }
								

								
									    ]
								

								
									    is_enabled  = var.CreateAdminUserExample-IsEnabled
								

								
									} 
								
								 

								
									### Creating Policy examples
								

								
									#### Create a Policy example set
								

								
									resource "citrix_policy_set" "CreatePolicyExampleSet" {
								

								
									    name           = var.CreatePolicyExampleSet-Name
								

								
									    description    = var.CreatePolicyExampleSet-Description
								

								
									    #type           = var.CreatePolicyExampleSet-Type
								

								
									    #scopes         = [ data.citrix_admin_scope.GetDefaultAdminScope.id ]
								

								
									    policies = [
								

								
									        {
								

								
									            name = "Printing"
								

								
									            description = "Example of Printer-related policy set"
								

								
									            enabled = true
								

								
									            policy_settings = [
								

								
									                {
								

								
									                    name = "ClientPrinterAutoCreation"
								

								
									                    value = "DefaultPrinterOnly"
								

								
									                    use_default = false
								

								
									                },
								

								
									                {
								

								
									                    name = "UniversalPrintDriverUsage"
								

								
									                    value = "FallbackToSpecific"
								

								
									                    use_default = false
								

								
									                },
								

								
									            ]
								

								
									            delivery_group_filters = [
								

								
									                {
								

								
									                    delivery_group_id   = data.citrix_delivery_group.GetDeliveryGroupNameForFiltering.id
								

								
									                    enabled = true
								

								
									                    allowed = true
								

								
									                },
								

								
									            ]
								

								
									            user_filters = [
								

								
									                {
								

								
									                    sid     = "S-1-5-21-3919795815-764310115-1491410927-2134"
								

								
									                    enabled = true
								

								
									                    allowed = true
								

								
									                },
								

								
									            ]
								

								
									        },
								

								
									        {
								

								
									            name = "HDX Graphics"
								

								
									            description = "Example of HDX Graphics-related policy set"
								

								
									            enabled = true
								

								
									            policy_settings = [
								

								
									                {
								

								
									                    name = "AllowVisuallyLosslessCompression"
								

								
									                    enabled = true
								

								
									                    use_default = false
								

								
									                },
								

								
									                {
								

								
									                    name = "UseVideoCodecForCompression"
								

								
									                    value = "UseVideoCodecIfPreferred"
								

								
									                    use_default = false
								

								
									                },
								

								
									                {
								

								
									                    name = "UseHardwareEncodingForVideoCodec"
								

								
									                    enabled = true
								

								
									                    use_default = false
								

								
									                },
								

								
									            ]
								

								
									            delivery_group_filters = [
								

								
									                {
								

								
									                    delivery_group_id   = data.citrix_delivery_group.GetDeliveryGroupNameForFiltering.id
								

								
									                    enabled = true
								

								
									                    allowed = true
								

								
									                },
								

								
									            ]
								

								
									            user_filters = [
								

								
									                {
								

								
									                    sid     = "S-1-5-21-3919795815-764310115-1491410927-2134"
								

								
									                    enabled = true
								

								
									                    allowed = true
								

								
									                },
								

								
									            ]
								

								
									            client_ip_filters = [
								

								
									                {
								

								
									                    ip_address = "10.10.0.0"
								

								
									                    enabled    = true
								

								
									                    allowed    = true
								

								
									                },
								

								
									            ]
								

								
									        },
								

								
									       {
								

								
									            name = "Client Drives"
								

								
									            description = "Example of Client Drive-related policy set"
								

								
									            enabled = true
								

								
									            policy_settings = [
								

								
									                {
								

								
									                    name = "AutoConnectDrives"
								

								
									                    enabled = true
								

								
									                    use_default = false
								

								
									                },
								

								
									                {
								

								
									                    name = "ClientDriveRedirection"
								

								
									                    enabled = true
								

								
									                    use_default = false
								

								
									                },
								

								
									                {
								

								
									                    name = "ClientFixedDrives"
								

								
									                    enabled = true
								

								
									                    use_default = false
								

								
									                },
								

								
									                {
								

								
									                    name = "ClientFloppyDrives"
								

								
									                    enabled = false
								

								
									                    use_default = false
								

								
									                },
								

								
									                {
								

								
									                    name = "ClientOpticalDrives"
								

								
									                    enabled = false
								

								
									                    use_default = false
								

								
									                },
								

								
									                {
								

								
									                    name = "ClientNetworkDrives"
								

								
									                    enabled = false
								

								
									                    use_default = false
								

								
									                },
								

								
									            ]
								

								
									            delivery_group_filters = [
								

								
									                {
								

								
									                    delivery_group_id   = data.citrix_delivery_group.GetDeliveryGroupNameForFiltering.id
								

								
									                    enabled = true
								

								
									                    allowed = true
								

								
									                },
								

								
									            ]
								

								
									            user_filters = [
								

								
									                {
								

								
									                    sid     = "S-1-5-21-3919795815-764310115-1491410927-2134"
								

								
									                    enabled = true
								

								
									                    allowed = true
								

								
									                },
								

								
									            ]
								

								
									            client_ip_filters = [
								

								
									                {
								

								
									                    ip_address = "10.10.0.0"
								

								
									                    enabled    = true
								

								
									                    allowed    = true
								

								
									                },
								

								
									            ]
								

								
									        } 
								

								
									    ]
								

								
									}
								
								 

								
									#### Create autoScale settings for the Delivery Group 
								

								
									resource "citrix_delivery_group" "CreateAutoScaleSettingsInDG" {
								

								
									    name                                    = data.citrix_delivery_group.GetDeliveryGroupNameForFiltering.name
								

								
									    associated_machine_catalogs = [
								

								
									        {
								

								
									            machine_catalog = data.citrix_machine_catalog.GetMachineCatalog.id
								

								
									            machine_count = 1
								

								
									        }
								

								
									    ]
								

								
									    session_support                         = "SingleSession" 
								

								
									    sharing_kind                            = "Shared"
								

								
									    minimum_functional_level                = "L7_34"
								

								
									    desktops                                = [ {
								

								
									                                                    published_name = var.GetDeliveryGroupName
								

								
									                                                    enable_session_roaming = true   
								

								
									                                                    enabled = true
								

								
									                                            } ]
								

								
									    autoscale_settings                      = {
								

								
									            autoscale_enabled                               = false
								

								
									            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.CVAD-DG-AS-Name
								

								
									                display_name                = var.CVAD-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
								

								
									            },
								

								
									        ]
								

								
									    }
								

								
									}]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_09/ws-no-policies.png.5403db41ca2b739586f4ca4652ce6148.png" length="44450" type="image/png"/><pubDate>Thu, 05 Sep 2024 14:34:00 +0000</pubDate></item><item><title>NetScaler Advanced Authentication for Citrix Endpoint Management: Certificate Only</title><link>https://community.stage.citrix.com/tech-zone/build/deployment-guides/netscaler-advanced-auth-for-cem-cert/</link><description><![CDATA[Overview 



	This deployment guide assists Citrix Endpoint Management (CEM) administrators in transitioning from NetScaler classic to advanced authentication policies, allowing users to log on via CEM for Mobile Application Management (MAM) traffic. 
 


	This is especially useful for customers using Citrix Secure Mail, Citrix Secure Web, or line-of-business MAM SDK apps that leverage microVPN connections via NetScaler Gateway. We are covering the use case of Certificate-based authentication. 
 


	For this guide we are covering the use case of Certificate based authentication, and we have used Citrix Endpoint Management service (24.8.0) and NetScaler 14.1 build 21.57nc.
 


	Applicable Deployments



	This deployment guide applies to customers using Citrix Endpoint Management service (cloud) or Citrix XenMobile Server (on-premises), regardless of version. 
 


	We highly recommend running a recent version of Citrix Endpoint Management (cloud or on-premises) and NetScaler Gateway to ensure stability, performance, and fixes are included.
 


	Prerequisites



	Before moving to Netscaler's advanced authentication policies configuration, make sure to follow the prerequisites from this Citrix Documentation: https://docs.citrix.com/en-us/citrix-endpoint-management/authentication/nfactor-authentication#prerequisites
 


	 
 


	
		Note: 
	 

	
		Follow all prerequisites to avoid connectivity issues from the mobile device to the NetScaler Gateway virtual server assigned for CEM. 
	 



	 
 


	To configure Certificate Based Authentication on Citrix Endpoint Management, please follow the steps outlined in this link: https://docs.citrix.com/en-us/citrix-endpoint-management/authentication/client-certificate
 


	 
 


	
		Important:
	 

	
		Once you save the NetScaler Gateway settings with Certificate as the logon type in the CEM console, export the NetScaler script. 
	 



	 
 


	Ensure to run the NetScaler script for Advanced policies (filename: ConfigureCitrixGatewayScript_Advanced.txt) 
 


	The current script only applies to NetScaler advanced session policies and not to advanced authentication policies.
 


	An enhancement request is needed to update the CEM NS script to include advanced authentication policies. In the meantime, follow the below steps to create the new advanced authentication policies and unbind the classic policies generated by the NS script.
 


	Alternatively, if you would like to convert classic to advanced authentication policies using Netscaler's NSPEPI too, refer to: https://docs.netscaler.com/en-us/citrix-adc/current-release/appexpert/policies-and-expressions/introduction-to-policies-and-exp/converting-policy-expressions-nspepi-tool.html
 


	Configure NetScaler Gateway advanced authentication policies.



	From the NetScaler Gateway: Create CERT profile



	We will configure CERT policies with the corresponding profile in the following steps. 
 


	As a use case, we will use UPN (UserPrincipalName) to enroll end users' mobile devices with CEM (e.g. username@company.com).
 


	
		In the NetScaler administration console, go to: 
		 
	
	
		Security &gt; AAA – Application Traffic &gt; Policies &gt; Authentication &gt; Advanced Policies &gt; click Add. 
	
	
		Select Action Type: CERT.
	
	
		Select the Profiles tab and then select Add.
	



	Complete the following fields to create the Authentication CERT Profile. 
 


	
		Name - Name for the client cert authentication server profile (action).
	
	
		Two factor – In this instance, the two-factor authentication option is ON.
	
	
		User Name Field – select SubjectAltName: PrincipalName
	
	
		Click Create.
	



	Example:
 


	
 


	 
 


	
		Note: 
	 

	
		If end users will be enrolling to CEM using SAMAccountName (e.g., username), then the CERT profile will look like this:
	 



	
 


	Finish the wizard to create the authentication policy for CERT. 
 


	Set expression as true. 
 


	Click Create.
 


	
 


	In the next step, unbind the classic authentication policies from the NetScaler Gateway virtual server.
 


	
 


	Next, we must bind the newly created advanced authentication policies under the same NetScaler Gateway virtual server for CEM.
 


	This time, the new advanced authentication policy for CERT will be bound to a new AAA virtual server that NetScaler Gateway will use as authentication/authorization.
 


	Next, create a new AAA virtual server. You can do it inside the NetScaler Gateway virtual server &gt; Create Authentication Profile &gt; Authentication Virtual Servers section.  
 


	For IP Address, you can select as non-addressable, since this is an internal AAA virtual server that will be used by the NetScaler Gateway.
 


	
 


	It is a good practice to bind a server certificate so the AAA virtual server status shows UP. Nevertheless, this is not necessary since this is not a virtual server exposed to the outside world. 
 


	
 


	 
 


	
 


	Next, all three new advanced authentication policies will be bound to this AAA virtual server. It should look like this:
 


	Policies bound to the AAA virtual server 
 


	
 


	Next, bind the newly created AAA virtual server under the NetScaler Gateway authentication profile. 
 


	Click Create.
 


	It will look like this:
 


	
 


	Finally, double-check on the NetScaler Gateway virtual server for CEM. You have Client Authentication ENABLED and Client Certificate as MANDATORY.
 


	
 


	From Citrix Endpoint Management console



	
		Ensure Certificate logon type are still set in the Citrix Gateway settings. 
	



	
 


	
		Set the Client Property ENABLE_MAM_NFACTOR_SSO to true. For more information on how to create the Client Property value, go here. 
	



	
 


	End user testing



	
		For new devices, enroll using UPN format or SAMAccountName as desired.
	
	
		For existing devices, sign off/on from Secure Hub. Validate users can still connect/authenticate successfully to the NetScaler Gateway.]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_08/image.png.698af953994282e762dc53fd7577dd38.png" length="125960" type="image/png"/><pubDate>Fri, 30 Aug 2024 16:19:56 +0000</pubDate></item><item><title>NetScaler Advanced Authentication for Citrix Endpoint Management: Domain Only</title><link>https://community.stage.citrix.com/tech-zone/build/deployment-guides/netscaler-advanced-auth-for-cem-domain/</link><description><![CDATA[Overview 



	This deployment guide assists Citrix Endpoint Management (CEM) administrators in transitioning from NetScaler classic to advanced authentication policies, allowing users to log on via CEM for Mobile Application Management (MAM) traffic. 
 


	This is especially useful for customers using Citrix Secure Mail, Citrix Secure Web, or line-of-business MAM SDK apps that leverage microVPN connections via NetScaler Gateway. We are covering the use case of Domain authentication. 
 


	For this guide, we cover the use case of domain authentication using Citrix Endpoint Management service (24.8.0) and NetScaler 14.1 build 21.57nc.
 


	Applicable Deployments



	This deployment guide applies to customers using Citrix Endpoint Management service (cloud) or Citrix XenMobile Server (on-premises), regardless of version. 
 


	We highly recommend running a recent version of Citrix Endpoint Management (cloud or on-premises) and NetScaler Gateway to ensure stability, performance, and fixes are included.
 


	Prerequisites



	Before moving to Netscaler's advanced authentication policies configuration, make sure to follow the prerequisites from this Citrix Documentation: https://docs.citrix.com/en-us/citrix-endpoint-management/authentication/nfactor-authentication#prerequisites
 


	
		Note: 
	 

	
		Follow all prerequisites to avoid connectivity issues from the mobile device to the NetScaler Gateway virtual server assigned for CEM. 
	 



	 
 


	To configure Domain (LDAP) on Citrix Endpoint Management, please follow the steps outlined in this link: https://docs.citrix.com/en-us/citrix-endpoint-management/authentication/authentication-domain-security-token 
 


	 
 


	
		Important 
	 

	
		Once you save the NetScaler Gateway settings with Domain as the logon type in the CEM console, export the NetScaler script. 
	 



	 
 


	Ensure to run the NetScaler script for Advanced policies (filename: ConfigureCitrixGatewayScript_Advanced.txt) 
 


	The current script only applies to NetScaler advanced session policies and not to advanced authentication policies. 
 


	An enhancement request is needed to update the CEM NS script to include advanced authentication policies. In the meantime, follow the below steps to create the new advanced authentication policies and unbind the classic policies generated by the NS script.
 


	Alternatively, if you would like to convert classic to advanced authentication policies using Netscaler's NSPEPI too, refer to: https://docs.netscaler.com/en-us/citrix-adc/current-release/appexpert/policies-and-expressions/introduction-to-policies-and-exp/converting-policy-expressions-nspepi-tool.html.
 


	Configure NetScaler Gateway advanced authentication policies.



	From the NetScaler Gateway: Create LDAP profile



	We will configure Domain policies with the corresponding profile in the following steps. 
 


	As a use case, we will use UPN (UserPrincipalName) to enroll end users' mobile devices with CEM (e.g username@company.com).
 


	Navigate to Security &gt; AAA – Application Traffic &gt; Policies &gt; Authentication &gt; Advanced Policies &gt; click Add. 
 


	Select Action Type: LDAP
 


	For Action, select the previously created LDAP server profile generated by the NS script (e.g., Gateway_LDAP_ACTION_UPN). In this example, I’m selecting the UPN one. 
 


	Expression is true.
 


	Click Create. 
 


	Example:
 


	
 


	Repeat the same steps above for LDAP, but this time, create an LDAP policy for SAMAccountName. This will be needed if end users enroll the mobile device using just their username for CEM (e.g username). 
 


	It will look like this:
 


	 
 


	
 


	As noted above, you can reuse the same LDAP profile for SAM (e.g., Gateway_LDAP_ACTION_SAM) created by the NS script. 
 


	In the next step, unbind the classic authentication policies from the NetScaler Gateway virtual server.
 


	
 


	Next, we must bind the newly created advanced authentication policies under the same NetScaler Gateway virtual server for CEM.
 


	This time, the new two advanced authentication policies will be bound to a new AAA virtual server that NetScaler Gateway will use as authentication/authorization.
 


	Next, create a new AAA virtual server. You can do it inside the NetScaler Gateway virtual server &gt; Create Authentication Profile &gt; Authentication Virtual Servers section.  
 


	For IP Address, you can select as non-addressable, since this is an internal AAA virtual server that will be used by the NetScaler Gateway.
 


	
 


	It is a good practice to bind a server certificate so the AAA virtual server status shows UP. Nevertheless, this is not necessary since this is not a virtual server exposed to the outside world. 
 


	
 


	 
 


	
 


	 
	 
 


	Next, all two new advanced authentication policies will be bound to this AAA virtual server. It should look like this:
 


	Policies bound to the AAA virtual server 
 


	
 


	Next, bind the newly created AAA virtual server under the NetScaler Gateway authentication profile. 
 


	Click Create.
 


	It will look like this. 
 


	
 


	From Citrix Endpoint Management console



	
		Ensure Domain logon type are still set in the Citrix Gateway settings. 
	



	
 


	
		Set the Client Property ENABLE_MAM_NFACTOR_SSO to true. For more information on how to create the Client Property value, go here. 
	



	
 


	End user testing



	
		For new devices, enroll using UPN format or SAMAccountName as desired.
	
	
		For existing devices, make sure to sign off/on from Secure Hub. Validate users can still connect/authenticate successfully to the NetScaler Gateway.]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_08/image.png.3fe787a5489e89056de447519ac275f4.png" length="83184" type="image/png"/><pubDate>Fri, 30 Aug 2024 16:13:22 +0000</pubDate></item><item><title>NetScaler Advanced Authentication for Citrix Endpoint Management: Certificate and Domain</title><link>https://community.stage.citrix.com/tech-zone/build/deployment-guides/netscaler-advanced-auth-for-cem-cert-and-domain/</link><description><![CDATA[Overview 



	This deployment guide assists Citrix Endpoint Management (CEM) administrators in transitioning from NetScaler classic to advanced authentication policies, allowing users to log on via CEM for Mobile Application Management (MAM) traffic. 
 


	This is especially useful for customers using Citrix Secure Mail, Citrix Secure Web, or line-of-business MAM SDK apps that leverage microVPN connections via NetScaler Gateway. We are covering the use case of Domain—and certificate-based authentication 
 


	For this guide we are covering the use case of Domain and Certificate based authentication, and we have used Citrix Endpoint Management service (24.8.0) and NetScaler 14.1 build 21.57nc.
 


	Applicable Deployments



	This deployment guide applies to customers using Citrix Endpoint Management service (cloud) or Citrix XenMobile Server (on-premises), regardless of version. 
 


	We highly recommend running a recent version of Citrix Endpoint Management (cloud or on-premises) and NetScaler Gateway to ensure stability, performance, and fixes are included.
 


	Prerequisites



	Before moving to NetScaler's advanced authentication policies configuration, make sure to follow the prerequisites from this Citrix Documentation: https://docs.citrix.com/en-us/citrix-endpoint-management/authentication/nfactor-authentication#prerequisites
 


	
		Note: 
	 

	
		Follow all prerequisites noted to avoid connectivity issues from the mobile device to the NetScaler Gateway virtual server assigned for CEM. 
	 



	To configure Certificate Based Authentication and Domain (LDAP) on Citrix Endpoint Management, please follow the steps outlined in this link: https://docs.citrix.com/en-us/citrix-endpoint-management/authentication/client-certificate
 


	 
 


	
		Important: 
	 

	
		In the CEM console, once you save the NetScaler Gateway settings with Certificate and Domain as the logon type, export the NetScaler script. 
	 



	 
 


	Ensure to run the NetScaler script for Advanced policies (filename: ConfigureCitrixGatewayScript_Advanced.txt) 
 


	The current script only applies to NetScaler advanced session policies and not to advanced authentication policies. 
 


	An enhancement request is needed to update the CEM NS script to include advanced authentication policies. In the meantime, follow the below steps to create the new advanced authentication policies and unbind the classic policies generated by the NS script.
 


	Alternatively, if you would like to convert classic to advanced authentication policies using NetScaler’s NSPEPI too, refer to: https://docs.netscaler.com/en-us/citrix-adc/current-release/appexpert/policies-and-expressions/introduction-to-policies-and-exp/converting-policy-expressions-nspepi-tool.html 
	 
 


	Configure NetScaler Gateway advanced authentication policies.



	From the NetScaler Gateway: Create CERT profile



	We will configure CERT policies with the corresponding profile in the following steps. 
 


	As a use case, we will use UPN (UserPrincipalName) to enroll end users' mobile devices with CEM (e.g, username@company.com).
 


	In the NetScaler administration console, go to:
 


	
		Security &gt; AAA – Application Traffic &gt; Policies &gt; Authentication &gt; Advanced Policies &gt; click Add. 
	
	
		Select Action Type: CERT.
	
	
		Select the Profiles tab and then select Add.
	



	Complete the following fields to create the Authentication CERT Profile. 
 


	
		Name - Name for the client cert authentication server profile (action).
	
	
		Two factor – In this instance, the two-factor authentication option is ON.
	
	
		User Name Field – select SubjectAltName: PrincipalName
	
	
		Click Create.
	



	Example:
 


	
 


	 
 


	
		Note: 
	 

	
		If end users will be enrolling to CEM using SAMAccountName (e.g., username), then the CERT profile will look like this:
	 

	
		
	 



	Finish the wizard to create the authentication policy for CERT. 
 


	Set expression as true. 
 


	Click Create.
 


	
 


	From the NetScaler Gateway: Create LDAP profile



	Navigate to Security &gt; AAA – Application Traffic &gt; Policies &gt; Authentication &gt; Advanced Policies &gt; click Add. 
 


	Select Action Type: LDAP
 


	For Action, select the previously created LDAP server profile generated by the NS script (e.g., Gateway_LDAP_ACTION_UPN). In this example, I’m selecting the UPN one. 
 


	Expression is true.
 


	Click Create. 
 


	Example:
 


	
 


	Repeat the same steps above for LDAP, but this time, create an LDAP policy for SAMAccountName. This will be needed when Certificate + Domain (LDAP) authentication is used for CEM. 
 


	It will look like this:
 


	
 


	As noted above, you can reuse the same LDAP profile for SAM (e.g., Gateway_LDAP_ACTION_SAM) created by the NS script. 
 


	In the next step, unbind the classic authentication policies from the NetScaler Gateway virtual server.
 


	
 


	Next, we must bind the newly created advanced authentication policies under the same NetScaler Gateway virtual server for CEM.
 


	This time, the new three advanced authentication policies will be bound to a new AAA virtual server that NetScaler Gateway will use as authentication/authorization.
 


	Next, create a new AAA virtual server. You can do it inside the NetScaler Gateway virtual server &gt; Create Authentication Profile &gt; Authentication Virtual Servers section.  
 


	For IP Address, you can select as non-addressable, since this is an internal AAA virtual server that will be used by the NetScaler Gateway.
 


	
 


	For the server certificate, it is a good practice to bind a server certificate so the AAA virtual server status shows UP. Nevertheless this is not necessary, since this is not a virtual server exposed to the outside world. 
 


	
 


	 
 


	
 


	Next, all three new advanced authentication policies will be bound to this AAA virtual server. It should look like this:
 


	Policies bound to the AAA virtual server 
 


	
 


	Next, bind the newly created AAA virtual server under the NetScaler Gateway authentication profile. 
 


	Click create.
 


	It will look like this. 
 


	
 


	Finally, double-check on the NetScaler Gateway virtual server for CEM. You have Client Authentication ENABLED and Client Certificate as MANDATORY.
 


	
 


	From Citrix Endpoint Management console



	
		Ensure Certificate and Domain logon type are still set in the Citrix Gateway settings. 
	



	
 


	
		Set the Client Property ENABLE_MAM_NFACTOR_SSO to true. For more information on how to create the Client Property value, go here. 
	



	
 


	End user testing



	
		For new devices, enroll using UPN format or SAMAccountName as desired.
	
	
		For existing devices, make sure to sign off/on from Secure Hub. Validate users can still connect/authenticate successfully to the NetScaler Gateway.]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_08/image.png.cda79a6f8e6918f9d54e479f9a6d8e75.png" length="125960" type="image/png"/><pubDate>Fri, 30 Aug 2024 13:35:00 +0000</pubDate></item><item><title>Mergers and Acquisitions</title><link>https://community.stage.citrix.com/tech-zone/design/reference-architectures/mergers-acquisitions/</link><description><![CDATA[Overview



	CompanyA is a food manufacturer located in the northern plains of the United States. CompanyA plans to acquire additional food manufacturers across different climates to continue growing and expanding into new types of food. As part of the acquisition process, CompanyA needs a repeatable strategy to integrate acquired company systems into a single, unified experience.
 


	The integration across multiple independent organizations introduces many technical challenges. The challenges mostly focus on granting external users (CompanyA users) access to private resources (CompanyB applications) that separate, independent identity providers authorize.
 


	Company A used Citrix Workspace as the cornerstone of its mergers and acquisition strategy. It is looking to use the same strategy for acquiring Companies C, D, and E.
 


	Success Criteria



	As part of the acquisition strategy, CompanyA needs a solution that can quickly and securely allow users access to CompanyA and CompanyB resources. To be successful, CompanyA defined a list of success criteria that forms the basis for the overarching design.
 


	User Experience



	The first aspect of a mergers and acquisitions solution is meeting the user’s needs. CompanyA identified the following user-related criteria for a successful design.
 


	
		
			
				
					Success Criteria
				 
			
			
				
					Description
				 
			
			
				
					Solution
				 
			
		
		
			
				
					Application Library
				 
			
			
				
					Company A and Company B users need a centralized way to access resources from the other company.
				 
			
			
				
					Citrix Workspace
				 
			
		
		
			
				
					Web App Single Sign-On
				 
			
			
				
					When accessing private web resources from another company, users are not required to remember and enter another user's accounts or passwords.
				 
			
			
				
					Citrix Secure Private Access service
				 
			
		
		
			
				
					Virtual App Single Sign-On
				 
			
			
				
					When accessing virtual Windows apps from another company, users are not required to remember and enter another user's accounts or passwords.
				 
			
			
				
					Citrix DaaS – Federated Authentications Service
				 
			
		
		
			
				
					Unified Experience
				 
			
			
				
					Regardless of the user’s original company, all users have the same authentication experience.
				 
			
			
				
					NetScaler – nFactor authentication policies
				 
			
		
	



	Security



	The second aspect of a mergers and acquisitions solution is meeting security needs. CompanyA identified the following security-related criteria for a successful design.
 


	
		
			
				
					Success Criteria
				 
			
			
				
					Description
				 
			
			
				
					Solution
				 
			
		
		
			
				
					Identity Providers
				 
			
			
				
					Each acquired organization maintains a separate identity provider until it can be integrated with CompanyA’s primary identity provider.
				 
			
			
				
					NetScaler
				 
			
		
		
			
				
					Multifactor authentication
				 
			
			
				
					Security is a top concern, so MFA is required to ensure another layer of authentication protection of corporate resources.
				 
			
			
				
					Integrate currently deployed solution or require Time-Based One-Time Password with Push
				 
			
		
		
			
				
					VPN-less Access
				 
			
			
				
					Corporate resources must be protected from untrusted and unsecured locations. Devices are not allowed direct access to the internal network to help prevent malware intrusion.
				 
			
			
				
					Citrix Secure Private Access service and Citrix DaaS
				 
			
		
		
			
				
					Internal Threats
				 
			
			
				
					There are documented cases where internal users who are unhappy with the acquisition steal customer data and intellectual property. Data capture and storage must be restricted.
				 
			
			
				
					Enhanced Security Policies, App Protection Policies, and Security Analytics
				 
			
		
		
			
				
					External Threats
				 
			
			
				
					NetScaler presents Workspace with an authentication web app to handle multi-directory authentication. CompanyA must add extra layers of protection for public-facing web apps.
				 
			
			
				
					NetScaler with Bot Management and Web App Firewall
				 
			
		
	



	Conceptual Architecture



	CompanyA created the following high-level conceptual architecture based on the requirements defined by its acquisition strategy. The conceptual architecture meets all of the requirements and gives CompanyA the foundation to expand to additional use cases as identified in the future.
 


	
 


	The architecture framework is divided into multiple layers. The framework provides a foundation for understanding the technical architecture of the mergers and acquisitions scenario. All layers flow together to create a complete, end-to-end solution.
 


	At a high level:
 


	User Layer: The user layer describes the end-user environment and end-point devices used to connect to resources.
 


	
		Users access resources from the Workspace app regardless of device, resulting in an identical experience across every form factor and device platform.
	



	Access Layer: The access layer describes how users authenticate to their Workspace and secondary resources.
 


	
		Users continue to authenticate with their pre-acquisition primary identity.
	
	
		Users continue to use their pre-acquisition multifactor authentication solution. If the company does not currently utilize multifactor authentication, CompanyA provides phone-based TOTP tokens that support push-based authentication.
	



	Resource Layer: The resource layer authorizes specific SaaS, web, and virtual resources for defined users and groups and their associated security policies.
 


	
		Regardless of a user’s originating company, the user must be allowed seamless access to any authorized resource hosted by other companies.
	
	
		To better protect data, CompanyA requires policies that disable the ability to print, download, and copy/paste content from the managed resource to and from the endpoint. CompanyA also requires restricting screen scraping\capturing applications and keylogging malware.
	
	
		Due to the unknown nature of the endpoint security status, CompanyA requires VPN-less access to resources using a managed enterprise browser with file encryption on unmanaged endpoints.
	



	Control Layer: The control layer defines how the underlying solution adjusts based on the user's activities.
 


	
		With the policies in place to protect the users and company data, there are still risks. CompanyA uses the Security Analytics service to identify compromised users or insider threats and automatically take actions to maintain a secure environment.
	
	
		The platform unifying multi-directory authentication must be secured from external threats and attacks. CompanyA enables the integrated Web App Firewall and Bot Management to protect the authentication point in the environment.
	



	Compute Layer: The Compute layer details how components are deployed on hardware, whether on-premises, in the cloud, or in a hybrid cloud.
 


	
		Citrix Workspace must be able to access every company’s identity provider, whether an on-premises Active Directory domain or a cloud-based offering from Okta, via a single Workspace site.
	



	The subsequent sections provide greater detail into specific design decisions for CompanyA’s mergers and acquisitions strategy reference architecture.
 


	Access Layer



	Authentication



	One of the challenges CompanyA experienced with previous acquisitions was integrating identity providers. Merging identity providers can take a significant amount of time. CompanyA utilizes a NetScaler to handle all authentication requests with the new strategy.
 


	
 


	The authentication process works by the NetScaler evaluating the user’s company and then applying the correct authentication request. As CompanyA has standardized on Okta for primary authentication, the request is forwarded to Okta. Once Okta completes user authentication, Okta replies to the NetScaler with a token identifying successful authentication.
 


	However, not every company that CompanyA acquires uses Okta. Instead, if the NetScaler identifies the user as from CompanyB, the user is asked for their Active Directory username and password. Those credentials are validated against CompanyB’s Active Directory domain. If CompanyB has already integrated a multifactor authentication solution, users will continue to use their registered tokens.
 


	Because strong authentication is a critical first step to security, CompanyA wants a solution that is ready for organizations not using multifactor authentication. In this instance, after the user authenticates with their company’s Active Directory domain, the NetScaler uses the native time-based one-time password engine to provide multifactor authentication for the user. Once registered to the user’s device, the user can enter the code manually or use the push notification service. Push notifications require users to select “Yes” from their registered mobile device to fulfill multifactor authentication.
 


	nFactor Policy



	NetScaler plays a critical role in primary authentication. A nFactor policy is utilized to make authentication decisions.
 


	
 


	To process authentication requests properly, the nFactor policy must know what company the user belongs to. Once the correct company identifier is selected, nFactor forwards the request to the correct branch of the authentication policy.
 


	Once the nFactor policy is defined, CompanyA can continue to expand it to incorporate additional organizations it acquires in the future. The nFactor policy allows CompanyA to create additional flows utilizing authentication standards, including LDAP, RADIUS, SAML, client certificates, OAuth OpenID Connect, Kerberos, and more. The nFactor policy engine allows CompanyA to continue integrating additional acquisitions without a redesign.
 


	Zero Trust Network Access



	To provide access to internal resources like private web apps, virtual apps, and virtual desktops, CompanyA plans to use the Citrix Enterprise Browser with Secure Private Access service and Citrix DaaS. These two services utilize a zero-trust network access solution, a more secure alternative to traditional VPNs.
 


	
 


	The Secure Private Access service and Citrix DaaS use the outbound control channel connections established by the cloud connectors and connector appliances. Those connections allow the user to access internal resources remotely. However, those connections are
 


	
		Limited in scope so that only the defined resource is accessible
	
	
		Based on the user’s primary, secured identity
	
	
		Only for specific protocols that disallow network traversal
	



	Company A wants to ensure the security of the devices connecting to the environment. To this end, they will implement compliance checks of all devices' security posture, including antivirus status, OS updates, and other necessary compliance requirements.
 


	Citrix Enterprise Browser will access all private web and SaaS applications. As a baseline, the following restrictions have been defined:
 


	
		
			
				
					Category
				 
			
			
				
					Web Apps
				 
			
			
				
					SaaS Apps
				 
			
			
				
					External URL
				 
			
		
		
			
				
					Clipboard Access
				 
			
			
				
					Restrict between managed browser apps     
				 
			
			
				
					Restrict between managed browser apps     
				 
			
			
				
					Enabled
				 
			
		
		
			
				
					Printing
				 
			
			
				
					Disabled
				 
			
			
				
					Disabled
				 
			
			
				
					Enabled
				 
			
		
		
			
				
					Downloads
				 
			
			
				
					Disabled
				 
			
			
				
					Disabled
				 
			
			
				
					Enabled
				 
			
		
		
			
				
					Uploads
				 
			
			
				
					Disabled
				 
			
			
				
					Disabled
				 
			
			
				
					Enabled
				 
			
		
		
			
				
					Watermark
				 
			
			
				
					Enabled
				 
			
			
				
					Enabled
				 
			
			
				
					Disabled
				 
			
		
		
			
				
					Restrict Keylogging
				 
			
			
				
					Enabled
				 
			
			
				
					Enabled
				 
			
			
				
					Enabled
				 
			
		
		
			
				
					Restrict Screen Capture
				 
			
			
				
					Enabled
				 
			
			
				
					Enabled
				 
			
			
				
					Disabled
				 
			
		
		
			
				
					Data Masking
				 
			
			
				
					Enabled
				 
			
			
				
					Enabled
				 
			
			
				
					Enabled
				 
			
		
		
			
				
				
					Browser cache &amp; download encryption
				 
			
			
				
				
					Enabled
				 
				
			
			
				
				
					Enabled
				 
				
			
			
				
				
					Enabled
				 
				
			
		
	



	Resource Layer



	Federated Authentication Services



	Users authenticate to Citrix Workspace with a primary identity. The primary identity is based on the company’s identity provider. One of the challenges with mergers and acquisitions is access to secondary resources based on a secondary identity. For example, CompanyA allows certain users from CompanyB and CompanyC to access virtual Windows applications. To access a virtual Windows application, the user must have a user account (secondary identity) within the virtual resource domain. A CompanyB user’s account (primary identity) will not authenticate to a CompanyA resource (secondary identity). To translate credentials between a primary and secondary identity and provide single sign-on to virtual Windows applications, CompanyA uses the Federated Authentication Service within Citrix Cloud.
 


	 
 


	The Workspace Single Sign-On Tech Brief contains additional information related to the Federated Authentication Service.     
 


	 
 


	Web and SaaS applications must be configured to accept authentication information coming from either NetScaler or identity provider for users from Companies B and C to access. 
 


	Resource Security Policies



	CompanyA wants to limit the risk of data loss due to an insider threat. Therefore, it incorporates numerous restrictions within the different application types to prevent users from copying, downloading, or printing data.
 


	As a baseline policy, CompanyA defined the following (with the ability to relax policies as needed based on user and application).
 


	
		
			
				
					Category
				 
			
			
				
					SaaS Apps
				 
			
			
				
					Web Apps
				 
			
			
				
					Virtual Apps/Desktops
				 
			
		
		
			
				
					Clipboard access
				 
			
			
				
					Denied
				 
			
			
				
					Denied
				 
			
			
				
					Client to Server only
				 
			
		
		
			
				
					Printing
				 
			
			
				
					Denied
				 
			
			
				
					Denied
				 
			
			
				
					Denied
				 
			
		
		
			
				
					Downloads
				 
			
			
				
					Denied
				 
			
			
				
					Denied
				 
			
			
				
					Denied
				 
			
		
		
			
				
					Watermark
				 
			
			
				
					Enabled
				 
			
			
				
					Enabled
				 
			
			
				
					Enabled
				 
			
		
		
			
				
					Keylogging Prevention
				 
			
			
				
					Enabled
				 
			
			
				
					Enabled
				 
			
			
				
					Enabled
				 
			
		
		
			
				
					Screenshot Prevention
				 
			
			
				
					Enabled
				 
			
			
				
					Enabled
				 
			
			
				
					Enabled
				 
			
		
		
			
				
					Data Masking
				 
			
			
				
					Enabled
				 
			
			
				
					Enabled
				 
			
			
				
					Enabled
				 
			
		
	



	Control Layer



	Web App Firewall



	When users authenticate to Citrix Workspace, they access a custom authentication form that supports the mergers and acquisitions strategy. The authentication form, hosted on NetScaler, is a public webpage that must be protected from bots and attacks. CompanyA uses the Bot Management and Web App Firewall components of the NetScaler solution to protect the public web app better.
 


	
 


	The first line of defense is Bot Management. Bots can crash or slow a public web app by overwhelming the service with fraudulent requests. The NetScaler's bot management component detects a bot request and prevents it from inundating the system.
 


	The second line of defense is the Web App Firewall. The Web App Firewall protects the policy engine that handles the submitted credentials from attack. These attacks typically include buffer overflow, SQL injection, and cross-site scripting. Web App Firewall detects and denies these attacks from impacting the authentication policy engine.
 


	Security Analytics



	CompanyA must identify and stop insider threats to the environment before the impact is too great.
 


	To help protect the environment, CompanyA uses Citrix Analytics for Security to identify insider threats, compromised users, and compromised endpoints. In many cases, a single instance of a threat does not warrant drastic action, but a series of threats can indicate a security breach.
 


	CompanyA developed the following initial security policies:
 


	
		
			
				
					Name
				 
			
			
				
					Conditions
				 
			
			
				
					Action
				 
			
			
				
					Reason
				 
			
		
		
			
				
					Unusual access
				 
			
			
				
					Log on from a suspicious IP and access from an unusual location
				 
			
			
				
					Lock user
				 
			
			
				
					If a user logs in from an unusual location and a suspicious IP, there is a strong indication the user was compromised.
				 
			
		
		
			
				
					Unusual app behavior
				 
			
			
				
					Unusual time of app usage and access from unusual location
				 
			
			
				
					Start session recording
				 
			
			
				
					If a user accesses a virtual app at a strange time and location, the user's security could be compromised. Security analytics records the session to have the admin verify its legitimacy.
				 
			
		
		
			
				
					Potential credential exploits
				 
			
			
				
					Excessive authentication failures and access from an unusual location
				 
			
			
				
					Add to watchlist
				 
			
			
				
					If a user has many authentication failures from an unusual location, it can indicate someone is trying to break into the system. However, the attacker has yet to succeed. You only need to add the user to the watchlist.
				 
			
		
	



	The Citrix User Risk Indicators document contains additional information regarding the various risk indicators provided to Citrix Security Analytics.
 


	The Citrix Policies and Actions page contains information about the remediation steps Citrix Security Analytics can perform.
 


	Sources



	We want to provide you with source diagrams that you can adapt to help you plan a mergers and acquisition strategy. These source diagrams are found here: Ref Architecture - M and A.vsdx]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_08/image.png.122bcbd775efb5b3d98b5e10ebef58d3.png" length="162324" type="image/png"/><pubDate>Tue, 27 Aug 2024 14:15:00 +0000</pubDate></item><item><title>Deployment Guide: Using Terraform to deploy a Citrix Virtual Apps and Desktops 2402 LTSR site on Nutanix AHV</title><link>https://community.stage.citrix.com/tech-zone/automation/cvad-terraform-nutanix/</link><description><![CDATA[Using Terraform to deploy a Citrix Virtual Apps and Desktops 2402 LTSR site on Nutanix AHV  
	 
 


	Overview



	This guide will showcase the possibility of deploying a Citrix DaaS Virtual Apps and Desktops 2402 LTSR site on Nutanix AHV using Terraform. We want to reduce manual interventions to the absolute minimum. 
	 
	In the end, you will have created:
 


	
		2 Virtual Machines registered with the Domain for the deployment of the Cloud Connectors
	
	
		1 Virtual Machine registered with the Domain for deploying all Terraform scripts (explanations follow later in this guide)
	
	
		A complete CVAD 2402 deployment containing 2 Delivery Controllers, StoreFront, WebStudio, and a License Server
	
	
		A Hosting Connection to Nutanix AHV
	
	
		A Machine Catalog containing 2 VDI machines based on a Master Image
	
	
		A Delivery Group with full AutoScale support
	



	 
 


	Installation and Configuration of Terraform



	Please find a description of installing Terraform and initial configurations in our Tech Zone Deployment Guide: Installing Terraform and Configuring the Citrix Terraform Provider.
 


	All Terraform configuration files can be found later on GitHub.
 


	 
	Deployment overview



	 
	This guide uses an existing domain and will not deploy a new one. For further instructions on deploying a new domain, refer to the guide Citrix DaaS and Terraform - Automatic Deployment of a Resource Location on Microsoft Azure. 
 


	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 primary Domain Controller using IPSec-based Site-to-Site VPNs. Each Resource Location has its subdomain.
 


	Note:  
	 
	We did not use loop-constructs or count keywords —each resource is created explicitly—for easier reading and understanding.
 


	The Terraform flow is split into five different modules:
 


	
		Module One - this part can be run on any computer where Terraform is installed :

		
			
				Creating the initially needed Resources on Nutanix AHV:

				
					
						Creating two Windows Server 2022-based VMs used as Delivery Controllers in Module 2.
					
					
						Creating a Windows Server 2022-based VM acting as an Administrative workstation for running the further Terraform Modules is necessary because WinRM is used for further configuration and deployment in these Modules.
					
					
						Creating all necessary scripts for joining the VMs to the existing sub-domain.
					
					
						Putting the VMs into the existing sub-domain.
					
				
			
		
	



	Important:  
	 
	The Master Image must be configured before Terraform uses it to create the Machine Catalog. 
	The Administrative VM must have Terraform installed - use the Chocolately Guidance provided previously for installation. 
	 
 


	
		Module Two - this part can only be run after completion of Module One. 
		It must be run on the previously created Administrative VM as the deployment relies heavily on WinRM:

		
			
				Configuring the three previously created Virtual Machines on Nutanix AHV:

				
					
						Installing the required software on the Delivery Controller VMs
					
					
						Installing the required software on the Admin-VM 
						 
					
				
			
			
				Creating the necessary Resources for the Citrix Virtual Apps and Desktops site:
				
					
						Creating the needed databases
					
					
						Creating the Citrix Virtual Apps and Desktops site
					
					
						Initial configuration of the Citrix Virtual Apps and Desktops site
					
					
						Installing Citrix StoreFront on the Delivery Controllers
					
				
			
		
	



	 
 


	
		Module Three -  this part can only be run after completion of Module Two. 
		It must be run on the previously created Administrative VM as the deployment relies heavily on WinRM:

		
			
				Configuring Citrix StoreFront server 
				 
			
		
	



	Important: 
	 
	The Terraform Provider can only configure StoreFront on one StoreFront server/Delivery Controller (listed in the Provider configuration). Due to current limitations In PowerShell-Invoking, we cannot automatically add the second Delivery Controller/Storefront Server to the configured StoreFront Cluster. 
	You must manually sync the configuration to the second StoreFront server/Delivery Controller.  
	 
 


	
 


	 
 


	
		Module Four - this part can only be run after completion of Module Three. 
		It must be run on the previously created Administrative VM as the deployment relies heavily on WinRM:

		
			
				Changing the SSL certificate on the Delivery Controllers/StoreFront servers to a publicly signed certificate to avoid certificate issues
			
			
				Uploading a valid license file to a License Server locally installed on the Primary Delivery Controller (optional)
			
			
				Setting the License Configuration of the site to point to a central License Server (optional) 
				 
			
		
	
	
		Module Five - this part can only be run after the completion of Module Four. 
		It must be run on the previously created Administrative VM as the deployment relies heavily on WinRM:
		
			
				Creating all needed entities in the Citrix Virtual Apps and Desktops site:
			
		
	



	
		Uploading and installing the Nutanix AHV Plug-In on the Delivery Controllers
	
	
		Creating a dedicated Hypervisor Connection to Nutanix AHV
	
	
		Creating a dedicated Hypervisor Resource Pool
	
	
		Creating a Machine Catalog based on the referenced Master Image
	
	
		Creating a Delivery Group
	
	
		Creating AutoScale settings and applying these to the newly created Delivery Group 
		 
	



	Important: 
	 
	Make sure that all Terraform-related VMs can communicate using WinRM. The deployment fails if the Admin-VM cannot connect to the CCs using WinRM. Various configuration guides for WinRM can be found on the Internet. We rely on GPOs related to WinRM to configure all Domain Members accordingly. 
	 
 


	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 td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0200b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\TACG&gt; test-wsman -ComputerName &lt;IP-Address of the computer you want to reach&gt; -Credential &lt;IP-Address of the computer you want to reach&gt;\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 &lt;IP-Address of the computer you want to reach&gt; -Credential &lt;IP-Address of the computer you wish to reach&gt;\administrator  
					 

					
						The response should look like:   
						[172.31.22.104]: PS C:\Users\Administrator\Documents&gt;
					 
				
			
		
	

	
		 
		A short Terraform script also checks if the communication via WinRM between the Admin-VM and, in this example, the DDC1-VM is working as intended:
	 

	
		
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #1e1e1e;
    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 #1e1e1e;
    background-color: #1e1e1e;
    padding: 10px;
}

.table_component td {
    border: 1px solid #1e1e1e;
    background-color: #1e1e1e;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}		
		
			
				
					
						
							 
							
								locals {
							 

							
								  #### Test the WinRM communication
							 

							
								  #### Need to invoke PowerShell as Domain User as the provisioner does not allow it to be run in a Domain Users-context
							 

							
								  TerraformTestWinRMScript     = &lt;&lt;-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 td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0200b0;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.  
						 

						
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
							null_resource.CreateTestScriptOnCC1 (remote-exec): C:\Users\Administrator&gt;powershell -File c:/temp/xdinst/DATA/Terraform-Test-WinRM.ps1
						 

						
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;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 that must be set.  
		Some configuration settings are propagated throughout the whole Terraform configuration...
	 

	
		You must start each module 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. 
		 
	 

	
		Important: 
		 
		Each module/step must be completed successfully before the next module can be started. 
		 
	 

	
		 
	 

	
		File System structure
	

	
		Root-Directory
	 

	
		Module 1: _CVADOnNutanix-Creation:
	 

	
		
			
				
				
			
			
				
					
						
							Filename                                                                                                       
						 
					
					
						
							Purpose                                                
						 
					
				
			
			
				
					
						
							_CVADOnNutanix-Create.tf
						 
					
					
						
							Resource configuration and primary flow definition
						 
					
				
				
					
						
							_CVADOnNutanix-Create-variables.tf
						 
					
					
						
							Definition of Variables
						 
					
				
				
					
						
							_CVADOnNutanix-Create.auto.tfvars.json
						 
					
					
						
							Setting the values of the Variables
						 
					
				
				
					
						
							_CVADOnNutanix-Create-Provider.tf
						 
					
					
						
							Provider definition and configuration
						 
					
				
				
					
						
							_CVADOnNutanix-Create-Provider-variables.tf
						 
					
					
						
							Definition of Variables
						 
					
				
				
					
						
							_CVADOnNutanix-Create-Provider.auto.tfvars.json
						 
					
					
						
							Setting the values of the Variables
						 
					
				
			
		
	

	
		 
		Module 2: _CVADOnNutanix-Configuration:
	 

	
		
			
				
				
			
			
				
					
						
							Filename                                                                                                        
						 
					
					
						
							Purpose                                               
						 
					
				
			
			
				
					
						
							_CVADOnNutanix-InstallCVADandSF.tf
						 
					
					
						
							Resource configuration and primary flow definition
						 
					
				
				
					
						
							_CVADOnNutanix-InstallCVADandSF-variables.tf
						 
					
					
						
							Definition of Variables
						 
					
				
				
					
						
							_CVADOnNutanix-InstallCVADandSF.auto.tfvars.json
						 
					
					
						
							Setting the values of the Variables
						 
					
				
				
					
						
							_CVADOnNutanix-InstallCVADandSF-Provider.tf
						 
					
					
						
							Provider definition and configuration
						 
					
				
				
					
						
							_CVADOnNutanix-InstallCVADandSF-Provider-variables.tf
						 
					
					
						
							Definition of Variables
						 
					
				
				
					
						
							_CVADOnNutanix-InstallCVADandSF-Provider.auto.tfvars.json
						 
					
					
						
							Setting the values of the Variables
						 
					
				
				
					
						
							DATA Directory
						 
					
					
						
							Contains dynamically created scripts which will be uploaded and executed on the Cloud Connector VMs
						 
					
				
				
					
						 
					
					
						 
					
				
			
		
	

	
		 
		Module 3: _CVADOnNutanix-Configuration-Storefront:
	 

	
		
			
				
				
			
			
				
					
						
							Filename                                                                                                        
						 
					
					
						
							Purpose                                               
						 
					
				
			
			
				
					
						
							CVADOnNutanix-Configuration-StoreFront.tf
						 
					
					
						
							Resource configuration and primary flow definition
						 
					
				
				
					
						
							CVADOnNutanix-Configuration-StoreFront-variables.tf
						 
					
					
						
							Definition of Variables
						 
					
				
				
					
						
							CVADOnNutanix-Configuration-StoreFront.auto.tfvars.json
						 
					
					
						
							Setting the values of the Variables
						 
					
				
				
					
						
							CVADOnNutanix-Configuration-Provider.tf
						 
					
					
						
							Provider definition and configuration
						 
					
				
				
					
						
							CVADOnNutanix-Configuration-Provider-variables.tf
						 
					
					
						
							Definition of Variables
						 
					
				
				
					
						
							CVADOnNutanix-Configuration-Provider.auto.tfvars.json
						 
					
					
						
							Setting the values of the Variables
						 
					
				
				
					
						
							terraform.tfvars
						 
					
					
						
							Setting the values of Variables that can not be set by a .json-based file
						 
					
				
			
		
	
	 

	
		Module 4: _CVADOnNutanix-Configuration-AfterStorefront:
	 

	
		
			
				
				
			
			
				
					
						
							Filename                                                                                                        
						 
					
					
						
							Purpose                                               
						 
					
				
			
			
				
					
						
							_CVADOnNutanix-Configuration-AfterStorefront-scripts.tf
						 
					
					
						
							Resource configuration and primary flow definition
						 
					
				
				
					
						
							_CVADOnNutanix-Configuration-AfterStorefront-scripts-variables.tf
						 
					
					
						
							Definition of Variables
						 
					
				
				
					
						
							__CVADOnNutanix-Configuration-AfterStorefront-scripts.auto.tfvars.json
						 
					
					
						
							Setting the values of the Variables
						 
					
				
				
					
						
							terraform.tvars
						 
					
					
						
							Setting the values of Variables that can not be set by a .json-based file
						 
					
				
				
					
						
							_CVADOnNutanix-Configuration-AfterStorefront-Provider.tf
						 
					
					
						
							Provider definition and configuration
						 
					
				
				
					
						
							_CVADOnNutanix-Configuration-AfterStorefront-Provider-variables.tf
						 
					
					
						
							Definition of Variables
						 
					
				
				
					
						
							_CVADOnNutanix-Configuration-AfterStorefront-Provider.auto.tfvars.json
						 
					
					
						
							Setting the values of the Variables
						 
					
				
			
		
	

	
		 
		Module 5: _CVADOnNutanix-CVAD-Entities:
	 

	
		
			
				
				
			
			
				
					
						
							Filename                                                                                                        
						 
					
					
						
							Purpose                                               
						 
					
				
			
			
				
					
						
							_CVADOnNutanix-CVAD-CreateCCEntities.tf
						 
					
					
						
							Resource configuration and primary flow definition
						 
					
				
				
					
						
							_CVADOnNutanix-CVAD-CreateCCEntities-variables.tf
						 
					
					
						
							Definition of Variables
						 
					
				
				
					
						
							_CVADOnNutanix-CVAD-CreateCCEntities.auto.tfvars.json
						 
					
					
						
							Setting the values of the Variables
						 
					
				
				
					
						
							terraform.tvars
						 
					
					
						
							Setting the values of Variables that can not be set by a .json-based file
						 
					
				
				
					
						
							_CVADOnNutanix-CVAD-Provider.tf
						 
					
					
						
							Provider definition and configuration
						 
					
				
				
					
						
							_CVADOnNutanix-CVAD-Provider-variables.tf
						 
					
					
						
							Definition of Variables
						 
					
				
				
					
						
							_CVADOnNutanix-CVAD-Provider.auto.tfvars.json
						 
					
					
						
							Setting the values of the Variables
						 
					
				
			
		
	

	
		 
	 

	
		Caution: 
		 
		All Terraform-related directories and files (.terraform, -terrafoom.lock.hcl, terraform.tfstate, terraform.tfstate) must not be changed or deleted - doing so might break the deployment. 
		 
	 

	
		 
		Change the settings in the .json or .tfvars files to match your needs.
	 

	
		To ensure a smooth and error-free build, the following prerequisites must be met before setting the corresponding settings or running the Terraform workflow.
	 

	
		 
	 

	
		Software Components for Configuration and Deployment 
		 
	 

	
		Important: 
		 
		Please ensure that the Delivery Controller VMs have the CVAD 2402 LTSR-ISO mounted so Terraform can access it during the configuration run. 
		 
	 

	
		The Terraform deployment needs actual versions of the following software components:
	 

	
		
			Nutanix AHV PlugIn for Citrix: NutanixAHV_Citrix_Plugin.msi. Terraform downloads this file automatically.
		
	

	
		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 to which all the required software is uploaded - or directly from the Internet.  
		The URIs of the Storage Repository can be set in the corresponding variables - e.g:
	 

	
		
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #1e1e1e;
    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 #1e1e1e;
    background-color: #1e1e1e;
    padding: 10px;
}

.table_component td {
    border: 1px solid #1e1e1e;
    background-color: #1e1e1e;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}		
		
			
				
					
						
							 
							"CC_Install_AHVPluginURI":"https://download.nutanix.com/citrixAhv/2.7.2.0/NutanixAHV_Citrix_plugin.msi 
							 
						
					
				
			
		
	

	
		 
	 

	
		We must also determine various information outside the Nutanix Cluster to correctly set specific variables, such as Network and Master Image names.
	 

	
		The Nutanix Cmdlets are a PowerShell extension specifically for accessing a Nutanix environment. 
		You need to install Nutanix Cmdlets to proceed with the following steps.
	 

	
		A detailed guide for installing Nutanix Cmdlets can be found at https://portal.nutanix.com/page/documents/details?targetId=PS-Cmdlets-AOS-v6_8:PS-Cmdlets-AOS-v6_8
	 

	
		 
		After successful installation, you should find a new icon on your Desktop: 
	 

	
		 
		The first step is to connect to the Nutanix Cluster:
	 

	
		
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0200b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}		
	 
	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0200b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\Windows\system32&gt; Connect-NutanixCluster -Server nutanix.the-austrian-citrix-guy.at -UserName XXXXXXXXXX -Password (Read-Host "Password: " -AsSecureString) -AcceptInvalidSSLCerts 
						Password:: *******************
					 

					
						Server                : nutanix.the-austrian-citrix-guy.at 
						UserName              : XXXXXXXXXX 
						Password              : System.Security.SecureString 
						AcceptInvalidSSLCerts : True 
						ForcedConnection      : False 
						ParameterSetName      : __AllParameterSets 
						MyInvocation          : System.Management.Automation.InvocationInfo 
						PagingParameters      : 
						InvokeCommand         : System.Management.Automation.CommandInvocationIntrinsics 
						Host                  : System.Management.Automation.Internal.Host.InternalHost 
						SessionState          : System.Management.Automation.SessionState 
						Events                : System.Management.Automation.PSLocalEventManager 
						JobRepository         : System.Management.Automation.JobRepository 
						JobManager            : System.Management.Automation.JobManager 
						InvokeProvider        : System.Management.Automation.ProviderIntrinsics 
						Stopping              : False 
						CommandRuntime        : Connect-NutanixCluster 
						CurrentPSTransaction  : 
						CommandOrigin         : Runspace 
						lastAccessTimestamp   : 20.08.2024 18:02:23 
						IsConnected           : True
					 

					
						PS C:\Windows\system32&gt;
					 
				
			
		
	

	
		 
		Now we need to get some Cluster Data – especially the Name:
	 

	
		
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0200b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}		
	 
	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0200b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\Windows\system32&gt; Get-NTNXCluster
					 

					
						id                                   : 00061bb2-8c9f-2210-41e4-48210b5c6d75::4747999213917072757 
						uuid                                 : 00061bb2-8c9f-2210-41e4-48210b5c6d75 
						clusterIncarnationId                 : 1719303537631760 
						clusterUuid                          : 00061bb2-8c9f-2210-41e4-48210b5c6d75 
						name                                 : TACG-NTX 
						clusterExternalIPAddress             : 
						isNSEnabled                          : False 
						clusterExternalDataServicesIPAddress : 
						segmentedIscsiDataServicesIPAddress  : 
						clusterMasqueradingIPAddress         : 
						clusterMasqueradingPort              : 
						timezone                             : UTC 
						supportVerbosityType                 : BASIC_COREDUMP 
						operationMode                        : Normal 
						encrypted                            : True 
						storageType                          : mixed 
						clusterFunctions                     : {NDFS} 
						isLTS                                : True 
						isRegisteredToPC                     : 
						numNodes                             : 1 
						blockSerials                         : {da6ea61d} 
						version                              : 6.5.2 
						fullVersion                          : el7.3-release-fraser-6.5.2-stable-f2ce4db7d67f495ebfd6208bef9ab0afec9c74af 
						targetVersion                        : 6.5.2 
						externalSubnet                       : 10.10.0.0/255.255.0.0 
						internalSubnet                       : 192.168.5.0/255.255.255.128 
						nccVersion                           : ncc-4.6.2.1 
						enableLockDown                       : False 
						enablePasswordRemoteLoginToCluster   : True 
						fingerprintContentCachePercentage    : 100 
						ssdPinningPercentageLimit            : 25 
						enableShadowClones                   : True 
						globalNfsWhiteList                   : {} 
						nameServers                          : {10.10.0.1, 10.10.100.1} 
						ntpServers                           : {1.pool.ntp.org, 0.pool.ntp.org} 
						hypervisorTypes                      : {kKvm} 
						multicluster                         : False 
						cloudcluster                         : False 
						hasSelfEncryptingDrive               : False 
						isUpgradeInProgress                  : False 
						clusterArch                          : X86_64 
						isSspEnabled                         : False 
						domain                               : 
						nosClusterAndHostsDomainJoined       : False 
						allHypervNodesInFailoverCluster      : False 
						enforceRackableUnitAwarePlacement    : False 
						disableDegradedNodeMonitoring        : False 
						commonCriteriaMode                   : False 
						enableOnDiskDedup                    : False 
						faultToleranceDomainType             : DISK 
						clusterGpus                          : 
						gpuDriverVersion                     :
					 

					
						PS C:\Windows\system32&gt;
					 
				
			
		
	

	
		 
		Now we need the Network Names:
	 

	
		
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0200b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}		
	 
	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0200b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\Windows\system32&gt; Get-NTNXNetwork
					 

					
						logicalTimestamp : 29 
						vlanId           : 0 
						uuid             : c78019a8-ad3b-4096-9f0c-e6aa1e5ce3a0 
						name             : NW-NTX-TACG 
						vswitchName      : br0 
						annotation       : 
						networkType      :
					 

					
						PS C:\Windows\system32&gt;
					 
				
			
		
	

	
		 
		We can deploy the Machine Catalog from a Template VM or a Snapshot. 
		We can determine both:
	 

	
		
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0200b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}		
	 
	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0200b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\Windows\system32&gt; Get-NTNXImage | FT Name, imagetype, uuid
					 

					
						name                imageType  uuid 
						----                ---------  ---- 
						IMG-W2K22-NoSecBoot DISK_IMAGE 57d43449-fc0b-4afc-bf49-7c05cc8f92cf 
						W2K22               ISO_IMAGE  080549c9-352a-4661-97a8-fc6d5c161cea 
						CVAD2402            ISO_IMAGE  fbcb9af3-b656-4437-9b8a-82de0d08d864 
						W11                 ISO_IMAGE  0ddf73af-2b2f-478c-a76b-5283c3dff241 
						VirtIO              ISO_IMAGE  dcf8e574-40ff-4a0d-ab07-289af0e548d7
					 

					
						PS C:\Windows\system32&gt;
					 
				
			
		
	

	
		In this case, the Image name we need is „IMG-W2K22-NoSecBoot“.
	 

	
		
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0200b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}		
	 
	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0200b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\Windows\system32&gt; Get-NTNXVM | FT vmname,uuid
					 

					
						vmName              uuid 
						------              ---- 
						TACG-NTX-CC1        084d27f2-c425-4713-aa50-89601da860d9 
						NTNX-da6ea61d-A-CVM ec66b4b8-9aa8-422d-a6cc-1178d75c7708 
						TACG-NTX-CC2        52864356-8179-451b-8561-7c1d0a2b9d4b 
						TACG-NTX-AVM        958ed855-7e1b-4c3a-910e-c6be16184b10 
						TACG-NTX-WK22-MI    a14204c5-3717-4636-a802-d4b404f1ecad 
						IMG-W2K22-NoSecBoot d61461f2-fe11-4732-b27e-087e49dfbe78
					 

					
						PS C:\Windows\system32&gt; Get-NTNXSnapshot | FT SnapshotName, vmuuid
					 

					
						snapshotName           vmUuid 
						------------           ------ 
						after config           958ed855-7e1b-4c3a-910e-c6be16184b10 
						mi                     a14204c5-3717-4636-a802-d4b404f1ecad 
						after config plugin    084d27f2-c425-4713-aa50-89601da860d9 
						after config plugin    52864356-8179-451b-8561-7c1d0a2b9d4b
					 

					
						PS C:\Windows\system32&gt;
					 
				
			
		
	

	
		In this case, the Snapshot name we need is „mi“.
	 

	
		 
		The following entity name we need is the Storage Container name:
	 

	
		
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0200b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}		
	 
	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0200b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\Windows\system32&gt; Get-NTNXContainer | FT id,vStoreNameList
					 

					
						id                                        vstoreNameList 
						--                                        -------------- 
						00061bb2-8c9f-2210-41e4-48210b5c6d75::4   {default-container-36052048168163} 
						00061bb2-8c9f-2210-41e4-48210b5c6d75::5   {NutanixManagementShare} 
						00061bb2-8c9f-2210-41e4-48210b5c6d75::776 {SelfServiceContainer}
					 

					
						PS C:\Windows\system32&gt;
					 
				
			
		
	

	
		We have determined all the values needed and can start the deployment. 
		 
	 

	
		Important:  
		 
		Do not forget to put all previously determined entity values in the corresponding .auto.tfvars.json variable files. 
		 
	 

	
		 
	 

	
		Module 1: Create the initially needed Resources on Nutanix AHV
	

	
		This module is split into the following configuration parts:
	 

	
		
			Creating the initially needed Resources on Nutanix AHV:

			
				
					Creating an AutoUnattend.xml setup file. So Nutanix can put the VMs into the domain.
				
				
					Creating two Windows Server 2022-based VMs, which will be used as Delivery Controller VMs in Step 2.
				
				
					Creating a Windows Server 2022-based VM acting as an Administrative workstation for running Terraform Modules 2, 3, 4 and 5 is necessary because we will use WinRM for all further configuration and deployment steps.
				
				
					Creating all necessary scripts for joining the VMs to the existing sub-domain.
				
				
					Putting the VMs into the existing subdomain.
				
			
		
	

	
		Terraform automatically does all these steps. 
		 
	 

	
		Important: 
		 
		This first part is separated from the following steps as we assume no Admin-VM is installed in the corresponding VPC/Subnet.  
		The Admin-VM is created now in Module 1, and all subsequent modules must be run from it. Working with WinRM calls over the Internet would pose a security threat and significantly reduce the performance of the Terraform-based deployment.  
		These are the reasons for the split approach. 
		 
	 
	 

	
		Important: 
		 
		Before starting the Terraform workflow, please ensure you have configured the variables according to your needs. 
		 
	 

	
		The configuration can be started by following the standard Terraform workflow:
	 

	
		terraform init, 
		 
		terraform plan 
		 
	 

	
		and if no errors occur
	 

	
		terraform apply
	 

	
		 
	 

	
		
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}		
		
			
				
					
						 
						
							PS C:\_TACG\_CVADOnNutanix&gt; terraform init 
							Initializing the backend... 
							Initializing provider plugins... 
							- Finding mastercard/restapi versions matching "1.18.2"... 
							- Finding latest version of hashicorp/template... 
							- Finding nutanix/nutanix versions matching "1.9.5"... 
							- Installing mastercard/restapi v1.18.2... 
							- Installed mastercard/restapi v1.18.2 (self-signed, key ID DCB8C431D71C30AB) 
							- Installing hashicorp/template v2.2.0... 
							- Installed hashicorp/template v2.2.0 (signed by HashiCorp) 
							- Installing nutanix/nutanix v1.9.5... 
							- Installed nutanix/nutanix v1.9.5 (signed by a HashiCorp partner, key ID BEA5F795571AD06E) 
							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\_CVADOnNutanix&gt; terraform plan 
							data.template_file.AutoUnattendXMLCC2: Reading... 
							data.template_file.AutoUnattendXMLAVM: Reading... 
							data.template_file.AutoUnattendXMLCC1: Reading... 
							data.template_file.AutoUnattendXMLAVM: Read complete after 0s [id=93574b4246d99671e081585c8db80c972dbb7c82bc40ea1b250b012c4495db92] 
							data.template_file.AutoUnattendXMLCC2: Read complete after 0s [id=c1e13a102cf9f4fc102c26ffa7c9d5ee931e5952e2ba138fc13209a50f11a18a] 
							data.template_file.AutoUnattendXMLCC1: Read complete after 0s [id=96f5f922adde3562c5cd2a9faa2ab847b4368cca2357b6dfceddbc9d1c770310] 
							data.nutanix_subnets.NTX-Subnets: Reading... 
							data.nutanix_clusters.NTX-Clusters: Reading... 
							data.nutanix_subnets.NTX-Subnets: Read complete after 0s [id=terraform-20240815064359727600000001] 
							data.nutanix_clusters.NTX-Clusters: Read complete after 0s [id=terraform-20240815064359832000000002] 
							data.nutanix_cluster.NTX-Cluster: Reading... 
							data.nutanix_cluster.NTX-Cluster: Read complete after 0s [id=00061bb2-8c9f-2210-41e4-48210b5c6d75] 
							 
							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: 
							 
							  # nutanix_virtual_machine.AVM-VM will be created 
							  + resource "nutanix_virtual_machine" "AVM-VM" { 
							   + api_version                                   = (known after apply) 
							   + availability_zone_reference                   = (known after apply) 
							   + boot_device_disk_address                      = (known after apply) 
							   + boot_device_mac_address                       = (known after apply) 
							   + boot_device_order_list                        = [ 
							       + "DISK", 
							     ] 
							   + boot_type                                     = "LEGACY" 
							   + cloud_init_cdrom_uuid                         = (known after apply) 
							   + cluster_name                                  = (known after apply) 
							   + cluster_uuid                                  = "00061bb2-8c9f-2210-41e4-48210b5c6d75" 
							   + description                                   = "TACG-NTX Admin VM" 
							   + enable_cpu_passthrough                        = false 
							   + enable_script_exec                            = (known after apply) 
							   + guest_customization_cloud_init_custom_key_values = (known after apply) 
							   + guest_customization_cloud_init_meta_data      = (known after apply) 
							   + guest_customization_cloud_init_user_data      = (known after apply) 
							   + guest_customization_is_overridable            = (known after apply) 
							   + guest_customization_sysprep                   = { 
							       + "install_type" = "PREPARED" 
							       + "unattend_xml" = "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHVuYXR0ZW5kIHhtbG5zPSJ1cm46c2NoZW1hcy1taWNyb3NvZnQtY29tOnVuYXR0ZW5kIj4KICAgIDxzZXR0aW5ncyBwYXNzPSJvb2JlU3lzdGVtIj4KICAgICAgICA8Y29tcG9uZW50IG5hbWU9Ik1pY3Jvc29mdC1XaW5kb3dzLVNoZWxsLVNldHVwIiBwcm9jZXNzb3JBcmNoaXRlY3R1cmU9ImFtZDY0IiBwdWJsaWNLZXlUb2tlbj0iMzFiZjM4NTZhZDM2NGUzNSIgbGFuZ3VhZ2U9Im5ldXRyYWwiIHZlcnNpb25TY29wZT0ibm9uU3hTIiB4bWxuczp3Y209Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vV01JQ29uZmlnLzIwMDIvU3RhdGUiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiPgogICAgICAgICAgICA8T09CRT4KICAgICAgICAgICAgICAgIDxIaWRlRVVMQVBhZ2U+dHJ1ZTwvSGlkZUVVTEFQYWdlPgogICAgICAgICAgICAgICAgPFNraXBVc2VyT09CRT50cnVlPC9Ta2lwVXNlck9PQkU+CiAgICAgICAgICAgICAgICA8SGlkZU9FTVJlZ2lzdHJhdGlvblNjcmVlbj50cnVlPC9IaWRlT0VNUmVnaXN0cmF0aW9uU2NyZWVuPgogICAgICAgICAgICAgICAgPEhpZGVPbmxpbmVBY2NvdW50U2NyZWVucz50cnVlPC9IaWRlT25saW5lQWNjb3VudFNjcmVlbnM+CiAgICAgICAgICAgICAgICA8SGlkZVdpcmVsZXNzU2V0dXBJbk9PQkU+dHJ1ZTwvSGlkZVdpcmVsZXNzU2V0dXBJbk9PQkU+CiAgICAgICAgICAgICAgICA8TmV0d29ya0xvY2F0aW9uPldvcms8L05ldHdvcmtMb2NhdGlvbj4KICAgICAgICAgICAgICAgIDxQcm90ZWN0WW91clBDPjE8L1Byb3RlY3RZb3VyUEM+CiAgICAgICAgICAgICAgICA8SGlkZUxvY2FsQWNjb3VudFNjcmVlbj50cnVlPC9IaWRlTG9jYWxBY2NvdW50U2NyZWVuPgogICAgICAgICAgICA8L09PQkU+CiAgICAgICAgICAgIDxVc2VyQWNjb3VudHM+CiAgICAgICAgICAgICAgICA8QWRtaW5pc3RyYXRvclBhc3N3b3JkPgogICAgICAgICAgICAgICAgICAgIDxWYWx1ZT5JUUJFQUdrQWJRQnVBRUVBVUFCWEFDRUFRUUJpQURJQU1BQXlBRFFBSVFBeEFDRUFJUUJCQUdRQWJRQnBBRzRBYVFCekFIUUFjZ0JoQUhRQWJ3QnlBRkFBWVFCekFITUFkd0J2QUhJQVpBQT08L1ZhbHVlPgogICAgICAgICAgICAgICAgICAgIDxQbGFpblRleHQ+ZmFsc2U8L1BsYWluVGV4dD4KICAgICAgICAgICAgICAgIDwvQWRtaW5pc3RyYXRvclBhc3N3b3JkPgogICAgICAgICAgICA8L1VzZXJBY2NvdW50cz4KICAgICAgICAgICAgPEF1dG9Mb2dvbj4KICAgICAgICAgICAgICAgIDxQYXNzd29yZD4KICAgICAgICAgICAgICAgICAgICA8VmFsdWU+SVFCRUFHa0FiUUJ1QUVFQVVBQlhBQ0VBUVFCaUFESUFNQUF5QURRQUlRQXhBQ0VBSVFCUUFHRUFjd0J6QUhjQWJ3QnlBR1FBPC9WYWx1ZT4KICAgICAgICAgICAgICAgICAgICA8UGxhaW5UZXh0PmZhbHNlPC9QbGFpblRleHQ+CiAgICAgICAgICAgICAgICA8L1Bhc3N3b3JkPgogICAgICAgICAgICAgICAgPEVuYWJsZWQ+dHJ1ZTwvRW5hYmxlZD4KICAgICAgICAgICAgICAgIDxMb2dvbkNvdW50PjE8L0xvZ29uQ291bnQ+CiAgICAgICAgICAgICAgICA8VXNlcm5hbWU+QWRtaW5pc3RyYXRvcjwvVXNlcm5hbWU+CiAgICAgICAgICAgIDwvQXV0b0xvZ29uPgogICAgICAgICAgICA8Rmlyc3RMb2dvbkNvbW1hbmRzPgogICAgICAgICAgICAgICAgPFN5bmNocm9ub3VzQ29tbWFuZCB3Y206YWN0aW9uPSJhZGQiPgogICAgICAgICAgICAgICAgICAgIDxDb21tYW5kTGluZT5wb3dlcnNoZWxsLmV4ZSAtTm9Qcm9maWxlIC1FeGVjdXRpb25Qb2xpY3kgQnlwYXNzIC1Db21tYW5kICJJbnZva2UtRXhwcmVzc2lvbiAoSW52b2tlLVdlYlJlcXVlc3QgLVVzZUJhc2ljUGFyc2luZyAtVXJpICdodHRwczovL3dtd2Jsb2IuYmxvYi5jb3JlLndpbmRvd3MubmV0L3RmZGF0YS9FbmFibGVXaW5STUZpcnN0UnVuLnBzMScpIjwvQ29tbWFuZExpbmU+CiAgICAgICAgICAgICAgICAgICAgPERlc2NyaXB0aW9uPnBvd2Vyc2hlbGxfcnVub25jZTwvRGVzY3JpcHRpb24+CiAgICAgICAgICAgICAgICAgICAgPE9yZGVyPjE8L09yZGVyPgogICAgICAgICAgICAgICAgPC9TeW5jaHJvbm91c0NvbW1hbmQ+CiAgICAgICAgICAgIDwvRmlyc3RMb2dvbkNvbW1hbmRzPgogICAgICAgIDwvY29tcG9uZW50PgogICAgICAgIDxjb21wb25lbnQgbmFtZT0iTWljcm9zb2Z0LVdpbmRvd3MtSW50ZXJuYXRpb25hbC1Db3JlIiBwcm9jZXNzb3JBcmNoaXRlY3R1cmU9ImFtZDY0IiBwdWJsaWNLZXlUb2tlbj0iMzFiZjM4NTZhZDM2NGUzNSIgbGFuZ3VhZ2U9Im5ldXRyYWwiIHZlcnNpb25TY29wZT0ibm9uU3hTIiB4bWxuczp3Y209Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vV01JQ29uZmlnLzIwMDIvU3RhdGUiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiPgogICAgICAgICAgICA8SW5wdXRMb2NhbGU+ZGUtQVQ8L0lucHV0TG9jYWxlPgogICAgICAgICAgICA8U3lzdGVtTG9jYWxlPmRlLUFUPC9TeXN0ZW1Mb2NhbGU+CiAgICAgICAgICAgIDxVSUxhbmd1YWdlPmVuLVVTPC9VSUxhbmd1YWdlPgogICAgICAgICAgICA8VXNlckxvY2FsZT5kZS1BVDwvVXNlckxvY2FsZT4KICAgICAgICAgICAgPFVJTGFuZ3VhZ2VGYWxsYmFjaz5kZS1ERTwvVUlMYW5ndWFnZUZhbGxiYWNrPgogICAgICAgIDwvY29tcG9uZW50PgogICAgPC9zZXR0aW5ncz4KICAgIDxzZXR0aW5ncyBwYXNzPSJzcGVjaWFsaXplIj4KICAgICAgICA8Y29tcG9uZW50IG5hbWU9Ik1pY3Jvc29mdC1XaW5kb3dzLVNoZWxsLVNldHVwIiBwcm9jZXNzb3JBcmNoaXRlY3R1cmU9ImFtZDY0IiBwdWJsaWNLZXlUb2tlbj0iMzFiZjM4NTZhZDM2NGUzNSIgbGFuZ3VhZ2U9Im5ldXRyYWwiIHZlcnNpb25TY29wZT0ibm9uU3hTIiB4bWxuczp3Y209Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vV01JQ29uZmlnLzIwMDIvU3RhdGUiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiPgogICAgICAgICAgICA8UmVnaXN0ZXJlZE9yZ2FuaXphdGlvbj5UQUNHIE51dGFuaXg8L1JlZ2lzdGVyZWRPcmdhbml6YXRpb24+CiAgICAgICAgICAgIDxSZWdpc3RlcmVkT3duZXI+VEFDRyBOdXRhbml4PC9SZWdpc3RlcmVkT3duZXI+CiAgICAgICAgICAgIDxUaW1lWm9uZT5HTVQgU3RhbmRhcmQgVGltZTwvVGltZVpvbmU+CiAgICAgICAgICAgIDxQcm9kdWN0S2V5Pk5SVjRRLVQ3WTczLUhNSjZWLTI4SlA4LTI5MlhZPC9Qcm9kdWN0S2V5PgogICAgICAgICAgICA8Q29tcHV0ZXJOYW1lPlRBQ0ctTlRYLUNWQVZNPC9Db21wdXRlck5hbWU+CiAgICAgICAgPC9jb21wb25lbnQ+CiAgICAgICAgPGNvbXBvbmVudCBuYW1lPSJNaWNyb3NvZnQtV2luZG93cy1VbmF0dGVuZGVkSm9pbiIgcHJvY2Vzc29yQXJjaGl0ZWN0dXJlPSJhbWQ2NCIgcHVibGljS2V5VG9rZW49IjMxYmYzODU2YWQzNjRlMzUiIGxhbmd1YWdlPSJuZXV0cmFsIiB2ZXJzaW9uU2NvcGU9Im5vblN4UyIgeG1sbnM6d2NtPSJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL1dNSUNvbmZpZy8yMDAyL1N0YXRlIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIj4KICAgICAgICAgICAgPElkZW50aWZpY2F0aW9uPgogICAgICAgICAgICAgICAgPENyZWRlbnRpYWxzPgogICAgICAgICAgICAgICAgICAgIDxEb21haW4+VEFDRzwvRG9tYWluPgogICAgICAgICAgICAgICAgICAgIDxVc2VybmFtZT5hZG1pbjwvVXNlcm5hbWU+CiAgICAgICAgICAgICAgICAgICAgPFBhc3N3b3JkPiFEaW1uQVBXIUFiMjAyNCExISE8L1Bhc3N3b3JkPgogICAgICAgICAgICAgICAgPC9DcmVkZW50aWFscz4KICAgICAgICAgICAgICAgIDxKb2luRG9tYWluPnRoZS1hdXN0cmlhbi1jaXRyaXgtZ3V5LmF0PC9Kb2luRG9tYWluPgogICAgICAgICAgICAgICAgPE1hY2hpbmVPYmplY3RPVT5PVT1Db21wdXRlcnMsZGM9dGhlLGRjPWF1c3RyaWFuLGRjPWNpdHJpeCxkYz1ndXksZGM9YXQ8L01hY2hpbmVPYmplY3RPVT4KICAgICAgICAgICAgICAgIDxVbnNlY3VyZUpvaW4gLz4KICAgICAgICAgICAgICAgIDxUaW1lb3V0UGVyaW9kSW5NaW51dGVzIC8+CiAgICAgICAgICAgICAgICA8RGVidWdKb2luT25seU9uVGhpc0Vycm9yIC8+CiAgICAgICAgICAgICAgICA8RGVidWdKb2luIC8+CiAgICAgICAgICAgIDwvSWRlbnRpZmljYXRpb24+CiAgICAgICAgPC9jb21wb25lbnQ+CiAgICA8L3NldHRpbmdzPgo8L3VuYXR0ZW5kPgo=" 
							     } 
							   + guest_customization_sysprep_custom_key_values = (known after apply) 
							   + guest_os_id                                   = (known after apply) 
							   + hardware_clock_timezone                       = (known after apply) 
							   + host_reference                                = (known after apply) 
							   + hypervisor_type                               = (known after apply) 
							   + id                                               = (known after apply) 
							   + is_vcpu_hard_pinned                           = false 
							   + machine_type                                  = (known after apply) 
							   + memory_size_mib                               = 8192 
							   + metadata                                      = (known after apply) 
							   + name                                          = "TACG-NTX-CVAVM" 
							   + ngt_credentials                               = (known after apply) 
							   + ngt_enabled_capability_list                   = (known after apply) 
							   + nic_list_status                               = (known after apply) 
							   + num_sockets                                   = 1 
							   + num_vcpus_per_socket                          = 2 
							   + num_vnuma_nodes                               = (known after apply) 
							   + nutanix_guest_tools                           = (known after apply) 
							   + owner_reference                               = (known after apply) 
							   + parent_reference                              = (known after apply) 
							   + power_state                                   = (known after apply) 
							   + power_state_mechanism                         = (known after apply) 
							   + project_reference                             = (known after apply) 
							   + should_fail_on_script_failure                 = (known after apply) 
							   + state                                         = (known after apply) 
							   + use_hot_add                                   = true 
							   + vga_console_enabled                           = (known after apply) 
							 
							   + categories (known after apply) 
							 
							   + disk_list { 
							       + data_source_reference  = { 
							           + "kind" = "image" 
							           + "uuid" = "57d43449-fc0b-4afc-bf49-7c05cc8f92cf" 
							         } 
							       + disk_size_bytes     = 0 
							       + disk_size_mib       = 0 
							       + uuid                = (known after apply) 
							       + volume_group_reference = (known after apply) 
							     } 
							 
							   + gpu_list (known after apply) 
							 
							   + nic_list { 
							       + is_connected                  = "true" 
							       + mac_address                   = (known after apply) 
							       + model                         = (known after apply) 
							       + network_function_chain_reference = (known after apply) 
							       + network_function_nic_type     = (known after apply) 
							       + nic_type                      = (known after apply) 
							       + num_queues                    = (known after apply) 
							       + subnet_name                   = (known after apply) 
							       + subnet_uuid                   = "5584d26a-f126-493e-84c5-9254c3d4378e" 
							       + uuid                          = (known after apply) 
							 
							       + ip_endpoint_list { 
							           + ip   = "10.10.175.3" 
							           + type = "ASSIGNED" 
							         } 
							     } 
							} 
							 
							  # nutanix_virtual_machine.CC1-VM will be created 
							  + resource "nutanix_virtual_machine" "CC1-VM" { 
							   + api_version                                   = (known after apply) 
							   + availability_zone_reference                   = (known after apply) 
							   + boot_device_disk_address                      = (known after apply) 
							   + boot_device_mac_address                       = (known after apply) 
							   + boot_device_order_list                        = [ 
							       + "DISK", 
							     ] 
							   + boot_type                                     = "LEGACY" 
							   + cloud_init_cdrom_uuid                         = (known after apply) 
							   + cluster_name                                  = (known after apply) 
							   + cluster_uuid                                  = "00061bb2-8c9f-2210-41e4-48210b5c6d75" 
							   + description                                   = "TACG-NTX-Delivery Controller 1" 
							   + enable_cpu_passthrough                        = false 
							   + enable_script_exec                            = (known after apply) 
							   + guest_customization_cloud_init_custom_key_values = (known after apply) 
							   + guest_customization_cloud_init_meta_data      = (known after apply) 
							   + guest_customization_cloud_init_user_data      = (known after apply) 
							   + guest_customization_is_overridable            = (known after apply) 
							   + guest_customization_sysprep                   = { 
							       + "install_type" = "PREPARED" 
							       + "unattend_xml" = "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHVuYXR0ZW5kIHhtbG5zPSJ1cm46c2NoZW1hcy1taWNyb3NvZnQtY29tOnVuYXR0ZW5kIj4KICAgIDxzZXR0aW5ncyBwYXNzPSJvb2JlU3lzdGVtIj4KICAgICAgICA8Y29tcG9uZW50IG5hbWU9Ik1pY3Jvc29mdC1XaW5kb3dzLVNoZWxsLVNldHVwIiBwcm9jZXNzb3JBcmNoaXRlY3R1cmU9ImFtZDY0IiBwdWJsaWNLZXlUb2tlbj0iMzFiZjM4NTZhZDM2NGUzNSIgbGFuZ3VhZ2U9Im5ldXRyYWwiIHZlcnNpb25TY29wZT0ibm9uU3hTIiB4bWxuczp3Y209Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vV01JQ29uZmlnLzIwMDIvU3RhdGUiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiPgogICAgICAgICAgICA8T09CRT4KICAgICAgICAgICAgICAgIDxIaWRlRVVMQVBhZ2U+dHJ1ZTwvSGlkZUVVTEFQYWdlPgogICAgICAgICAgICAgICAgPFNraXBVc2VyT09CRT50cnVlPC9Ta2lwVXNlck9PQkU+CiAgICAgICAgICAgICAgICA8SGlkZU9FTVJlZ2lzdHJhdGlvblNjcmVlbj50cnVlPC9IaWRlT0VNUmVnaXN0cmF0aW9uU2NyZWVuPgogICAgICAgICAgICAgICAgPEhpZGVPbmxpbmVBY2NvdW50U2NyZWVucz50cnVlPC9IaWRlT25saW5lQWNjb3VudFNjcmVlbnM+CiAgICAgICAgICAgICAgICA8SGlkZVdpcmVsZXNzU2V0dXBJbk9PQkU+dHJ1ZTwvSGlkZVdpcmVsZXNzU2V0dXBJbk9PQkU+CiAgICAgICAgICAgICAgICA8TmV0d29ya0xvY2F0aW9uPldvcms8L05ldHdvcmtMb2NhdGlvbj4KICAgICAgICAgICAgICAgIDxQcm90ZWN0WW91clBDPjE8L1Byb3RlY3RZb3VyUEM+CiAgICAgICAgICAgICAgICA8SGlkZUxvY2FsQWNjb3VudFNjcmVlbj50cnVlPC9IaWRlTG9jYWxBY2NvdW50U2NyZWVuPgogICAgICAgICAgICA8L09PQkU+CiAgICAgICAgICAgIDxVc2VyQWNjb3VudHM+CiAgICAgICAgICAgICAgICA8QWRtaW5pc3RyYXRvclBhc3N3b3JkPgogICAgICAgICAgICAgICAgICAgIDxWYWx1ZT5JUUJFQUdrQWJRQnVBRUVBVUFCWEFDRUFRUUJpQURJQU1BQXlBRFFBSVFBeEFDRUFJUUJCQUdRQWJRQnBBRzRBYVFCekFIUUFjZ0JoQUhRQWJ3QnlBRkFBWVFCekFITUFkd0J2QUhJQVpBQT08L1ZhbHVlPgogICAgICAgICAgICAgICAgICAgIDxQbGFpblRleHQ+ZmFsc2U8L1BsYWluVGV4dD4KICAgICAgICAgICAgICAgIDwvQWRtaW5pc3RyYXRvclBhc3N3b3JkPgogICAgICAgICAgICA8L1VzZXJBY2NvdW50cz4KICAgICAgICAgICAgPEF1dG9Mb2dvbj4KICAgICAgICAgICAgICAgIDxQYXNzd29yZD4KICAgICAgICAgICAgICAgICAgICA8VmFsdWU+SVFCRUFHa0FiUUJ1QUVFQVVBQlhBQ0VBUVFCaUFESUFNQUF5QURRQUlRQXhBQ0VBSVFCUUFHRUFjd0J6QUhjQWJ3QnlBR1FBPC9WYWx1ZT4KICAgICAgICAgICAgICAgICAgICA8UGxhaW5UZXh0PmZhbHNlPC9QbGFpblRleHQ+CiAgICAgICAgICAgICAgICA8L1Bhc3N3b3JkPgogICAgICAgICAgICAgICAgPEVuYWJsZWQ+dHJ1ZTwvRW5hYmxlZD4KICAgICAgICAgICAgICAgIDxMb2dvbkNvdW50PjE8L0xvZ29uQ291bnQ+CiAgICAgICAgICAgICAgICA8VXNlcm5hbWU+QWRtaW5pc3RyYXRvcjwvVXNlcm5hbWU+CiAgICAgICAgICAgIDwvQXV0b0xvZ29uPgogICAgICAgICAgICA8Rmlyc3RMb2dvbkNvbW1hbmRzPgogICAgICAgICAgICAgICAgPFN5bmNocm9ub3VzQ29tbWFuZCB3Y206YWN0aW9uPSJhZGQiPgogICAgICAgICAgICAgICAgICAgIDxDb21tYW5kTGluZT5wb3dlcnNoZWxsLmV4ZSAtTm9Qcm9maWxlIC1FeGVjdXRpb25Qb2xpY3kgQnlwYXNzIC1Db21tYW5kICJJbnZva2UtRXhwcmVzc2lvbiAoSW52b2tlLVdlYlJlcXVlc3QgLVVzZUJhc2ljUGFyc2luZyAtVXJpICdodHRwczovL3dtd2Jsb2IuYmxvYi5jb3JlLndpbmRvd3MubmV0L3RmZGF0YS9FbmFibGVXaW5STUZpcnN0UnVuLnBzMScpIjwvQ29tbWFuZExpbmU+CiAgICAgICAgICAgICAgICAgICAgPERlc2NyaXB0aW9uPnBvd2Vyc2hlbGxfcnVub25jZTwvRGVzY3JpcHRpb24+CiAgICAgICAgICAgICAgICAgICAgPE9yZGVyPjE8L09yZGVyPgogICAgICAgICAgICAgICAgPC9TeW5jaHJvbm91c0NvbW1hbmQ+CiAgICAgICAgICAgIDwvRmlyc3RMb2dvbkNvbW1hbmRzPgogICAgICAgIDwvY29tcG9uZW50PgogICAgICAgIDxjb21wb25lbnQgbmFtZT0iTWljcm9zb2Z0LVdpbmRvd3MtSW50ZXJuYXRpb25hbC1Db3JlIiBwcm9jZXNzb3JBcmNoaXRlY3R1cmU9ImFtZDY0IiBwdWJsaWNLZXlUb2tlbj0iMzFiZjM4NTZhZDM2NGUzNSIgbGFuZ3VhZ2U9Im5ldXRyYWwiIHZlcnNpb25TY29wZT0ibm9uU3hTIiB4bWxuczp3Y209Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vV01JQ29uZmlnLzIwMDIvU3RhdGUiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiPgogICAgICAgICAgICA8SW5wdXRMb2NhbGU+ZGUtQVQ8L0lucHV0TG9jYWxlPgogICAgICAgICAgICA8U3lzdGVtTG9jYWxlPmRlLUFUPC9TeXN0ZW1Mb2NhbGU+CiAgICAgICAgICAgIDxVSUxhbmd1YWdlPmVuLVVTPC9VSUxhbmd1YWdlPgogICAgICAgICAgICA8VXNlckxvY2FsZT5kZS1BVDwvVXNlckxvY2FsZT4KICAgICAgICAgICAgPFVJTGFuZ3VhZ2VGYWxsYmFjaz5kZS1ERTwvVUlMYW5ndWFnZUZhbGxiYWNrPgogICAgICAgIDwvY29tcG9uZW50PgogICAgPC9zZXR0aW5ncz4KICAgIDxzZXR0aW5ncyBwYXNzPSJzcGVjaWFsaXplIj4KICAgICAgICA8Y29tcG9uZW50IG5hbWU9Ik1pY3Jvc29mdC1XaW5kb3dzLVNoZWxsLVNldHVwIiBwcm9jZXNzb3JBcmNoaXRlY3R1cmU9ImFtZDY0IiBwdWJsaWNLZXlUb2tlbj0iMzFiZjM4NTZhZDM2NGUzNSIgbGFuZ3VhZ2U9Im5ldXRyYWwiIHZlcnNpb25TY29wZT0ibm9uU3hTIiB4bWxuczp3Y209Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vV01JQ29uZmlnLzIwMDIvU3RhdGUiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiPgogICAgICAgICAgICA8UmVnaXN0ZXJlZE9yZ2FuaXphdGlvbj5UQUNHIE51dGFuaXg8L1JlZ2lzdGVyZWRPcmdhbml6YXRpb24+CiAgICAgICAgICAgIDxSZWdpc3RlcmVkT3duZXI+VEFDRyBOdXRhbml4PC9SZWdpc3RlcmVkT3duZXI+CiAgICAgICAgICAgIDxUaW1lWm9uZT5HTVQgU3RhbmRhcmQgVGltZTwvVGltZVpvbmU+CiAgICAgICAgICAgIDxQcm9kdWN0S2V5Pk5SVjRRLVQ3WTczLUhNSjZWLTI4SlA4LTI5MlhZPC9Qcm9kdWN0S2V5PgogICAgICAgICAgICA8Q29tcHV0ZXJOYW1lPlRBQ0ctTlRYLUREQzE8L0NvbXB1dGVyTmFtZT4KICAgICAgICA8L2NvbXBvbmVudD4KICAgICAgICA8Y29tcG9uZW50IG5hbWU9Ik1pY3Jvc29mdC1XaW5kb3dzLVVuYXR0ZW5kZWRKb2luIiBwcm9jZXNzb3JBcmNoaXRlY3R1cmU9ImFtZDY0IiBwdWJsaWNLZXlUb2tlbj0iMzFiZjM4NTZhZDM2NGUzNSIgbGFuZ3VhZ2U9Im5ldXRyYWwiIHZlcnNpb25TY29wZT0ibm9uU3hTIiB4bWxuczp3Y209Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vV01JQ29uZmlnLzIwMDIvU3RhdGUiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiPgogICAgICAgICAgICA8SWRlbnRpZmljYXRpb24+CiAgICAgICAgICAgICAgICA8Q3JlZGVudGlhbHM+CiAgICAgICAgICAgICAgICAgICAgPERvbWFpbj5UQUNHPC9Eb21haW4+CiAgICAgICAgICAgICAgICAgICAgPFVzZXJuYW1lPmFkbWluPC9Vc2VybmFtZT4KICAgICAgICAgICAgICAgICAgICA8UGFzc3dvcmQ+IURpbW5BUFchQWIyMDI0ITEhITwvUGFzc3dvcmQ+CiAgICAgICAgICAgICAgICA8L0NyZWRlbnRpYWxzPgogICAgICAgICAgICAgICAgPEpvaW5Eb21haW4+dGhlLWF1c3RyaWFuLWNpdHJpeC1ndXkuYXQ8L0pvaW5Eb21haW4+CiAgICAgICAgICAgICAgICA8TWFjaGluZU9iamVjdE9VPk9VPUNvbXB1dGVycyxkYz10aGUsZGM9YXVzdHJpYW4sZGM9Y2l0cml4LGRjPWd1eSxkYz1hdDwvTWFjaGluZU9iamVjdE9VPgogICAgICAgICAgICAgICAgPFVuc2VjdXJlSm9pbiAvPgogICAgICAgICAgICAgICAgPFRpbWVvdXRQZXJpb2RJbk1pbnV0ZXMgLz4KICAgICAgICAgICAgICAgIDxEZWJ1Z0pvaW5Pbmx5T25UaGlzRXJyb3IgLz4KICAgICAgICAgICAgICAgIDxEZWJ1Z0pvaW4gLz4KICAgICAgICAgICAgPC9JZGVudGlmaWNhdGlvbj4KICAgICAgICA8L2NvbXBvbmVudD4KICAgIDwvc2V0dGluZ3M+CjwvdW5hdHRlbmQ+Cg==" 
							     } 
							   + guest_customization_sysprep_custom_key_values = (known after apply) 
							   + guest_os_id                                   = (known after apply) 
							   + hardware_clock_timezone                       = (known after apply) 
							   + host_reference                                = (known after apply) 
							   + hypervisor_type                               = (known after apply) 
							   + id                                               = (known after apply) 
							   + is_vcpu_hard_pinned                           = false 
							   + machine_type                                  = (known after apply) 
							   + memory_size_mib                               = 192 
							   + metadata                                      = (known after apply) 
							   + name                                          = "TACG-NTX-DDC1" 
							   + ngt_credentials                               = (known after apply) 
							   + ngt_enabled_capability_list                   = (known after apply) 
							   + nic_list_status                               = (known after apply) 
							   + num_sockets                                   = 1 
							   + num_vcpus_per_socket                          = 2 
							   + num_vnuma_nodes                               = (known after apply) 
							   + nutanix_guest_tools                           = (known after apply) 
							   + owner_reference                               = (known after apply) 
							   + parent_reference                              = (known after apply) 
							   + power_state                                   = (known after apply) 
							   + power_state_mechanism                         = (known after apply) 
							   + project_reference                             = (known after apply) 
							   + should_fail_on_script_failure                 = (known after apply) 
							   + state                                         = (known after apply) 
							   + use_hot_add                                   = true 
							   + vga_console_enabled                           = (known after apply) 
							 
							   + categories (known after apply) 
							 
							   + disk_list { 
							       + data_source_reference  = { 
							           + "kind" = "image" 
							           + "uuid" = "57d43449-fc0b-4afc-bf49-7c05cc8f92cf" 
							         } 
							       + disk_size_bytes     = 0 
							       + disk_size_mib       = 0 
							       + uuid                = (known after apply) 
							       + volume_group_reference = (known after apply) 
							     } 
							 
							   + gpu_list (known after apply) 
							 
							   + nic_list { 
							       + is_connected                  = "true" 
							       + mac_address                   = (known after apply) 
							       + model                         = (known after apply) 
							       + network_function_chain_reference = (known after apply) 
							       + network_function_nic_type     = (known after apply) 
							       + nic_type                      = (known after apply) 
							       + num_queues                    = (known after apply) 
							       + subnet_name                   = (known after apply) 
							       + subnet_uuid                   = "5584d26a-f126-493e-84c5-9254c3d4378e" 
							       + uuid                          = (known after apply) 
							 
							       + ip_endpoint_list { 
							           + ip   = "10.10.175.1" 
							           + type = "ASSIGNED" 
							         } 
							     } 
							} 
							 
							  # nutanix_virtual_machine.CC2-VM will be created 
							  + resource "nutanix_virtual_machine" "CC2-VM" { 
							   + api_version                                   = (known after apply) 
							   + availability_zone_reference                   = (known after apply) 
							   + boot_device_disk_address                      = (known after apply) 
							   + boot_device_mac_address                       = (known after apply) 
							   + boot_device_order_list                        = [ 
							       + "DISK", 
							     ] 
							  + boot_type                                     = "LEGACY" 
							   + cloud_init_cdrom_uuid                         = (known after apply) 
							   + cluster_name                                  = (known after apply) 
							   + cluster_uuid                                  = "00061bb2-8c9f-2210-41e4-48210b5c6d75" 
							   + description                                   = "TACG-NTX-Delivery Controller 2" 
							   + enable_cpu_passthrough                        = false 
							   + enable_script_exec                            = (known after apply) 
							   + guest_customization_cloud_init_custom_key_values = (known after apply) 
							   + guest_customization_cloud_init_meta_data      = (known after apply) 
							   + guest_customization_cloud_init_user_data      = (known after apply) 
							   + guest_customization_is_overridable            = (known after apply) 
							   + guest_customization_sysprep                   = { 
							       + "install_type" = "PREPARED" 
							       + "unattend_xml" = "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHVuYXR0ZW5kIHhtbG5zPSJ1cm46c2NoZW1hcy1taWNyb3NvZnQtY29tOnVuYXR0ZW5kIj4KICAgIDxzZXR0aW5ncyBwYXNzPSJvb2JlU3lzdGVtIj4KICAgICAgICA8Y29tcG9uZW50IG5hbWU9Ik1pY3Jvc29mdC1XaW5kb3dzLVNoZWxsLVNldHVwIiBwcm9jZXNzb3JBcmNoaXRlY3R1cmU9ImFtZDY0IiBwdWJsaWNLZXlUb2tlbj0iMzFiZjM4NTZhZDM2NGUzNSIgbGFuZ3VhZ2U9Im5ldXRyYWwiIHZlcnNpb25TY29wZT0ibm9uU3hTIiB4bWxuczp3Y209Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vV01JQ29uZmlnLzIwMDIvU3RhdGUiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiPgogICAgICAgICAgICA8T09CRT4KICAgICAgICAgICAgICAgIDxIaWRlRVVMQVBhZ2U+dHJ1ZTwvSGlkZUVVTEFQYWdlPgogICAgICAgICAgICAgICAgPFNraXBVc2VyT09CRT50cnVlPC9Ta2lwVXNlck9PQkU+CiAgICAgICAgICAgICAgICA8SGlkZU9FTVJlZ2lzdHJhdGlvblNjcmVlbj50cnVlPC9IaWRlT0VNUmVnaXN0cmF0aW9uU2NyZWVuPgogICAgICAgICAgICAgICAgPEhpZGVPbmxpbmVBY2NvdW50U2NyZWVucz50cnVlPC9IaWRlT25saW5lQWNjb3VudFNjcmVlbnM+CiAgICAgICAgICAgICAgICA8SGlkZVdpcmVsZXNzU2V0dXBJbk9PQkU+dHJ1ZTwvSGlkZVdpcmVsZXNzU2V0dXBJbk9PQkU+CiAgICAgICAgICAgICAgICA8TmV0d29ya0xvY2F0aW9uPldvcms8L05ldHdvcmtMb2NhdGlvbj4KICAgICAgICAgICAgICAgIDxQcm90ZWN0WW91clBDPjE8L1Byb3RlY3RZb3VyUEM+CiAgICAgICAgICAgICAgICA8SGlkZUxvY2FsQWNjb3VudFNjcmVlbj50cnVlPC9IaWRlTG9jYWxBY2NvdW50U2NyZWVuPgogICAgICAgICAgICA8L09PQkU+CiAgICAgICAgICAgIDxVc2VyQWNjb3VudHM+CiAgICAgICAgICAgICAgICA8QWRtaW5pc3RyYXRvclBhc3N3b3JkPgogICAgICAgICAgICAgICAgICAgIDxWYWx1ZT5JUUJFQUdrQWJRQnVBRUVBVUFCWEFDRUFRUUJpQURJQU1BQXlBRFFBSVFBeEFDRUFJUUJCQUdRQWJRQnBBRzRBYVFCekFIUUFjZ0JoQUhRQWJ3QnlBRkFBWVFCekFITUFkd0J2QUhJQVpBQT08L1ZhbHVlPgogICAgICAgICAgICAgICAgICAgIDxQbGFpblRleHQ+ZmFsc2U8L1BsYWluVGV4dD4KICAgICAgICAgICAgICAgIDwvQWRtaW5pc3RyYXRvclBhc3N3b3JkPgogICAgICAgICAgICA8L1VzZXJBY2NvdW50cz4KICAgICAgICAgICAgPEF1dG9Mb2dvbj4KICAgICAgICAgICAgICAgIDxQYXNzd29yZD4KICAgICAgICAgICAgICAgICAgICA8VmFsdWU+SVFCRUFHa0FiUUJ1QUVFQVVBQlhBQ0VBUVFCaUFESUFNQUF5QURRQUlRQXhBQ0VBSVFCUUFHRUFjd0J6QUhjQWJ3QnlBR1FBPC9WYWx1ZT4KICAgICAgICAgICAgICAgICAgICA8UGxhaW5UZXh0PmZhbHNlPC9QbGFpblRleHQ+CiAgICAgICAgICAgICAgICA8L1Bhc3N3b3JkPgogICAgICAgICAgICAgICAgPEVuYWJsZWQ+dHJ1ZTwvRW5hYmxlZD4KICAgICAgICAgICAgICAgIDxMb2dvbkNvdW50PjE8L0xvZ29uQ291bnQ+CiAgICAgICAgICAgICAgICA8VXNlcm5hbWU+QWRtaW5pc3RyYXRvcjwvVXNlcm5hbWU+CiAgICAgICAgICAgIDwvQXV0b0xvZ29uPgogICAgICAgICAgICA8Rmlyc3RMb2dvbkNvbW1hbmRzPgogICAgICAgICAgICAgICAgPFN5bmNocm9ub3VzQ29tbWFuZCB3Y206YWN0aW9uPSJhZGQiPgogICAgICAgICAgICAgICAgICAgIDxDb21tYW5kTGluZT5wb3dlcnNoZWxsLmV4ZSAtTm9Qcm9maWxlIC1FeGVjdXRpb25Qb2xpY3kgQnlwYXNzIC1Db21tYW5kICJJbnZva2UtRXhwcmVzc2lvbiAoSW52b2tlLVdlYlJlcXVlc3QgLVVzZUJhc2ljUGFyc2luZyAtVXJpICdodHRwczovL3dtd2Jsb2IuYmxvYi5jb3JlLndpbmRvd3MubmV0L3RmZGF0YS9FbmFibGVXaW5STUZpcnN0UnVuLnBzMScpIjwvQ29tbWFuZExpbmU+CiAgICAgICAgICAgICAgICAgICAgPERlc2NyaXB0aW9uPnBvd2Vyc2hlbGxfcnVub25jZTwvRGVzY3JpcHRpb24+CiAgICAgICAgICAgICAgICAgICAgPE9yZGVyPjE8L09yZGVyPgogICAgICAgICAgICAgICAgPC9TeW5jaHJvbm91c0NvbW1hbmQ+CiAgICAgICAgICAgIDwvRmlyc3RMb2dvbkNvbW1hbmRzPgogICAgICAgIDwvY29tcG9uZW50PgogICAgICAgIDxjb21wb25lbnQgbmFtZT0iTWljcm9zb2Z0LVdpbmRvd3MtSW50ZXJuYXRpb25hbC1Db3JlIiBwcm9jZXNzb3JBcmNoaXRlY3R1cmU9ImFtZDY0IiBwdWJsaWNLZXlUb2tlbj0iMzFiZjM4NTZhZDM2NGUzNSIgbGFuZ3VhZ2U9Im5ldXRyYWwiIHZlcnNpb25TY29wZT0ibm9uU3hTIiB4bWxuczp3Y209Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vV01JQ29uZmlnLzIwMDIvU3RhdGUiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiPgogICAgICAgICAgICA8SW5wdXRMb2NhbGU+ZGUtQVQ8L0lucHV0TG9jYWxlPgogICAgICAgICAgICA8U3lzdGVtTG9jYWxlPmRlLUFUPC9TeXN0ZW1Mb2NhbGU+CiAgICAgICAgICAgIDxVSUxhbmd1YWdlPmVuLVVTPC9VSUxhbmd1YWdlPgogICAgICAgICAgICA8VXNlckxvY2FsZT5kZS1BVDwvVXNlckxvY2FsZT4KICAgICAgICAgICAgPFVJTGFuZ3VhZ2VGYWxsYmFjaz5kZS1ERTwvVUlMYW5ndWFnZUZhbGxiYWNrPgogICAgICAgIDwvY29tcG9uZW50PgogICAgPC9zZXR0aW5ncz4KICAgIDxzZXR0aW5ncyBwYXNzPSJzcGVjaWFsaXplIj4KICAgICAgICA8Y29tcG9uZW50IG5hbWU9Ik1pY3Jvc29mdC1XaW5kb3dzLVNoZWxsLVNldHVwIiBwcm9jZXNzb3JBcmNoaXRlY3R1cmU9ImFtZDY0IiBwdWJsaWNLZXlUb2tlbj0iMzFiZjM4NTZhZDM2NGUzNSIgbGFuZ3VhZ2U9Im5ldXRyYWwiIHZlcnNpb25TY29wZT0ibm9uU3hTIiB4bWxuczp3Y209Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vV01JQ29uZmlnLzIwMDIvU3RhdGUiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiPgogICAgICAgICAgICA8UmVnaXN0ZXJlZE9yZ2FuaXphdGlvbj5UQUNHIE51dGFuaXg8L1JlZ2lzdGVyZWRPcmdhbml6YXRpb24+CiAgICAgICAgICAgIDxSZWdpc3RlcmVkT3duZXI+VEFDRyBOdXRhbml4PC9SZWdpc3RlcmVkT3duZXI+CiAgICAgICAgICAgIDxUaW1lWm9uZT5HTVQgU3RhbmRhcmQgVGltZTwvVGltZVpvbmU+CiAgICAgICAgICAgIDxQcm9kdWN0S2V5Pk5SVjRRLVQ3WTczLUhNSjZWLTI4SlA4LTI5MlhZPC9Qcm9kdWN0S2V5PgogICAgICAgICAgICA8Q29tcHV0ZXJOYW1lPlRBQ0ctTlRYLUREQzI8L0NvbXB1dGVyTmFtZT4KICAgICAgICA8L2NvbXBvbmVudD4KICAgICAgICA8Y29tcG9uZW50IG5hbWU9Ik1pY3Jvc29mdC1XaW5kb3dzLVVuYXR0ZW5kZWRKb2luIiBwcm9jZXNzb3JBcmNoaXRlY3R1cmU9ImFtZDY0IiBwdWJsaWNLZXlUb2tlbj0iMzFiZjM4NTZhZDM2NGUzNSIgbGFuZ3VhZ2U9Im5ldXRyYWwiIHZlcnNpb25TY29wZT0ibm9uU3hTIiB4bWxuczp3Y209Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vV01JQ29uZmlnLzIwMDIvU3RhdGUiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiPgogICAgICAgICAgICA8SWRlbnRpZmljYXRpb24+CiAgICAgICAgICAgICAgICA8Q3JlZGVudGlhbHM+CiAgICAgICAgICAgICAgICAgICAgPERvbWFpbj5UQUNHPC9Eb21haW4+CiAgICAgICAgICAgICAgICAgICAgPFVzZXJuYW1lPmFkbWluPC9Vc2VybmFtZT4KICAgICAgICAgICAgICAgICAgICA8UGFzc3dvcmQ+IURpbW5BUFchQWIyMDI0ITEhITwvUGFzc3dvcmQ+CiAgICAgICAgICAgICAgICA8L0NyZWRlbnRpYWxzPgogICAgICAgICAgICAgICAgPEpvaW5Eb21haW4+dGhlLWF1c3RyaWFuLWNpdHJpeC1ndXkuYXQ8L0pvaW5Eb21haW4+CiAgICAgICAgICAgICAgICA8TWFjaGluZU9iamVjdE9VPk9VPUNvbXB1dGVycyxkYz10aGUsZGM9YXVzdHJpYW4sZGM9Y2l0cml4LGRjPWd1eSxkYz1hdDwvTWFjaGluZU9iamVjdE9VPgogICAgICAgICAgICAgICAgPFVuc2VjdXJlSm9pbiAvPgogICAgICAgICAgICAgICAgPFRpbWVvdXRQZXJpb2RJbk1pbnV0ZXMgLz4KICAgICAgICAgICAgICAgIDxEZWJ1Z0pvaW5Pbmx5T25UaGlzRXJyb3IgLz4KICAgICAgICAgICAgICAgIDxEZWJ1Z0pvaW4gLz4KICAgICAgICAgICAgPC9JZGVudGlmaWNhdGlvbj4KICAgICAgICA8L2NvbXBvbmVudD4KICAgIDwvc2V0dGluZ3M+CjwvdW5hdHRlbmQ+Cg==" 
							     } 
							   + guest_customization_sysprep_custom_key_values = (known after apply) 
							   + guest_os_id                                   = (known after apply) 
							   + hardware_clock_timezone                       = (known after apply) 
							   + host_reference                                = (known after apply) 
							   + hypervisor_type                               = (known after apply) 
							   + id                                               = (known after apply) 
							   + is_vcpu_hard_pinned                           = false 
							   + machine_type                                  = (known after apply) 
							   + memory_size_mib                               = 8192 
							   + metadata                                      = (known after apply) 
							   + name                                          = "TACG-NTX-DDC2" 
							   + ngt_credentials                               = (known after apply) 
							   + ngt_enabled_capability_list                   = (known after apply) 
							   + nic_list_status                               = (known after apply) 
							   + num_sockets                                   = 1 
							  + num_vcpus_per_socket                          = 2 
							   + num_vnuma_nodes                               = (known after apply) 
							   + nutanix_guest_tools                           = (known after apply) 
							   + owner_reference                               = (known after apply) 
							   + parent_reference                              = (known after apply) 
							   + power_state                                   = (known after apply) 
							   + power_state_mechanism                         = (known after apply) 
							   + project_reference                             = (known after apply) 
							   + should_fail_on_script_failure                 = (known after apply) 
							   + state                                         = (known after apply) 
							   + use_hot_add                                   = true 
							   + vga_console_enabled                           = (known after apply) 
							 
							   + categories (known after apply) 
							 
							   + disk_list { 
							       + data_source_reference  = { 
							           + "kind" = "image" 
							           + "uuid" = "57d43449-fc0b-4afc-bf49-7c05cc8f92cf" 
							         } 
							       + disk_size_bytes     = 0 
							       + disk_size_mib       = 0 
							       + uuid                = (known after apply) 
							       + volume_group_reference = (known after apply) 
							     } 
							 
							   + gpu_list (known after apply) 
							 
							   + nic_list { 
							       + is_connected                  = "true" 
							       + mac_address                   = (known after apply) 
							       + model                         = (known after apply) 
							       + network_function_chain_reference = (known after apply) 
							       + network_function_nic_type     = (known after apply) 
							       + nic_type                      = (known after apply) 
							       + num_queues                    = (known after apply) 
							       + subnet_name                   = (known after apply) 
							       + subnet_uuid                   = "5584d26a-f126-493e-84c5-9254c3d4378e" 
							       + uuid                          = (known after apply) 
							 
							       + ip_endpoint_list { 
							           + ip   = "10.10.175.2" 
							           + type = "ASSIGNED" 
							         } 
							     } 
							} 
							 
							Plan: 3 to add, 0 to change, 0 to destroy. 
							╷ 
							│ Warning: Disabled Providers: foundation, ndb. Please provide required fields in provider configuration to enable them. Refer docs. 
							│ 
							│   with provider["registry.terraform.io/nutanix/nutanix"], 
							│   on CVADOnNutanix-provider.tf line 20, in provider "nutanix": 
							│   20: provider "nutanix" { 
							│ 
							╵ 
							 
							─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 
							 
							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\_CVADOnNutanix&gt; terraform apply 
							data.template_file.AutoUnattendXMLCC1: Reading... 
							data.template_file.AutoUnattendXMLCC2: Reading... 
							data.template_file.AutoUnattendXMLAVM: Reading... 
							data.template_file.AutoUnattendXMLCC1: Read complete after 0s [id=96f5f922adde3562c5cd2a9faa2ab847b4368cca2357b6dfceddbc9d1c770310] 
							data.template_file.AutoUnattendXMLCC2: Read complete after 0s [id=c1e13a102cf9f4fc102c26ffa7c9d5ee931e5952e2ba138fc13209a50f11a18a] 
							data.template_file.AutoUnattendXMLAVM: Read complete after 0s [id=93574b4246d99671e081585c8db80c972dbb7c82bc40ea1b250b012c4495db92] 
							data.nutanix_clusters.NTX-Clusters: Reading... 
							data.nutanix_subnets.NTX-Subnets: Reading... 
							data.nutanix_subnets.NTX-Subnets: Read complete after 0s [id=terraform-20240815064404909600000001] 
							data.nutanix_clusters.NTX-Clusters: Read complete after 0s [id=terraform-20240815064405026300000002] 
							data.nutanix_cluster.NTX-Cluster: Reading... 
							data.nutanix_cluster.NTX-Cluster: Read complete after 0s [id=00061bb2-8c9f-2210-41e4-48210b5c6d75] 
							 
							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: 
							 
							# nutanix_image.CVAD2402LTSR will be created 
							  + resource "nutanix_image" "CVAD2402LTSR" { 
							   + api_version                 = (known after apply) 
							   + architecture                = (known after apply) 
							   + availability_zone_reference = (known after apply) 
							   + checksum                    = (known after apply) 
							   + cluster_name                = (known after apply) 
							   + cluster_uuid                = (known after apply) 
							   + current_cluster_reference_list = (known after apply) 
							   + description                 = "CVAD 2402 LTSR-ISO" 
							   + id                          = (known after apply) 
							   + image_type                  = (known after apply) 
							   + metadata                    = (known after apply) 
							   + name                        = "CVAD 2402 LTSR-ISO" 
							   + owner_reference             = (known after apply) 
							   + retrieval_uri_list          = (known after apply) 
							   + size_bytes                  = (known after apply) 
							   + source_path                 = "./DATA/Citrix_Virtual_Apps_and_Desktops_7_2402_LTSR.iso" 
							   + source_uri                  = (known after apply) 
							   + state                       = (known after apply) 
							   + version                     = (known after apply) 
							 
							   + categories (known after apply) 
							 
							   + cluster_references (known after apply) 
							} 
						 

						
							 
						 

						
							# nutanix_virtual_machine.AVM-VM will be created 
							  + resource "nutanix_virtual_machine" "AVM-VM" { 
							   + api_version                                   = (known after apply) 
							   + availability_zone_reference                   = (known after apply) 
							   + boot_device_disk_address                      = (known after apply) 
							   + boot_device_mac_address                       = (known after apply) 
							   + boot_device_order_list                        = [ 
							       + "DISK", 
							     ] 
							   + boot_type                                     = "LEGACY" 
							   + cloud_init_cdrom_uuid                         = (known after apply) 
							   + cluster_name                                  = (known after apply) 
							   + cluster_uuid                                  = "00061bb2-8c9f-2210-41e4-48210b5c6d75" 
							   + description                                   = "TACG-NTX Admin VM" 
							   + enable_cpu_passthrough                        = false 
							   + enable_script_exec                            = (known after apply) 
							   + guest_customization_cloud_init_custom_key_values = (known after apply) 
							   + guest_customization_cloud_init_meta_data      = (known after apply) 
							   + guest_customization_cloud_init_user_data      = (known after apply) 
							   + guest_customization_is_overridable            = (known after apply) 
							   + guest_customization_sysprep                   = { 
							       + "install_type" = "PREPARED" 
							       + "unattend_xml" = "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHVuYXR0ZW5kIHhtbG5zPSJ1cm46c2NoZW1hcy1taWNyb3NvZnQtY29tOnVuYXR0ZW5kIj4KICAgIDxzZXR0aW5ncyBwYXNzPSJvb2JlU3lzdGVtIj4KICAgICAgICA8Y29tcG9uZW50IG5hbWU9Ik1pY3Jvc29mdC1XaW5kb3dzLVNoZWxsLVNldHVwIiBwcm9jZXNzb3JBcmNoaXRlY3R1cmU9ImFtZDY0IiBwdWJsaWNLZXlUb2tlbj0iMzFiZjM4NTZhZDM2NGUzNSIgbGFuZ3VhZ2U9Im5ldXRyYWwiIHZlcnNpb25TY29wZT0ibm9uU3hTIiB4bWxuczp3Y209Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vV01JQ29uZmlnLzIwMDIvU3RhdGUiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiPgogICAgICAgICAgICA8T09CRT4KICAgICAgICAgICAgICAgIDxIaWRlRVVMQVBhZ2U+dHJ1ZTwvSGlkZUVVTEFQYWdlPgogICAgICAgICAgICAgICAgPFNraXBVc2VyT09CRT50cnVlPC9Ta2lwVXNlck9PQkU+CiAgICAgICAgICAgICAgICA8SGlkZU9FTVJlZ2lzdHJhdGlvblNjcmVlbj50cnVlPC9IaWRlT0VNUmVnaXN0cmF0aW9uU2NyZWVuPgogICAgICAgICAgICAgICAgPEhpZGVPbmxpbmVBY2NvdW50U2NyZWVucz50cnVlPC9IaWRlT25saW5lQWNjb3VudFNjcmVlbnM+CiAgICAgICAgICAgICAgICA8SGlkZVdpcmVsZXNzU2V0dXBJbk9PQkU+dHJ1ZTwvSGlkZVdpcmVsZXNzU2V0dXBJbk9PQkU+CiAgICAgICAgICAgICAgICA8TmV0d29ya0xvY2F0aW9uPldvcms8L05ldHdvcmtMb2NhdGlvbj4KICAgICAgICAgICAgICAgIDxQcm90ZWN0WW91clBDPjE8L1Byb3RlY3RZb3VyUEM+CiAgICAgICAgICAgICAgICA8SGlkZUxvY2FsQWNjb3VudFNjcmVlbj50cnVlPC9IaWRlTG9jYWxBY2NvdW50U2NyZWVuPgogICAgICAgICAgICA8L09PQkU+CiAgICAgICAgICAgIDxVc2VyQWNjb3VudHM+CiAgICAgICAgICAgICAgICA8QWRtaW5pc3RyYXRvclBhc3N3b3JkPgogICAgICAgICAgICAgICAgICAgIDxWYWx1ZT5JUUJFQUdrQWJRQnVBRUVBVUFCWEFDRUFRUUJpQURJQU1BQXlBRFFBSVFBeEFDRUFJUUJCQUdRQWJRQnBBRzRBYVFCekFIUUFjZ0JoQUhRQWJ3QnlBRkFBWVFCekFITUFkd0J2QUhJQVpBQT08L1ZhbHVlPgogICAgICAgICAgICAgICAgICAgIDxQbGFpblRleHQ+ZmFsc2U8L1BsYWluVGV4dD4KICAgICAgICAgICAgICAgIDwvQWRtaW5pc3RyYXRvclBhc3N3b3JkPgogICAgICAgICAgICA8L1VzZXJBY2NvdW50cz4KICAgICAgICAgICAgPEF1dG9Mb2dvbj4KICAgICAgICAgICAgICAgIDxQYXNzd29yZD4KICAgICAgICAgICAgICAgICAgICA8VmFsdWU+SVFCRUFHa0FiUUJ1QUVFQVVBQlhBQ0VBUVFCaUFESUFNQUF5QURRQUlRQXhBQ0VBSVFCUUFHRUFjd0J6QUhjQWJ3QnlBR1FBPC9WYWx1ZT4KICAgICAgICAgICAgICAgICAgICA8UGxhaW5UZXh0PmZhbHNlPC9QbGFpblRleHQ+CiAgICAgICAgICAgICAgICA8L1Bhc3N3b3JkPgogICAgICAgICAgICAgICAgPEVuYWJsZWQ+dHJ1ZTwvRW5hYmxlZD4KICAgICAgICAgICAgICAgIDxMb2dvbkNvdW50PjE8L0xvZ29uQ291bnQ+CiAgICAgICAgICAgICAgICA8VXNlcm5hbWU+QWRtaW5pc3RyYXRvcjwvVXNlcm5hbWU+CiAgICAgICAgICAgIDwvQXV0b0xvZ29uPgogICAgICAgICAgICA8Rmlyc3RMb2dvbkNvbW1hbmRzPgogICAgICAgICAgICAgICAgPFN5bmNocm9ub3VzQ29tbWFuZCB3Y206YWN0aW9uPSJhZGQiPgogICAgICAgICAgICAgICAgICAgIDxDb21tYW5kTGluZT5wb3dlcnNoZWxsLmV4ZSAtTm9Qcm9maWxlIC1FeGVjdXRpb25Qb2xpY3kgQnlwYXNzIC1Db21tYW5kICJJbnZva2UtRXhwcmVzc2lvbiAoSW52b2tlLVdlYlJlcXVlc3QgLVVzZUJhc2ljUGFyc2luZyAtVXJpICdodHRwczovL3dtd2Jsb2IuYmxvYi5jb3JlLndpbmRvd3MubmV0L3RmZGF0YS9FbmFibGVXaW5STUZpcnN0UnVuLnBzMScpIjwvQ29tbWFuZExpbmU+CiAgICAgICAgICAgICAgICAgICAgPERlc2NyaXB0aW9uPnBvd2Vyc2hlbGxfcnVub25jZTwvRGVzY3JpcHRpb24+CiAgICAgICAgICAgICAgICAgICAgPE9yZGVyPjE8L09yZGVyPgogICAgICAgICAgICAgICAgPC9TeW5jaHJvbm91c0NvbW1hbmQ+CiAgICAgICAgICAgIDwvRmlyc3RMb2dvbkNvbW1hbmRzPgogICAgICAgIDwvY29tcG9uZW50PgogICAgICAgIDxjb21wb25lbnQgbmFtZT0iTWljcm9zb2Z0LVdpbmRvd3MtSW50ZXJuYXRpb25hbC1Db3JlIiBwcm9jZXNzb3JBcmNoaXRlY3R1cmU9ImFtZDY0IiBwdWJsaWNLZXlUb2tlbj0iMzFiZjM4NTZhZDM2NGUzNSIgbGFuZ3VhZ2U9Im5ldXRyYWwiIHZlcnNpb25TY29wZT0ibm9uU3hTIiB4bWxuczp3Y209Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vV01JQ29uZmlnLzIwMDIvU3RhdGUiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiPgogICAgICAgICAgICA8SW5wdXRMb2NhbGU+ZGUtQVQ8L0lucHV0TG9jYWxlPgogICAgICAgICAgICA8U3lzdGVtTG9jYWxlPmRlLUFUPC9TeXN0ZW1Mb2NhbGU+CiAgICAgICAgICAgIDxVSUxhbmd1YWdlPmVuLVVTPC9VSUxhbmd1YWdlPgogICAgICAgICAgICA8VXNlckxvY2FsZT5kZS1BVDwvVXNlckxvY2FsZT4KICAgICAgICAgICAgPFVJTGFuZ3VhZ2VGYWxsYmFjaz5kZS1ERTwvVUlMYW5ndWFnZUZhbGxiYWNrPgogICAgICAgIDwvY29tcG9uZW50PgogICAgPC9zZXR0aW5ncz4KICAgIDxzZXR0aW5ncyBwYXNzPSJzcGVjaWFsaXplIj4KICAgICAgICA8Y29tcG9uZW50IG5hbWU9Ik1pY3Jvc29mdC1XaW5kb3dzLVNoZWxsLVNldHVwIiBwcm9jZXNzb3JBcmNoaXRlY3R1cmU9ImFtZDY0IiBwdWJsaWNLZXlUb2tlbj0iMzFiZjM4NTZhZDM2NGUzNSIgbGFuZ3VhZ2U9Im5ldXRyYWwiIHZlcnNpb25TY29wZT0ibm9uU3hTIiB4bWxuczp3Y209Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vV01JQ29uZmlnLzIwMDIvU3RhdGUiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiPgogICAgICAgICAgICA8UmVnaXN0ZXJlZE9yZ2FuaXphdGlvbj5UQUNHIE51dGFuaXg8L1JlZ2lzdGVyZWRPcmdhbml6YXRpb24+CiAgICAgICAgICAgIDxSZWdpc3RlcmVkT3duZXI+VEFDRyBOdXRhbml4PC9SZWdpc3RlcmVkT3duZXI+CiAgICAgICAgICAgIDxUaW1lWm9uZT5HTVQgU3RhbmRhcmQgVGltZTwvVGltZVpvbmU+CiAgICAgICAgICAgIDxQcm9kdWN0S2V5Pk5SVjRRLVQ3WTczLUhNSjZWLTI4SlA4LTI5MlhZPC9Qcm9kdWN0S2V5PgogICAgICAgICAgICA8Q29tcHV0ZXJOYW1lPlRBQ0ctTlRYLUNWQVZNPC9Db21wdXRlck5hbWU+CiAgICAgICAgPC9jb21wb25lbnQ+CiAgICAgICAgPGNvbXBvbmVudCBuYW1lPSJNaWNyb3NvZnQtV2luZG93cy1VbmF0dGVuZGVkSm9pbiIgcHJvY2Vzc29yQXJjaGl0ZWN0dXJlPSJhbWQ2NCIgcHVibGljS2V5VG9rZW49IjMxYmYzODU2YWQzNjRlMzUiIGxhbmd1YWdlPSJuZXV0cmFsIiB2ZXJzaW9uU2NvcGU9Im5vblN4UyIgeG1sbnM6d2NtPSJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL1dNSUNvbmZpZy8yMDAyL1N0YXRlIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIj4KICAgICAgICAgICAgPElkZW50aWZpY2F0aW9uPgogICAgICAgICAgICAgICAgPENyZWRlbnRpYWxzPgogICAgICAgICAgICAgICAgICAgIDxEb21haW4+VEFDRzwvRG9tYWluPgogICAgICAgICAgICAgICAgICAgIDxVc2VybmFtZT5hZG1pbjwvVXNlcm5hbWU+CiAgICAgICAgICAgICAgICAgICAgPFBhc3N3b3JkPiFEaW1uQVBXIUFiMjAyNCExISE8L1Bhc3N3b3JkPgogICAgICAgICAgICAgICAgPC9DcmVkZW50aWFscz4KICAgICAgICAgICAgICAgIDxKb2luRG9tYWluPnRoZS1hdXN0cmlhbi1jaXRyaXgtZ3V5LmF0PC9Kb2luRG9tYWluPgogICAgICAgICAgICAgICAgPE1hY2hpbmVPYmplY3RPVT5PVT1Db21wdXRlcnMsZGM9dGhlLGRjPWF1c3RyaWFuLGRjPWNpdHJpeCxkYz1ndXksZGM9YXQ8L01hY2hpbmVPYmplY3RPVT4KICAgICAgICAgICAgICAgIDxVbnNlY3VyZUpvaW4gLz4KICAgICAgICAgICAgICAgIDxUaW1lb3V0UGVyaW9kSW5NaW51dGVzIC8+CiAgICAgICAgICAgICAgICA8RGVidWdKb2luT25seU9uVGhpc0Vycm9yIC8+CiAgICAgICAgICAgICAgICA8RGVidWdKb2luIC8+CiAgICAgICAgICAgIDwvSWRlbnRpZmljYXRpb24+CiAgICAgICAgPC9jb21wb25lbnQ+CiAgICA8L3NldHRpbmdzPgo8L3VuYXR0ZW5kPgo=" 
							     } 
							   + guest_customization_sysprep_custom_key_values = (known after apply) 
							   + guest_os_id                                   = (known after apply) 
							   + hardware_clock_timezone                       = (known after apply) 
							   + host_reference                                   = (known after apply) 
							   + hypervisor_type                               = (known after apply) 
							   + id                                               = (known after apply) 
							   + is_vcpu_hard_pinned                           = false 
							   + machine_type                                  = (known after apply) 
							   + memory_size_mib                               = 8192 
							   + metadata                                      = (known after apply) 
							   + name                                          = "TACG-NTX-CVAVM" 
							   + ngt_credentials                               = (known after apply) 
							   + ngt_enabled_capability_list                   = (known after apply) 
							   + nic_list_status                               = (known after apply) 
							   + num_sockets                                   = 1 
							   + num_vcpus_per_socket                          = 2 
							   + num_vnuma_nodes                               = (known after apply) 
							   + nutanix_guest_tools                           = (known after apply) 
							   + owner_reference                               = (known after apply) 
							   + parent_reference                              = (known after apply) 
							      + power_state                                   = (known after apply) 
							   + power_state_mechanism                         = (known after apply) 
							   + project_reference                             = (known after apply) 
							   + should_fail_on_script_failure                 = (known after apply) 
							   + state                                         = (known after apply) 
							   + use_hot_add                                   = true 
							   + vga_console_enabled                           = (known after apply) 
							 
							   + categories (known after apply) 
							 
							   + disk_list { 
							       + data_source_reference  = { 
							           + "kind" = "image" 
							           + "uuid" = "57d43449-fc0b-4afc-bf49-7c05cc8f92cf" 
							         } 
							       + disk_size_bytes     = 0 
							       + disk_size_mib       = 0 
							       + uuid                = (known after apply) 
							       + volume_group_reference = (known after apply) 
							     } 
							 
							   + gpu_list (known after apply) 
							 
							   + nic_list { 
							       + is_connected                  = "true" 
							       + mac_address                   = (known after apply) 
							       + model                         = (known after apply) 
							       + network_function_chain_reference = (known after apply) 
							       + network_function_nic_type     = (known after apply) 
							       + nic_type                      = (known after apply) 
							       + num_queues                    = (known after apply) 
							       + subnet_name                   = (known after apply) 
							       + subnet_uuid                   = "5584d26a-f126-493e-84c5-9254c3d4378e" 
							       + uuid                          = (known after apply) 
							 
							       + ip_endpoint_list { 
							           + ip   = "10.10.175.3" 
							           + type = "ASSIGNED" 
							         } 
							     } 
							} 
							 
							  # nutanix_virtual_machine.CC1-VM will be created 
							  + resource "nutanix_virtual_machine" "CC1-VM" { 
							   + api_version                                   = (known after apply) 
							   + availability_zone_reference                   = (known after apply) 
							   + boot_device_disk_address                      = (known after apply) 
							   + boot_device_mac_address                       = (known after apply) 
							   + boot_device_order_list                        = [ 
							       + "DISK", 
							     ] 
							   + boot_type                                     = "LEGACY" 
							   + cloud_init_cdrom_uuid                         = (known after apply) 
							   + cluster_name                                  = (known after apply) 
							   + cluster_uuid                                  = "00061bb2-8c9f-2210-41e4-48210b5c6d75" 
							   + description                                   = "TACG-NTX-Delivery Controller 1" 
							   + enable_cpu_passthrough                        = false 
							   + enable_script_exec                            = (known after apply) 
							   + guest_customization_cloud_init_custom_key_values = (known after apply) 
							   + guest_customization_cloud_init_meta_data      = (known after apply) 
							   + guest_customization_cloud_init_user_data      = (known after apply) 
							   + guest_customization_is_overridable            = (known after apply) 
							   + guest_customization_sysprep                   = { 
							       + "install_type" = "PREPARED" 
							       + "unattend_xml" = "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHVuYXR0ZW5kIHhtbG5zPSJ1cm46c2NoZW1hcy1taWNyb3NvZnQtY29tOnVuYXR0ZW5kIj4KICAgIDxzZXR0aW5ncyBwYXNzPSJvb2JlU3lzdGVtIj4KICAgICAgICA8Y29tcG9uZW50IG5hbWU9Ik1pY3Jvc29mdC1XaW5kb3dzLVNoZWxsLVNldHVwIiBwcm9jZXNzb3JBcmNoaXRlY3R1cmU9ImFtZDY0IiBwdWJsaWNLZXlUb2tlbj0iMzFiZjM4NTZhZDM2NGUzNSIgbGFuZ3VhZ2U9Im5ldXRyYWwiIHZlcnNpb25TY29wZT0ibm9uU3hTIiB4bWxuczp3Y209Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vV01JQ29uZmlnLzIwMDIvU3RhdGUiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiPgogICAgICAgICAgICA8T09CRT4KICAgICAgICAgICAgICAgIDxIaWRlRVVMQVBhZ2U+dHJ1ZTwvSGlkZUVVTEFQYWdlPgogICAgICAgICAgICAgICAgPFNraXBVc2VyT09CRT50cnVlPC9Ta2lwVXNlck9PQkU+CiAgICAgICAgICAgICAgICA8SGlkZU9FTVJlZ2lzdHJhdGlvblNjcmVlbj50cnVlPC9IaWRlT0VNUmVnaXN0cmF0aW9uU2NyZWVuPgogICAgICAgICAgICAgICAgPEhpZGVPbmxpbmVBY2NvdW50U2NyZWVucz50cnVlPC9IaWRlT25saW5lQWNjb3VudFNjcmVlbnM+CiAgICAgICAgICAgICAgICA8SGlkZVdpcmVsZXNzU2V0dXBJbk9PQkU+dHJ1ZTwvSGlkZVdpcmVsZXNzU2V0dXBJbk9PQkU+CiAgICAgICAgICAgICAgICA8TmV0d29ya0xvY2F0aW9uPldvcms8L05ldHdvcmtMb2NhdGlvbj4KICAgICAgICAgICAgICAgIDxQcm90ZWN0WW91clBDPjE8L1Byb3RlY3RZb3VyUEM+CiAgICAgICAgICAgICAgICA8SGlkZUxvY2FsQWNjb3VudFNjcmVlbj50cnVlPC9IaWRlTG9jYWxBY2NvdW50U2NyZWVuPgogICAgICAgICAgICA8L09PQkU+CiAgICAgICAgICAgIDxVc2VyQWNjb3VudHM+CiAgICAgICAgICAgICAgICA8QWRtaW5pc3RyYXRvclBhc3N3b3JkPgogICAgICAgICAgICAgICAgICAgIDxWYWx1ZT5JUUJFQUdrQWJRQnVBRUVBVUFCWEFDRUFRUUJpQURJQU1BQXlBRFFBSVFBeEFDRUFJUUJCQUdRQWJRQnBBRzRBYVFCekFIUUFjZ0JoQUhRQWJ3QnlBRkFBWVFCekFITUFkd0J2QUhJQVpBQT08L1ZhbHVlPgogICAgICAgICAgICAgICAgICAgIDxQbGFpblRleHQ+ZmFsc2U8L1BsYWluVGV4dD4KICAgICAgICAgICAgICAgIDwvQWRtaW5pc3RyYXRvclBhc3N3b3JkPgogICAgICAgICAgICA8L1VzZXJBY2NvdW50cz4KICAgICAgICAgICAgPEF1dG9Mb2dvbj4KICAgICAgICAgICAgICAgIDxQYXNzd29yZD4KICAgICAgICAgICAgICAgICAgICA8VmFsdWU+SVFCRUFHa0FiUUJ1QUVFQVVBQlhBQ0VBUVFCaUFESUFNQUF5QURRQUlRQXhBQ0VBSVFCUUFHRUFjd0J6QUhjQWJ3QnlBR1FBPC9WYWx1ZT4KICAgICAgICAgICAgICAgICAgICA8UGxhaW5UZXh0PmZhbHNlPC9QbGFpblRleHQ+CiAgICAgICAgICAgICAgICA8L1Bhc3N3b3JkPgogICAgICAgICAgICAgICAgPEVuYWJsZWQ+dHJ1ZTwvRW5hYmxlZD4KICAgICAgICAgICAgICAgIDxMb2dvbkNvdW50PjE8L0xvZ29uQ291bnQ+CiAgICAgICAgICAgICAgICA8VXNlcm5hbWU+QWRtaW5pc3RyYXRvcjwvVXNlcm5hbWU+CiAgICAgICAgICAgIDwvQXV0b0xvZ29uPgogICAgICAgICAgICA8Rmlyc3RMb2dvbkNvbW1hbmRzPgogICAgICAgICAgICAgICAgPFN5bmNocm9ub3VzQ29tbWFuZCB3Y206YWN0aW9uPSJhZGQiPgogICAgICAgICAgICAgICAgICAgIDxDb21tYW5kTGluZT5wb3dlcnNoZWxsLmV4ZSAtTm9Qcm9maWxlIC1FeGVjdXRpb25Qb2xpY3kgQnlwYXNzIC1Db21tYW5kICJJbnZva2UtRXhwcmVzc2lvbiAoSW52b2tlLVdlYlJlcXVlc3QgLVVzZUJhc2ljUGFyc2luZyAtVXJpICdodHRwczovL3dtd2Jsb2IuYmxvYi5jb3JlLndpbmRvd3MubmV0L3RmZGF0YS9FbmFibGVXaW5STUZpcnN0UnVuLnBzMScpIjwvQ29tbWFuZExpbmU+CiAgICAgICAgICAgICAgICAgICAgPERlc2NyaXB0aW9uPnBvd2Vyc2hlbGxfcnVub25jZTwvRGVzY3JpcHRpb24+CiAgICAgICAgICAgICAgICAgICAgPE9yZGVyPjE8L09yZGVyPgogICAgICAgICAgICAgICAgPC9TeW5jaHJvbm91c0NvbW1hbmQ+CiAgICAgICAgICAgIDwvRmlyc3RMb2dvbkNvbW1hbmRzPgogICAgICAgIDwvY29tcG9uZW50PgogICAgICAgIDxjb21wb25lbnQgbmFtZT0iTWljcm9zb2Z0LVdpbmRvd3MtSW50ZXJuYXRpb25hbC1Db3JlIiBwcm9jZXNzb3JBcmNoaXRlY3R1cmU9ImFtZDY0IiBwdWJsaWNLZXlUb2tlbj0iMzFiZjM4NTZhZDM2NGUzNSIgbGFuZ3VhZ2U9Im5ldXRyYWwiIHZlcnNpb25TY29wZT0ibm9uU3hTIiB4bWxuczp3Y209Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vV01JQ29uZmlnLzIwMDIvU3RhdGUiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiPgogICAgICAgICAgICA8SW5wdXRMb2NhbGU+ZGUtQVQ8L0lucHV0TG9jYWxlPgogICAgICAgICAgICA8U3lzdGVtTG9jYWxlPmRlLUFUPC9TeXN0ZW1Mb2NhbGU+CiAgICAgICAgICAgIDxVSUxhbmd1YWdlPmVuLVVTPC9VSUxhbmd1YWdlPgogICAgICAgICAgICA8VXNlckxvY2FsZT5kZS1BVDwvVXNlckxvY2FsZT4KICAgICAgICAgICAgPFVJTGFuZ3VhZ2VGYWxsYmFjaz5kZS1ERTwvVUlMYW5ndWFnZUZhbGxiYWNrPgogICAgICAgIDwvY29tcG9uZW50PgogICAgPC9zZXR0aW5ncz4KICAgIDxzZXR0aW5ncyBwYXNzPSJzcGVjaWFsaXplIj4KICAgICAgICA8Y29tcG9uZW50IG5hbWU9Ik1pY3Jvc29mdC1XaW5kb3dzLVNoZWxsLVNldHVwIiBwcm9jZXNzb3JBcmNoaXRlY3R1cmU9ImFtZDY0IiBwdWJsaWNLZXlUb2tlbj0iMzFiZjM4NTZhZDM2NGUzNSIgbGFuZ3VhZ2U9Im5ldXRyYWwiIHZlcnNpb25TY29wZT0ibm9uU3hTIiB4bWxuczp3Y209Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vV01JQ29uZmlnLzIwMDIvU3RhdGUiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiPgogICAgICAgICAgICA8UmVnaXN0ZXJlZE9yZ2FuaXphdGlvbj5UQUNHIE51dGFuaXg8L1JlZ2lzdGVyZWRPcmdhbml6YXRpb24+CiAgICAgICAgICAgIDxSZWdpc3RlcmVkT3duZXI+VEFDRyBOdXRhbml4PC9SZWdpc3RlcmVkT3duZXI+CiAgICAgICAgICAgIDxUaW1lWm9uZT5HTVQgU3RhbmRhcmQgVGltZTwvVGltZVpvbmU+CiAgICAgICAgICAgIDxQcm9kdWN0S2V5Pk5SVjRRLVQ3WTczLUhNSjZWLTI4SlA4LTI5MlhZPC9Qcm9kdWN0S2V5PgogICAgICAgICAgICA8Q29tcHV0ZXJOYW1lPlRBQ0ctTlRYLUREQzE8L0NvbXB1dGVyTmFtZT4KICAgICAgICA8L2NvbXBvbmVudD4KICAgICAgICA8Y29tcG9uZW50IG5hbWU9Ik1pY3Jvc29mdC1XaW5kb3dzLVVuYXR0ZW5kZWRKb2luIiBwcm9jZXNzb3JBcmNoaXRlY3R1cmU9ImFtZDY0IiBwdWJsaWNLZXlUb2tlbj0iMzFiZjM4NTZhZDM2NGUzNSIgbGFuZ3VhZ2U9Im5ldXRyYWwiIHZlcnNpb25TY29wZT0ibm9uU3hTIiB4bWxuczp3Y209Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vV01JQ29uZmlnLzIwMDIvU3RhdGUiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiPgogICAgICAgICAgICA8SWRlbnRpZmljYXRpb24+CiAgICAgICAgICAgICAgICA8Q3JlZGVudGlhbHM+CiAgICAgICAgICAgICAgICAgICAgPERvbWFpbj5UQUNHPC9Eb21haW4+CiAgICAgICAgICAgICAgICAgICAgPFVzZXJuYW1lPmFkbWluPC9Vc2VybmFtZT4KICAgICAgICAgICAgICAgICAgICA8UGFzc3dvcmQ+IURpbW5BUFchQWIyMDI0ITEhITwvUGFzc3dvcmQ+CiAgICAgICAgICAgICAgICA8L0NyZWRlbnRpYWxzPgogICAgICAgICAgICAgICAgPEpvaW5Eb21haW4+dGhlLWF1c3RyaWFuLWNpdHJpeC1ndXkuYXQ8L0pvaW5Eb21haW4+CiAgICAgICAgICAgICAgICA8TWFjaGluZU9iamVjdE9VPk9VPUNvbXB1dGVycyxkYz10aGUsZGM9YXVzdHJpYW4sZGM9Y2l0cml4LGRjPWd1eSxkYz1hdDwvTWFjaGluZU9iamVjdE9VPgogICAgICAgICAgICAgICAgPFVuc2VjdXJlSm9pbiAvPgogICAgICAgICAgICAgICAgPFRpbWVvdXRQZXJpb2RJbk1pbnV0ZXMgLz4KICAgICAgICAgICAgICAgIDxEZWJ1Z0pvaW5Pbmx5T25UaGlzRXJyb3IgLz4KICAgICAgICAgICAgICAgIDxEZWJ1Z0pvaW4gLz4KICAgICAgICAgICAgPC9JZGVudGlmaWNhdGlvbj4KICAgICAgICA8L2NvbXBvbmVudD4KICAgIDwvc2V0dGluZ3M+CjwvdW5hdHRlbmQ+Cg==" 
							     } 
							   + guest_customization_sysprep_custom_key_values = (known after apply) 
							   + guest_os_id                                   = (known after apply) 
							   + hardware_clock_timezone                       = (known after apply) 
							   + host_reference                                = (known after apply) 
							   + hypervisor_type                               = (known after apply) 
							   + id                                               = (known after apply) 
							   + is_vcpu_hard_pinned                           = false 
							   + machine_type                                  = (known after apply) 
							   + memory_size_mib                               = 192 
							   + metadata                                      = (known after apply) 
							   + name                                          = "TACG-NTX-DDC1" 
							   + ngt_credentials                               = (known after apply) 
							      + ngt_enabled_capability_list                   = (known after apply) 
							   + nic_list_status                               = (known after apply) 
							   + num_sockets                                   = 1 
							   + num_vcpus_per_socket                          = 2 
							   + num_vnuma_nodes                               = (known after apply) 
							   + nutanix_guest_tools                           = (known after apply) 
							   + owner_reference                               = (known after apply) 
							   + parent_reference                              = (known after apply) 
							   + power_state                                   = (known after apply) 
							   + power_state_mechanism                         = (known after apply) 
							   + project_reference                             = (known after apply) 
							   + should_fail_on_script_failure                 = (known after apply) 
							   + state                                         = (known after apply) 
							   + use_hot_add                                   = true 
							   + vga_console_enabled                           = (known after apply) 
							 
							   + categories (known after apply) 
							 
							   + disk_list { 
							       + data_source_reference  = { 
							           + "kind" = "image" 
							           + "uuid" = "57d43449-fc0b-4afc-bf49-7c05cc8f92cf" 
							         } 
							       + disk_size_bytes     = 0 
							       + disk_size_mib       = 0 
							       + uuid                = (known after apply) 
							       + volume_group_reference = (known after apply) 
							     } 
							 
							   + gpu_list (known after apply) 
							 
							   + nic_list { 
							       + is_connected                  = "true" 
							       + mac_address                   = (known after apply) 
							       + model                         = (known after apply) 
							       + network_function_chain_reference = (known after apply) 
							       + network_function_nic_type     = (known after apply) 
							       + nic_type                      = (known after apply) 
							       + num_queues                    = (known after apply) 
							       + subnet_name                   = (known after apply) 
							       + subnet_uuid                   = "5584d26a-f126-493e-84c5-9254c3d4378e" 
							       + uuid                          = (known after apply) 
							 
							       + ip_endpoint_list { 
							           + ip   = "10.10.175.1" 
							           + type = "ASSIGNED" 
							         } 
							     } 
							} 
							 
							  # nutanix_virtual_machine.CC2-VM will be created 
							  + resource "nutanix_virtual_machine" "CC2-VM" { 
							   + api_version                                   = (known after apply) 
							   + availability_zone_reference                   = (known after apply) 
							   + boot_device_disk_address                      = (known after apply) 
							   + boot_device_mac_address                       = (known after apply) 
							   + boot_device_order_list                        = [ 
							       + "DISK", 
							     ] 
							   + boot_type                                     = "LEGACY" 
							   + cloud_init_cdrom_uuid                         = (known after apply) 
							   + cluster_name                                  = (known after apply) 
							   + cluster_uuid                                  = "00061bb2-8c9f-2210-41e4-48210b5c6d75" 
							   + description                                   = "TACG-NTX-Delivery Controller 2" 
							   + enable_cpu_passthrough                        = false 
							   + enable_script_exec                            = (known after apply) 
							   + guest_customization_cloud_init_custom_key_values = (known after apply) 
							   + guest_customization_cloud_init_meta_data      = (known after apply) 
							   + guest_customization_cloud_init_user_data      = (known after apply) 
							   + guest_customization_is_overridable            = (known after apply) 
							   + guest_customization_sysprep                   = { 
							       + "install_type" = "PREPARED" 
							       + "unattend_xml" = "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHVuYXR0ZW5kIHhtbG5zPSJ1cm46c2NoZW1hcy1taWNyb3NvZnQtY29tOnVuYXR0ZW5kIj4KICAgIDxzZXR0aW5ncyBwYXNzPSJvb2JlU3lzdGVtIj4KICAgICAgICA8Y29tcG9uZW50IG5hbWU9Ik1pY3Jvc29mdC1XaW5kb3dzLVNoZWxsLVNldHVwIiBwcm9jZXNzb3JBcmNoaXRlY3R1cmU9ImFtZDY0IiBwdWJsaWNLZXlUb2tlbj0iMzFiZjM4NTZhZDM2NGUzNSIgbGFuZ3VhZ2U9Im5ldXRyYWwiIHZlcnNpb25TY29wZT0ibm9uU3hTIiB4bWxuczp3Y209Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vV01JQ29uZmlnLzIwMDIvU3RhdGUiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiPgogICAgICAgICAgICA8T09CRT4KICAgICAgICAgICAgICAgIDxIaWRlRVVMQVBhZ2U+dHJ1ZTwvSGlkZUVVTEFQYWdlPgogICAgICAgICAgICAgICAgPFNraXBVc2VyT09CRT50cnVlPC9Ta2lwVXNlck9PQkU+CiAgICAgICAgICAgICAgICA8SGlkZU9FTVJlZ2lzdHJhdGlvblNjcmVlbj50cnVlPC9IaWRlT0VNUmVnaXN0cmF0aW9uU2NyZWVuPgogICAgICAgICAgICAgICAgPEhpZGVPbmxpbmVBY2NvdW50U2NyZWVucz50cnVlPC9IaWRlT25saW5lQWNjb3VudFNjcmVlbnM+CiAgICAgICAgICAgICAgICA8SGlkZVdpcmVsZXNzU2V0dXBJbk9PQkU+dHJ1ZTwvSGlkZVdpcmVsZXNzU2V0dXBJbk9PQkU+CiAgICAgICAgICAgICAgICA8TmV0d29ya0xvY2F0aW9uPldvcms8L05ldHdvcmtMb2NhdGlvbj4KICAgICAgICAgICAgICAgIDxQcm90ZWN0WW91clBDPjE8L1Byb3RlY3RZb3VyUEM+CiAgICAgICAgICAgICAgICA8SGlkZUxvY2FsQWNjb3VudFNjcmVlbj50cnVlPC9IaWRlTG9jYWxBY2NvdW50U2NyZWVuPgogICAgICAgICAgICA8L09PQkU+CiAgICAgICAgICAgIDxVc2VyQWNjb3VudHM+CiAgICAgICAgICAgICAgICA8QWRtaW5pc3RyYXRvclBhc3N3b3JkPgogICAgICAgICAgICAgICAgICAgIDxWYWx1ZT5JUUJFQUdrQWJRQnVBRUVBVUFCWEFDRUFRUUJpQURJQU1BQXlBRFFBSVFBeEFDRUFJUUJCQUdRQWJRQnBBRzRBYVFCekFIUUFjZ0JoQUhRQWJ3QnlBRkFBWVFCekFITUFkd0J2QUhJQVpBQT08L1ZhbHVlPgogICAgICAgICAgICAgICAgICAgIDxQbGFpblRleHQ+ZmFsc2U8L1BsYWluVGV4dD4KICAgICAgICAgICAgICAgIDwvQWRtaW5pc3RyYXRvclBhc3N3b3JkPgogICAgICAgICAgICA8L1VzZXJBY2NvdW50cz4KICAgICAgICAgICAgPEF1dG9Mb2dvbj4KICAgICAgICAgICAgICAgIDxQYXNzd29yZD4KICAgICAgICAgICAgICAgICAgICA8VmFsdWU+SVFCRUFHa0FiUUJ1QUVFQVVBQlhBQ0VBUVFCaUFESUFNQUF5QURRQUlRQXhBQ0VBSVFCUUFHRUFjd0J6QUhjQWJ3QnlBR1FBPC9WYWx1ZT4KICAgICAgICAgICAgICAgICAgICA8UGxhaW5UZXh0PmZhbHNlPC9QbGFpblRleHQ+CiAgICAgICAgICAgICAgICA8L1Bhc3N3b3JkPgogICAgICAgICAgICAgICAgPEVuYWJsZWQ+dHJ1ZTwvRW5hYmxlZD4KICAgICAgICAgICAgICAgIDxMb2dvbkNvdW50PjE8L0xvZ29uQ291bnQ+CiAgICAgICAgICAgICAgICA8VXNlcm5hbWU+QWRtaW5pc3RyYXRvcjwvVXNlcm5hbWU+CiAgICAgICAgICAgIDwvQXV0b0xvZ29uPgogICAgICAgICAgICA8Rmlyc3RMb2dvbkNvbW1hbmRzPgogICAgICAgICAgICAgICAgPFN5bmNocm9ub3VzQ29tbWFuZCB3Y206YWN0aW9uPSJhZGQiPgogICAgICAgICAgICAgICAgICAgIDxDb21tYW5kTGluZT5wb3dlcnNoZWxsLmV4ZSAtTm9Qcm9maWxlIC1FeGVjdXRpb25Qb2xpY3kgQnlwYXNzIC1Db21tYW5kICJJbnZva2UtRXhwcmVzc2lvbiAoSW52b2tlLVdlYlJlcXVlc3QgLVVzZUJhc2ljUGFyc2luZyAtVXJpICdodHRwczovL3dtd2Jsb2IuYmxvYi5jb3JlLndpbmRvd3MubmV0L3RmZGF0YS9FbmFibGVXaW5STUZpcnN0UnVuLnBzMScpIjwvQ29tbWFuZExpbmU+CiAgICAgICAgICAgICAgICAgICAgPERlc2NyaXB0aW9uPnBvd2Vyc2hlbGxfcnVub25jZTwvRGVzY3JpcHRpb24+CiAgICAgICAgICAgICAgICAgICAgPE9yZGVyPjE8L09yZGVyPgogICAgICAgICAgICAgICAgPC9TeW5jaHJvbm91c0NvbW1hbmQ+CiAgICAgICAgICAgIDwvRmlyc3RMb2dvbkNvbW1hbmRzPgogICAgICAgIDwvY29tcG9uZW50PgogICAgICAgIDxjb21wb25lbnQgbmFtZT0iTWljcm9zb2Z0LVdpbmRvd3MtSW50ZXJuYXRpb25hbC1Db3JlIiBwcm9jZXNzb3JBcmNoaXRlY3R1cmU9ImFtZDY0IiBwdWJsaWNLZXlUb2tlbj0iMzFiZjM4NTZhZDM2NGUzNSIgbGFuZ3VhZ2U9Im5ldXRyYWwiIHZlcnNpb25TY29wZT0ibm9uU3hTIiB4bWxuczp3Y209Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vV01JQ29uZmlnLzIwMDIvU3RhdGUiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiPgogICAgICAgICAgICA8SW5wdXRMb2NhbGU+ZGUtQVQ8L0lucHV0TG9jYWxlPgogICAgICAgICAgICA8U3lzdGVtTG9jYWxlPmRlLUFUPC9TeXN0ZW1Mb2NhbGU+CiAgICAgICAgICAgIDxVSUxhbmd1YWdlPmVuLVVTPC9VSUxhbmd1YWdlPgogICAgICAgICAgICA8VXNlckxvY2FsZT5kZS1BVDwvVXNlckxvY2FsZT4KICAgICAgICAgICAgPFVJTGFuZ3VhZ2VGYWxsYmFjaz5kZS1ERTwvVUlMYW5ndWFnZUZhbGxiYWNrPgogICAgICAgIDwvY29tcG9uZW50PgogICAgPC9zZXR0aW5ncz4KICAgIDxzZXR0aW5ncyBwYXNzPSJzcGVjaWFsaXplIj4KICAgICAgICA8Y29tcG9uZW50IG5hbWU9Ik1pY3Jvc29mdC1XaW5kb3dzLVNoZWxsLVNldHVwIiBwcm9jZXNzb3JBcmNoaXRlY3R1cmU9ImFtZDY0IiBwdWJsaWNLZXlUb2tlbj0iMzFiZjM4NTZhZDM2NGUzNSIgbGFuZ3VhZ2U9Im5ldXRyYWwiIHZlcnNpb25TY29wZT0ibm9uU3hTIiB4bWxuczp3Y209Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vV01JQ29uZmlnLzIwMDIvU3RhdGUiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiPgogICAgICAgICAgICA8UmVnaXN0ZXJlZE9yZ2FuaXphdGlvbj5UQUNHIE51dGFuaXg8L1JlZ2lzdGVyZWRPcmdhbml6YXRpb24+CiAgICAgICAgICAgIDxSZWdpc3RlcmVkT3duZXI+VEFDRyBOdXRhbml4PC9SZWdpc3RlcmVkT3duZXI+CiAgICAgICAgICAgIDxUaW1lWm9uZT5HTVQgU3RhbmRhcmQgVGltZTwvVGltZVpvbmU+CiAgICAgICAgICAgIDxQcm9kdWN0S2V5Pk5SVjRRLVQ3WTczLUhNSjZWLTI4SlA4LTI5MlhZPC9Qcm9kdWN0S2V5PgogICAgICAgICAgICA8Q29tcHV0ZXJOYW1lPlRBQ0ctTlRYLUREQzI8L0NvbXB1dGVyTmFtZT4KICAgICAgICA8L2NvbXBvbmVudD4KICAgICAgICA8Y29tcG9uZW50IG5hbWU9Ik1pY3Jvc29mdC1XaW5kb3dzLVVuYXR0ZW5kZWRKb2luIiBwcm9jZXNzb3JBcmNoaXRlY3R1cmU9ImFtZDY0IiBwdWJsaWNLZXlUb2tlbj0iMzFiZjM4NTZhZDM2NGUzNSIgbGFuZ3VhZ2U9Im5ldXRyYWwiIHZlcnNpb25TY29wZT0ibm9uU3hTIiB4bWxuczp3Y209Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vV01JQ29uZmlnLzIwMDIvU3RhdGUiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiPgogICAgICAgICAgICA8SWRlbnRpZmljYXRpb24+CiAgICAgICAgICAgICAgICA8Q3JlZGVudGlhbHM+CiAgICAgICAgICAgICAgICAgICAgPERvbWFpbj5UQUNHPC9Eb21haW4+CiAgICAgICAgICAgICAgICAgICAgPFVzZXJuYW1lPmFkbWluPC9Vc2VybmFtZT4KICAgICAgICAgICAgICAgICAgICA8UGFzc3dvcmQ+IURpbW5BUFchQWIyMDI0ITEhITwvUGFzc3dvcmQ+CiAgICAgICAgICAgICAgICA8L0NyZWRlbnRpYWxzPgogICAgICAgICAgICAgICAgPEpvaW5Eb21haW4+dGhlLWF1c3RyaWFuLWNpdHJpeC1ndXkuYXQ8L0pvaW5Eb21haW4+CiAgICAgICAgICAgICAgICA8TWFjaGluZU9iamVjdE9VPk9VPUNvbXB1dGVycyxkYz10aGUsZGM9YXVzdHJpYW4sZGM9Y2l0cml4LGRjPWd1eSxkYz1hdDwvTWFjaGluZU9iamVjdE9VPgogICAgICAgICAgICAgICAgPFVuc2VjdXJlSm9pbiAvPgogICAgICAgICAgICAgICAgPFRpbWVvdXRQZXJpb2RJbk1pbnV0ZXMgLz4KICAgICAgICAgICAgICAgIDxEZWJ1Z0pvaW5Pbmx5T25UaGlzRXJyb3IgLz4KICAgICAgICAgICAgICAgIDxEZWJ1Z0pvaW4gLz4KICAgICAgICAgICAgPC9JZGVudGlmaWNhdGlvbj4KICAgICAgICA8L2NvbXBvbmVudD4KICAgIDwvc2V0dGluZ3M+CjwvdW5hdHRlbmQ+Cg==" 
							     } 
							   + guest_customization_sysprep_custom_key_values = (known after apply) 
							   + guest_os_id                                   = (known after apply) 
							   + hardware_clock_timezone                       = (known after apply) 
							   + host_reference                                = (known after apply) 
							   + hypervisor_type                               = (known after apply) 
							   + id                                               = (known after apply) 
							   + is_vcpu_hard_pinned                           = false 
							   + machine_type                                  = (known after apply) 
							   + memory_size_mib                               = 8192 
							   + metadata                                      = (known after apply) 
							   + name                                          = "TACG-NTX-DDC2" 
							   + ngt_credentials                               = (known after apply) 
							   + ngt_enabled_capability_list                   = (known after apply) 
							   + nic_list_status                               = (known after apply) 
							   + num_sockets                                   = 1 
							   + num_vcpus_per_socket                          = 2 
							   + num_vnuma_nodes                               = (known after apply) 
							   + nutanix_guest_tools                           = (known after apply) 
							   + owner_reference                               = (known after apply) 
							   + parent_reference                              = (known after apply) 
							   + power_state                                   = (known after apply) 
							   + power_state_mechanism                         = (known after apply) 
							   + project_reference                             = (known after apply) 
							   + should_fail_on_script_failure                 = (known after apply) 
							   + state                                         = (known after apply) 
							   + use_hot_add                                   = true 
							   + vga_console_enabled                           = (known after apply) 
							 
							   + categories (known after apply) 
							 
							   + disk_list { 
							       + data_source_reference  = { 
							           + "kind" = "image" 
							           + "uuid" = "57d43449-fc0b-4afc-bf49-7c05cc8f92cf" 
							         } 
							       + disk_size_bytes     = 0 
							       + disk_size_mib       = 0 
							       + uuid                = (known after apply) 
							       + volume_group_reference = (known after apply) 
							     } 
							 
							   + gpu_list (known after apply) 
							 
							   + nic_list { 
							       + is_connected                  = "true" 
							       + mac_address                   = (known after apply) 
							       + model                         = (known after apply) 
							       + network_function_chain_reference = (known after apply) 
							       + network_function_nic_type     = (known after apply) 
							       + nic_type                      = (known after apply) 
							       + num_queues                    = (known after apply) 
							       + subnet_name                   = (known after apply) 
							       + subnet_uuid                   = "5584d26a-f126-493e-84c5-9254c3d4378e" 
							       + uuid                          = (known after apply) 
							 
							       + ip_endpoint_list { 
							           + ip   = "10.10.175.2" 
							           + type = "ASSIGNED" 
							         } 
							     } 
							} 
							 
							Plan: 4 to add, 0 to change, 0 to destroy. 
							╷ 
							│ Warning: Disabled Providers: foundation, ndb. Please provide required fields in provider configuration to enable them. Refer docs. 
							│ 
							│   with provider["registry.terraform.io/nutanix/nutanix"], 
							│   on CVADOnNutanix-provider.tf line 20, in provider "nutanix": 
							│   20: provider "nutanix" { 
							│ 
							╵ 
							 
							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 
							 
							nutanix_virtual_machine.CC1-VM: Creating... 
							nutanix_virtual_machine.AVM-VM: Creating... 
							nutanix_virtual_machine.CC2-VM: Creating... 
							nutanix_virtual_machine.CC1-VM: Creation complete after 7s [id=299e79f9-a01a-46a6-9319-1c29e6b6eaf9] 
							nutanix_virtual_machine.CC2-VM: Creation complete after 7s [id=0d127ec7-0272-4a02-affe-12d7b7ca737d] 
							nutanix_virtual_machine.AVM-VM: Still creating... [10s elapsed] 
							nutanix_virtual_machine.AVM-VM: Creation complete after 10s [id=8316bc4a-d042-44a9-bccf-07f1c9bc5565]
						 

						
							nutanix_image.CVAD2402LTSR: Creating... 
							nutanix_image.CVAD2402LTSR: Still creating... [10s elapsed] 
							nutanix_image.CVAD2402LTSR: Still creating... [20s elapsed] 
							nutanix_image.CVAD2402LTSR: Still creating... [30s elapsed] 
							nutanix_image.CVAD2402LTSR: Still creating... [40s elapsed] 
							nutanix_image.CVAD2402LTSR: Still creating... [50s elapsed] 
							nutanix_image.CVAD2402LTSR: Still creating... [1m0s elapsed] 
							nutanix_image.CVAD2402LTSR: Still creating... [1m10s elapsed] 
							nutanix_image.CVAD2402LTSR: Still creating... [1m20s elapsed] 
							nutanix_image.CVAD2402LTSR: Still creating... [1m30s elapsed] 
							nutanix_image.CVAD2402LTSR: Still creating... [1m40s elapsed] 
							nutanix_image.CVAD2402LTSR: Still creating... [1m50s elapsed] 
							nutanix_image.CVAD2402LTSR: Still creating... [2m0s elapsed] 
							nutanix_image.CVAD2402LTSR: Creation complete after 2m8s [id=bbce68d4-175e-4703-a814-68580d0a2703] 
							╷ 
							│ Warning: Disabled Providers: foundation, ndb. Please provide required fields in provider configuration to enable them. Refer docs. 
							│ 
							│   with provider["registry.terraform.io/nutanix/nutanix"], 
							│   on CVADOnNutanix-provider.tf line 20, in provider "nutanix": 
							│   20: provider "nutanix" { 
							│ 
							╵ 
							 
							Apply complete. Resources: 4 added, 0 changed, 0 destroyed. 
							PS C:\_TACG\_CVADOnNutanix&gt;
						 
					
				
			
		
	

	
		The Terraform flow was completed successfully, and the next module can begin.
	 

	
		Module 2: Install and Configure all Resources needed for the Resource Location on Nutanix AHV
	

	
		Important:  
		 
		This module can only be run on the previously created Administrative VM, as the following modules rely heavily on WinRM.  
		Please install Terraform on the Administrative VM before trying to run the scripts. 
		 
	 

	
		This module is split into the following configuration parts:
	 

	
		
			Configuring the three previously created Virtual Machines on Nutanix AHV:

			
				
					Installing the needed software on the Delivery Controller VMs
				
				
					Installing the needed software on the Admin-VM 
					 
				
			
		
		
			Creating the necessary Resources for the Citrix Virtual Apps and Desktops site:
			
				
					Creating the needed databases
				
				
					Creating the Citrix Virtual Apps and Desktops site
				
				
					Initial configuration of the Citrix Virtual Apps and Desktops site
				
				
					Installing Citrix StoreFront on the Delivery Controllers
				
			
		
	

	
		Note:  
		 
		This module is complex as many automated actions and different entities are created and run on the VMs. It contains a mixture of PowerShell scripts, REST-API calls, and Terraform scripts to achieve a working deployment. 
		 
	 

	
		Please ensure you have configured the variables according to your needs using the corresponding .auto.tfvars.json file.
	 

	
		Terraform creates various scripts before creating the CCs' configuration to set needed information, such as the script for Database creation, partial StoreFront configuration, and more. 
		 
		Examples of necessary scripts: 
		Terraform needs to add a DNS record for the StoreFront-Base-FQDN:
	 

	
		
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #1e1e1e;
    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 #1e1e1e;
    background-color: #1e1e1e;
    padding: 10px;
}

.table_component td {
    border: 1px solid #1e1e1e;
    background-color: #1e1e1e;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}		
		
			
				
					
						
							 
							"CC_Install_AHVPluginURI":"https://download.nutanix.com/citrixAhv/2.7.2.0/NutanixAHV_Citrix_plugin.msi 
							 
						
					
				
			
		
	

	
		 
	 

	
		Important: 
		 
		The Terraform script will pause for some time after creating the entities.  
		This is because of time constraints in the back-end.  
		 
		Installation and Database creation take some time, depending on the infrastructure.  
		We have seen delays of up to 10 minutes before the Databases were created.  
		If the script proceeds too fast, the deployment will fail, and the Terraform script will not be able to continue successfully. 
		That´s why you might think the built-in script pauses are too long. 
		 
	 

	
		 
		No Citrix Virtual Apps and Desktop Software is installed on the Delivery Controller VMs before running Module 2: 
		
	 

	
		 
		No Site-related Databases exist on the SQL Server: 
		
	 

	
		 
	 

	
		Important: 
		 
		Before starting the Terraform workflow, please ensure you have configured the variables according to your needs. 
		 
	 

	
		The configuration can be started by following the standard Terraform workflow:
	 

	
		terraform init, 
		terraform plan 
		 
	 

	
		and if no errors occur
	 

	
		terraform apply
	 

	
		 
	 

	
		
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0200b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}		
	 
	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0200b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\_TACG\_CVADOnNutanix\_CVADOnNutanix-Configuration&gt; terraform init 
						Initializing the backend... 
						Initializing provider plugins... 
						- Finding latest version of hashicorp/null... 
						- Finding latest version of hashicorp/time... 
						- Finding citrix/citrix versions matching "1.0.0"... 
						- Finding nutanix/nutanix versions matching "1.9.5"... 
						- Finding latest version of hashicorp/local... 
						- Installing hashicorp/null v3.2.2... 
						- Installed hashicorp/null v3.2.2 (signed by HashiCorp) 
						- Installing hashicorp/time v0.12.0... 
						- Installed hashicorp/time v0.12.0 (signed by HashiCorp) 
						- Installing citrix/citrix v1.0.0... 
						- Installed citrix/citrix v1.0.0 (self-signed, key ID BD4BD0E690CB7D88) 
						- Installing nutanix/nutanix v1.9.5... 
						- Installed nutanix/nutanix v1.9.5 (signed by a HashiCorp partner, key ID BEA5F795571AD06E) 
						- 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\_CVADOnNutanix\_CVADOnNutanix-Configuration&gt; terraform plan 
						 
						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.StoreFrontConfigurationScriptIntoDataDirectory will be created 
						  + resource "local_file" "StoreFrontConfigurationScriptIntoDataDirectory" { 
						   + content           = &lt;&lt;-EOT 
						         #$PSUsername = 'XXXXXXXXXXXXXXXX' 
						         #$PSPassword = '.XXXXXXXXXXXXXXXX..' 
						         #$PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force 
						         #$PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword) 
						         #Invoke-Command -ComputerName localhost -Credential $PSCredential -ScriptBlock { 
						         Add-PSSnapin Citrix.* 
						 
						         #Clear-Existing StoreFront deployments 
						         Clear-STFDeployment -Confirm:$false 
						         Write-Output 'Existing Deployments deleted.' 
						 
						         #Add new StoreFront deployment 
						         Add-STFDeployment -HostBaseUrl 'https://storefront.the-austrian-citrix-guy.at' -Confirm:$false 
						         Write-Output 'New deployment created.' 
						 
						         #Add new Authentication Service 
						         Add-STFAuthenticationService '/Citrix/Authentication' 
						         $authentication = Get-STFAuthenticationService 
						         Write-Output 'Authentication service created.' 
						 
						         Start-Sleep -Seconds 120 
						 
						         #Enable Access through a NetScaler Gateway 
						            Enable-STFAuthenticationServiceProtocol -Name CitrixAGBasic -AuthenticationService ($authentication) 
						         $protocols = Get-STFAuthenticationServiceProtocol -AuthenticationService ($authentication) 
						 
						         #Add NetScaler Gateway 
						         Add-STFRoamingGateway -Name TACG-TF-NSGW -LogonType Domain -GatewayUrl 'https://ns.the-austrian-citrix-guy.at' -CallbackUrl 'https://ns.the-austrian-citrix-guy.at/CitrixAuthService/AuthService.asmx' -SessionReliability:$true -SecureTicketAuthorityUrls 'http://TACG-NTX-DDC1.the-austrian-citrix-guy.at/scripts/ctxsta.dll,http://TACG-NTX-DDC2.the-austrian-citrix-guy.at/scripts/ctxsta.dll' -IsCloudGateway:$false 
						         Write-Output 'NSGW created.' 
						         #Add Beacons for NetScaler Gateway 
						         Set-STFRoamingBeacon -Internal 'http://TACG-NTX-DDC1.the-austrian-citrix-guy.at' -External https://ns.the-austrian-citrix-guy.at,https://news.orf.at 
						         Write-Output 'Beacons created.' 
						 
						         #Add new Store Service 
						         Add-STFStoreService -VirtualPath '/Citrix/Store' -AuthenticationService $authentication -FarmName 'TACG-SF' -FarmType 'XenDesktop' -Servers 'TACG-NTX-DDC1.the-austrian-citrix-guy.at' -FriendlyName 'TACG-SF' -TransportType HTTPS -Port 443 
						         Write-Output 'Store service created.' 
						 
						         #Register NetScaler Gateway and Beacons 
						         $store = Get-STFStoreService -VirtualPath '/Citrix/Store' 
						         $gateway = Get-STFRoamingGateway -Name TACG-TF-NSGW 
						         Register-STFStoreGateway -Gateway $gateway -StoreService $store -DefaultGateway -UseFullVpn:$false 
						         Write-Output 'NSGW registered.' 
						 
						         #Enable WebReceiver 
						         Add-STFWebReceiverService -VirtualPath '/Citrix/StoreWeb' -StoreService $store 
						         $receiver = Get-STFWebReceiverService -VirtualPath '/Citrix/StoreWeb' 
						         Write-Output 'Web Receiver site created.' 
						 
						         #Enable PNAGent site 
						         Enable-STFStorePna -StoreService $store -AllowUserPasswordChange -DefaultPnaService 
						         Write-Output 'PNA site created.' 
						 
						         #Enable HTML5Fallback 
						         Set-STFWebReceiverPluginAssistant -WebReceiverService $receiver -Enabled $true -Html5Enabled "Fallback" 
						         Write-Output 'HTML5 site created.' 
						         Write-Output 'Storefront configuration complete.' 
						         Write-Output 'Registering Storefront in CVAD Site configuration.' 
						 
						         $PSUsername = 'XXXXXXXXXXXXXXXX' 
						         $PSPassword = '.XXXXXXXXXXXXXXXX..' 
						         $PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force 
						         $PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword) 
						         Invoke-Command -ComputerName localhost -Credential $PSCredential -ScriptBlock { 
						         Add-PSSnapin Citrix.* 
						 
						         #Add StoreFront configuration to CVAD site 
						         $configurationSlot = Get-BrokerConfigurationSlot -Name "RS" 
						         $storefrontUrl = 'https://storefront.the-austrian-citrix-guy.at/Citrix/StoreWeb' 
						         $configuration = New-BrokerStorefrontAddress -Description "Citrix Storefront Address" -Enabled $true -Name "Citrix StoreFront" -Url $storefrontUrl 
						         New-BrokerMachineConfiguration -Policy $configuration -ConfigurationSlotUid $configurationSlot.Uid -Description "Citrix Storefront Address Configuration" -LeafName 'TACG-SF' 
						         Write-Output 'Registration in CVAD Site complete.' 
						         Write-Output 'Reboot needed...' 
						         } 
						     EOT 
						   + 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          = "./data/SF-Config.ps1" 
						   + id                = (known after apply) 
						} 
						 
						  # local_file.WriteCVADDBScriptIntoDataDirectory will be created 
						  + resource "local_file" "WriteCVADDBScriptIntoDataDirectory" { 
						   + 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          = "./data/CVAD-DBs.ps1" 
						   + id                = (known after apply) 
						} 
						 
						  # local_file.WriteCVADScriptIntoDataDirectory will be created 
						  + resource "local_file" "WriteCVADScriptIntoDataDirectory" { 
						   + content           = &lt;&lt;-EOT 
						         New-Item -Path 'C:/TEMP/XDINST' -ItemType Directory 
						         E:/x64/"XenDesktop Setup"/XenDesktopServerSetup.exe /components controller,webstudio,licenseserver,desktopdirector /noreboot /configure_firewall /quiet /nosql /Logpath C:/TEMP/XDINST 
						         #Restart-Computer -Force 
						     EOT 
						   + 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          = "./data/CVAD-Install.ps1" 
						   + id                = (known after apply) 
						} 
						 
						  # local_file.WriteDDC1SFTaskRunScriptIntoDataDirectory will be created 
						  + resource "local_file" "WriteDDC1SFTaskRunScriptIntoDataDirectory" { 
						   + content           = &lt;&lt;-EOT 
						         Add-PSSnapin Citrix.Storefront.* 
						         Start-Service -Name CitrixClusterService 
						         $JoinObject = Start-STFServerGroupJoin -IsAuthorizingServer -Confirm:$false 
						         $JoinPassCode = ($JoinObject | Select -ExpandProperty 'Passcode') 
						         $JoinPassCode | Out-File -Force -FilePath 'C:/TEMP/XDINST/pc.txt' 
						         Copy-Item -Path C:/TEMP/XDINST/pc.txt -Destination \\\\10.10.170.12\c$\temp\XDINST\pc.txt 
						         Write-Output 'Replication enabled.' 
						           } 
						     EOT 
						   + 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          = "./data/SF-CMD-EnableReplication.ps1" 
						   + id                = (known after apply) 
						} 
						 
						  # local_file.WriteDDC1SFTaskScriptIntoDataDirectory will be created 
						  + resource "local_file" "WriteDDC1SFTaskScriptIntoDataDirectory" { 
						   + 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          = "./data/SF-Task-StartReplication-DDC1.ps1" 
						   + id                = (known after apply) 
						} 
						 
						  # local_file.WriteDDC2SFTaskRunScriptIntoDataDirectory will be created 
						  + resource "local_file" "WriteDDC2SFTaskRunScriptIntoDataDirectory" { 
						   + content           = &lt;&lt;-EOT 
						         Add-PSSnapin Citrix.Storefront.* 
						         Start-Service -Name CitrixClusterService 
						         $Passcode = Get-Content -Path 'C:/TEMP/XDINST/pc.txt' 
						         Start-STFServerGroupJoin -AuthorizerHostName TACG-NTX-DDC1.the-austrian-citrix-guy.at -Passcode $Passcode -Confirm:$false 
						         Wait-STFServerGroupJoin -WaitTimeout 20 
						         Write-Output 'Replication complete.' 
						           } 
						     EOT 
						   + 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          = "./data/SF-CMD-StartReplication.ps1" 
						   + id                = (known after apply) 
						} 
						 
						  # local_file.WriteDDC2SFTaskScriptIntoDataDirectory will be created 
						  + resource "local_file" "WriteDDC2SFTaskScriptIntoDataDirectory" { 
						   + 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          = "./data/SF-Task-StartReplication-DDC2.ps1" 
						   + id                = (known after apply) 
						} 
						 
						  # local_file.WriteLicScriptIntoDataDirectory will be created 
						  + resource "local_file" "WriteLicScriptIntoDataDirectory" { 
						   + 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          = "./data/CVAD-CreateDNSARecordOnDC.ps1" 
						   + id                = (known after apply) 
						} 
						 
						  # local_file.WriteRebootScriptIntoDataDirectory will be created 
						  + resource "local_file" "WriteRebootScriptIntoDataDirectory" { 
						   + content           = &lt;&lt;-EOT 
						         Restart-Computer -Force 
						     EOT 
						   + 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          = "./data/Reboot.ps1" 
						   + id                = (known after apply) 
						} 
						 
						  # local_file.WriteSFScriptIntoDataDirectory will be created 
						  + resource "local_file" "WriteSFScriptIntoDataDirectory" { 
						   + content           = &lt;&lt;-EOT 
						            E:x64/StoreFront/CitrixStoreFront-x64.exe -silent 
						     EOT 
						   + 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          = "./data/SF-Install.ps1" 
						   + id                = (known after apply) 
						} 
						 
						  # null_resource.CreateCVADDBs will be created 
						  + resource "null_resource" "CreateCVADDBs" { 
						   + id = (known after apply) 
						} 
						 
						  # null_resource.InstallCVADFromCDROMOnDDC1 will be created 
						  + resource "null_resource" "InstallCVADFromCDROMOnDDC1" { 
						   + id = (known after apply) 
						} 
						 
						  # null_resource.InstallCVADFromCDROMOnDDC2 will be created 
						  + resource "null_resource" "InstallCVADFromCDROMOnDDC2" { 
						   + id = (known after apply) 
						} 
						 
						  # null_resource.InstallSFOnDDC1 will be created 
						  + resource "null_resource" "InstallSFOnDDC1" { 
						   + id = (known after apply) 
						} 
						 
						  # null_resource.InstallSFOnDDC2 will be created 
						  + resource "null_resource" "InstallSFOnDDC2" { 
						   + id = (known after apply) 
						} 
						 
						  # null_resource.RebootDDC1 will be created 
						  + resource "null_resource" "RebootDDC1" { 
						   + id = (known after apply) 
						} 
						 
						  # null_resource.RebootDDC1AfterStoreFrontInstall will be created 
						  + resource "null_resource" "RebootDDC1AfterStoreFrontInstall" { 
						   + id = (known after apply) 
						} 
						 
						  # null_resource.RebootDDC2 will be created 
						  + resource "null_resource" "RebootDDC2" { 
						   + id = (known after apply) 
						} 
						 
						  # null_resource.RebootDDC2AfterStoreFrontInstall will be created 
						  + resource "null_resource" "RebootDDC2AfterStoreFrontInstall" { 
						   + id = (known after apply) 
						} 
						 
						  # time_sleep.WaitForRebootofDDC1 will be created 
						  + resource "time_sleep" "WaitForRebootofDDC1" { 
						   + create_duration = "120s" 
						   + id           = (known after apply) 
						} 
						 
						  # time_sleep.WaitForRebootofDDC1AfterStoreFrontInstall will be created 
						  + resource "time_sleep" "WaitForRebootofDDC1AfterStoreFrontInstall" { 
						   + create_duration = "120s" 
						   + id           = (known after apply) 
						} 
						 
						  # time_sleep.WaitForRebootofDDC2 will be created 
						  + resource "time_sleep" "WaitForRebootofDDC2" { 
						   + create_duration = "120s" 
						   + id           = (known after apply) 
						} 
						 
						  # time_sleep.WaitForRebootofDDC2AfterStorefrontInstall will be created 
						  + resource "time_sleep" "WaitForRebootofDDC2AfterStorefrontInstall" { 
						   + create_duration = "120s" 
						   + id           = (known after apply) 
						} 
						 
						  # time_sleep.WaitToRebootDDC1AfterCVADInstall will be created 
						  + resource "time_sleep" "WaitToRebootDDC1AfterCVADInstall" { 
						   + create_duration = "600s" 
						   + id           = (known after apply) 
						} 
						 
						  # time_sleep.WaitToRebootDDC2AfterCVADInstall will be created 
						  + resource "time_sleep" "WaitToRebootDDC2AfterCVADInstall" { 
						   + create_duration = "600s" 
						   + id           = (known after apply) 
						} 
						 
						Plan: 25 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\_CVADOnNutanix\_CVADOnNutanix-Configuration&gt; terraform vaildate 
						Terraform has no command named "vaildate". Did you mean "validate"? 
						 
						To see all of Terraform's top-level commands, run: 
						  terraform -help 
						 
						PS C:\_TACG\_CVADOnNutanix\_CVADOnNutanix-Configuration&gt; terraform validate 
						Success. The configuration is valid. 
						 
						PS C:\_TACG\_CVADOnNutanix\_CVADOnNutanix-Configuration&gt; terraform apply 
						 
						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.StoreFrontConfigurationScriptIntoDataDirectory will be created 
						  + resource "local_file" "StoreFrontConfigurationScriptIntoDataDirectory" { 
						   + content           = &lt;&lt;-EOT 
						         #$PSUsername = 'XXXXXXXXXXXXXXXX' 
						         #$PSPassword = '.XXXXXXXXXXXXXXXX..' 
						         #$PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force 
						         #$PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword) 
						         #Invoke-Command -ComputerName localhost -Credential $PSCredential -ScriptBlock { 
						         Add-PSSnapin Citrix.* 
						 
						         #Clear-Existing StoreFront deployments 
						         Clear-STFDeployment -Confirm:$false 
						         Write-Output 'Existing Deployments deleted.' 
						 
						         #Add new StoreFront deployment 
						         Add-STFDeployment -HostBaseUrl 'https://storefront.the-austrian-citrix-guy.at' -Confirm:$false 
						         Write-Output 'New deployment created.' 
						 
						         #Add new Authentication Service 
						         Add-STFAuthenticationService '/Citrix/Authentication' 
						         $authentication = Get-STFAuthenticationService 
						         Write-Output 'Authentication service created.' 
						 
						         Start-Sleep -Seconds 120 
						 
						         #Enable Access through a NetScaler Gateway 
						            Enable-STFAuthenticationServiceProtocol -Name CitrixAGBasic -AuthenticationService ($authentication) 
						         $protocols = Get-STFAuthenticationServiceProtocol -AuthenticationService ($authentication) 
						 
						         #Add NetScaler Gateway 
						         Add-STFRoamingGateway -Name TACG-TF-NSGW -LogonType Domain -GatewayUrl 'https://ns.the-austrian-citrix-guy.at' -CallbackUrl 'https://ns.the-austrian-citrix-guy.at/CitrixAuthService/AuthService.asmx' -SessionReliability:$true -SecureTicketAuthorityUrls 'http://TACG-NTX-DDC1.the-austrian-citrix-guy.at/scripts/ctxsta.dll,http://TACG-NTX-DDC2.the-austrian-citrix-guy.at/scripts/ctxsta.dll' -IsCloudGateway:$false 
						         Write-Output 'NSGW created.' 
						         #Add Beacons for NetScaler Gateway 
						         Set-STFRoamingBeacon -Internal 'http://TACG-NTX-DDC1.the-austrian-citrix-guy.at' -External https://ns.the-austrian-citrix-guy.at,https://news.orf.at 
						         Write-Output 'Beacons created.' 
						 
						         #Add new Store Service 
						         Add-STFStoreService -VirtualPath '/Citrix/Store' -AuthenticationService $authentication -FarmName 'TACG-SF' -FarmType 'XenDesktop' -Servers 'TACG-NTX-DDC1.the-austrian-citrix-guy.at' -FriendlyName 'TACG-SF' -TransportType HTTPS -Port 443 
						         Write-Output 'Store service created.' 
						 
						         #Register NetScaler Gateway and Beacons 
						         $store = Get-STFStoreService -VirtualPath '/Citrix/Store' 
						         $gateway = Get-STFRoamingGateway -Name TACG-TF-NSGW 
						         Register-STFStoreGateway -Gateway $gateway -StoreService $store -DefaultGateway -UseFullVpn:$false 
						         Write-Output 'NSGW registered.' 
						 
						         #Enable WebReceiver 
						         Add-STFWebReceiverService -VirtualPath '/Citrix/StoreWeb' -StoreService $store 
						         $receiver = Get-STFWebReceiverService -VirtualPath '/Citrix/StoreWeb' 
						         Write-Output 'Web Receiver site created.' 
						 
						         #Enable PNAGent site 
						         Enable-STFStorePna -StoreService $store -AllowUserPasswordChange -DefaultPnaService 
						         Write-Output 'PNA site created.' 
						 
						         #Enable HTML5Fallback 
						         Set-STFWebReceiverPluginAssistant -WebReceiverService $receiver -Enabled $true -Html5Enabled "Fallback" 
						         Write-Output 'HTML5 site created.' 
						         Write-Output 'Storefront configuration complete.' 
						         Write-Output 'Registering Storefront in CVAD Site configuration.' 
						 
						         $PSUsername = 'XXXXXXXXXXXXXXXX' 
						         $PSPassword = '.XXXXXXXXXXXXXXXX..' 
						         $PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force 
						         $PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword) 
						         Invoke-Command -ComputerName localhost -Credential $PSCredential -ScriptBlock { 
						         Add-PSSnapin Citrix.* 
						 
						         #Add StoreFront configuration to CVAD site 
						         $configurationSlot = Get-BrokerConfigurationSlot -Name "RS" 
						         $storefrontUrl = 'https://storefront.the-austrian-citrix-guy.at/Citrix/StoreWeb' 
						         $configuration = New-BrokerStorefrontAddress -Description "Citrix Storefront Address" -Enabled $true -Name "Citrix StoreFront" -Url $storefrontUrl 
						         New-BrokerMachineConfiguration -Policy $configuration -ConfigurationSlotUid $configurationSlot.Uid -Description "Citrix Storefront Address Configuration" -LeafName 'TACG-SF' 
						         Write-Output 'Registration in CVAD Site complete.' 
						         Write-Output 'Reboot needed...' 
						         } 
						     EOT 
						   + 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          = "./data/SF-Config.ps1" 
						   + id                = (known after apply) 
						} 
						 
						  # local_file.WriteCVADDBScriptIntoDataDirectory will be created 
						  + resource "local_file" "WriteCVADDBScriptIntoDataDirectory" { 
						   + 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          = "./data/CVAD-DBs.ps1" 
						   + id                = (known after apply) 
						} 
						 
						  # local_file.WriteCVADScriptIntoDataDirectory will be created 
						  + resource "local_file" "WriteCVADScriptIntoDataDirectory" { 
						   + content           = &lt;&lt;-EOT 
						         New-Item -Path 'C:/TEMP/XDINST' -ItemType Directory 
						         E:/x64/"XenDesktop Setup"/XenDesktopServerSetup.exe /components controller,webstudio,licenseserver,desktopdirector /noreboot /configure_firewall /quiet /nosql /Logpath C:/TEMP/XDINST 
						         #Restart-Computer -Force 
						     EOT 
						   + 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          = "./data/CVAD-Install.ps1" 
						   + id                = (known after apply) 
						} 
						 
						  # local_file.WriteDDC1SFTaskRunScriptIntoDataDirectory will be created 
						  + resource "local_file" "WriteDDC1SFTaskRunScriptIntoDataDirectory" { 
						   + content           = &lt;&lt;-EOT 
						         Add-PSSnapin Citrix.Storefront.* 
						         Start-Service -Name CitrixClusterService 
						         $JoinObject = Start-STFServerGroupJoin -IsAuthorizingServer -Confirm:$false 
						         $JoinPassCode = ($JoinObject | Select -ExpandProperty 'Passcode') 
						         $JoinPassCode | Out-File -Force -FilePath 'C:/TEMP/XDINST/pc.txt' 
						         Copy-Item -Path C:/TEMP/XDINST/pc.txt -Destination \\\\10.10.170.12\c$\temp\XDINST\pc.txt 
						         Write-Output 'Replication enabled.' 
						           } 
						     EOT 
						   + 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          = "./data/SF-CMD-EnableReplication.ps1" 
						   + id                = (known after apply) 
						} 
						 
						  # local_file.WriteDDC1SFTaskScriptIntoDataDirectory will be created 
						  + resource "local_file" "WriteDDC1SFTaskScriptIntoDataDirectory" { 
						   + 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          = "./data/SF-Task-StartReplication-DDC1.ps1" 
						   + id                = (known after apply) 
						} 
						 
						  # local_file.WriteDDC2SFTaskRunScriptIntoDataDirectory will be created 
						  + resource "local_file" "WriteDDC2SFTaskRunScriptIntoDataDirectory" { 
						   + content           = &lt;&lt;-EOT 
						         Add-PSSnapin Citrix.Storefront.* 
						         Start-Service -Name CitrixClusterService 
						         $Passcode = Get-Content -Path 'C:/TEMP/XDINST/pc.txt' 
						         Start-STFServerGroupJoin -AuthorizerHostName TACG-NTX-DDC1.the-austrian-citrix-guy.at -Passcode $Passcode -Confirm:$false 
						         Wait-STFServerGroupJoin -WaitTimeout 20 
						         Write-Output 'Replication complete.' 
						           } 
						     EOT 
						   + 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          = "./data/SF-CMD-StartReplication.ps1" 
						   + id                = (known after apply) 
						} 
						 
						  # local_file.WriteDDC2SFTaskScriptIntoDataDirectory will be created 
						  + resource "local_file" "WriteDDC2SFTaskScriptIntoDataDirectory" { 
						   + 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          = "./data/SF-Task-StartReplication-DDC2.ps1" 
						   + id                = (known after apply) 
						} 
						 
						  # local_file.WriteLicScriptIntoDataDirectory will be created 
						  + resource "local_file" "WriteLicScriptIntoDataDirectory" { 
						   + 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          = "./data/CVAD-CreateDNSARecordOnDC.ps1" 
						   + id                = (known after apply) 
						} 
						 
						  # local_file.WriteRebootScriptIntoDataDirectory will be created 
						  + resource "local_file" "WriteRebootScriptIntoDataDirectory" { 
						   + content           = &lt;&lt;-EOT 
						         Restart-Computer -Force 
						     EOT 
						   + 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          = "./data/Reboot.ps1" 
						   + id                = (known after apply) 
						} 
						 
						  # local_file.WriteSFScriptIntoDataDirectory will be created 
						  + resource "local_file" "WriteSFScriptIntoDataDirectory" { 
						   + content           = &lt;&lt;-EOT 
						            E:x64/StoreFront/CitrixStoreFront-x64.exe -silent 
						     EOT 
						   + 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          = "./data/SF-Install.ps1" 
						   + id                = (known after apply) 
						} 
						 
						  # null_resource.CreateCVADDBs will be created 
						  + resource "null_resource" "CreateCVADDBs" { 
						   + id = (known after apply) 
						} 
						 
						  # null_resource.InstallCVADFromCDROMOnDDC1 will be created 
						  + resource "null_resource" "InstallCVADFromCDROMOnDDC1" { 
						   + id = (known after apply) 
						} 
						 
						  # null_resource.InstallCVADFromCDROMOnDDC2 will be created 
						  + resource "null_resource" "InstallCVADFromCDROMOnDDC2" { 
						   + id = (known after apply) 
						} 
						 
						  # null_resource.InstallSFOnDDC1 will be created 
						  + resource "null_resource" "InstallSFOnDDC1" { 
						   + id = (known after apply) 
						} 
						 
						  # null_resource.InstallSFOnDDC2 will be created 
						  + resource "null_resource" "InstallSFOnDDC2" { 
						   + id = (known after apply) 
						} 
						 
						  # null_resource.RebootDDC1 will be created 
						  + resource "null_resource" "RebootDDC1" { 
						   + id = (known after apply) 
						} 
						 
						  # null_resource.RebootDDC1AfterStoreFrontInstall will be created 
						  + resource "null_resource" "RebootDDC1AfterStoreFrontInstall" { 
						   + id = (known after apply) 
						} 
						 
						  # null_resource.RebootDDC2 will be created 
						  + resource "null_resource" "RebootDDC2" { 
						   + id = (known after apply) 
						} 
						 
						  # null_resource.RebootDDC2AfterStoreFrontInstall will be created 
						  + resource "null_resource" "RebootDDC2AfterStoreFrontInstall" { 
						   + id = (known after apply) 
						} 
						 
						  # time_sleep.WaitForRebootofDDC1 will be created 
						  + resource "time_sleep" "WaitForRebootofDDC1" { 
						   + create_duration = "120s" 
						   + id           = (known after apply) 
						} 
						 
						  # time_sleep.WaitForRebootofDDC1AfterStoreFrontInstall will be created 
						  + resource "time_sleep" "WaitForRebootofDDC1AfterStoreFrontInstall" { 
						   + create_duration = "120s" 
						   + id           = (known after apply) 
						} 
						 
						  # time_sleep.WaitForRebootofDDC2 will be created 
						  + resource "time_sleep" "WaitForRebootofDDC2" { 
						   + create_duration = "120s" 
						   + id           = (known after apply) 
						} 
						 
						  # time_sleep.WaitForRebootofDDC2AfterStorefrontInstall will be created 
						  + resource "time_sleep" "WaitForRebootofDDC2AfterStorefrontInstall" { 
						   + create_duration = "120s" 
						   + id           = (known after apply) 
						} 
						 
						  # time_sleep.WaitToRebootDDC1AfterCVADInstall will be created 
						  + resource "time_sleep" "WaitToRebootDDC1AfterCVADInstall" { 
						   + create_duration = "600s" 
						   + id           = (known after apply) 
						} 
						 
						  # time_sleep.WaitToRebootDDC2AfterCVADInstall will be created 
						  + resource "time_sleep" "WaitToRebootDDC2AfterCVADInstall" { 
						   + create_duration = "600s" 
						   + id           = (known after apply) 
						} 
						 
						Plan: 25 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.InstallSFOnDDC1: Creating... 
						null_resource.InstallSFOnDDC2: Creating... 
						null_resource.InstallSFOnDDC1: Provisioning with 'file'... 
						null_resource.InstallSFOnDDC2: Provisioning with 'file'... 
						local_file.WriteDDC2SFTaskRunScriptIntoDataDirectory: Creating... 
						local_file.WriteCVADScriptIntoDataDirectory: Creating... 
						local_file.WriteRebootScriptIntoDataDirectory: Creating... 
						local_file.WriteDDC1SFTaskScriptIntoDataDirectory: Creating... 
						local_file.WriteCVADScriptIntoDataDirectory: Creation complete after 0s [id=7f7d6ec0dd9f8c014f1464f533c71cabf91b441a] 
						local_file.WriteRebootScriptIntoDataDirectory: Creation complete after 0s [id=2baf7fa209ad203f96017f90f5727642e5f44a03] 
						local_file.WriteDDC1SFTaskRunScriptIntoDataDirectory: Creating... 
						local_file.StoreFrontConfigurationScriptIntoDataDirectory: Creating... 
						local_file.WriteDDC2SFTaskScriptIntoDataDirectory: Creating... 
						local_file.WriteSFScriptIntoDataDirectory: Creating... 
						local_file.WriteDDC2SFTaskRunScriptIntoDataDirectory: Creation complete after 0s [id=02161fe4131890766f98967cc930011f0b93eaca] 
						local_file.WriteDDC2SFTaskScriptIntoDataDirectory: Creation complete after 0s [id=aa984fd3ab00ed8f3a239cc7647b3f5688460bfc] 
						local_file.WriteDDC1SFTaskScriptIntoDataDirectory: Creation complete after 0s [id=6229451f12025d35a2e569add9ff899780513b24] 
						local_file.StoreFrontConfigurationScriptIntoDataDirectory: Creation complete after 0s [id=2f3d11280a9dcef7ea52e2f653c6a920f3a6f8c3] 
						null_resource.InstallCVADFromCDROMOnDDC2: Creating... 
						local_file.WriteCVADDBScriptIntoDataDirectory: Creating... 
						local_file.WriteSFScriptIntoDataDirectory: Creation complete after 0s [id=67b6e4162cdbda811d21577db9294315552b9fba] 
						local_file.WriteLicScriptIntoDataDirectory: Creating... 
						local_file.WriteDDC1SFTaskRunScriptIntoDataDirectory: Creation complete after 0s [id=686353748ed0dfaade698c06399d0537a0493b47] 
						null_resource.InstallCVADFromCDROMOnDDC1: Creating... 
						local_file.WriteCVADDBScriptIntoDataDirectory: Creation complete after 0s [id=551d23d8f6f28cbd10f101ca8fc334e35bd50eb4] 
						null_resource.InstallCVADFromCDROMOnDDC2: Provisioning with 'file'... 
						null_resource.InstallCVADFromCDROMOnDDC1: Provisioning with 'file'... 
						local_file.WriteLicScriptIntoDataDirectory: Creation complete after 0s [id=3dc0bf6141de46630445336b98d812d860dc229b] 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.InstallSFOnDDC1: Provisioning with 'remote-exec'... 
						null_resource.InstallSFOnDDC1 (remote-exec): Connecting to remote host via WinRM... 
						null_resource.InstallSFOnDDC1 (remote-exec):   Host: 10.10.175.1 
						null_resource.InstallSFOnDDC1 (remote-exec):   Port: 5985 
						null_resource.InstallSFOnDDC1 (remote-exec):   User: administrator 
						null_resource.InstallSFOnDDC1 (remote-exec):   Password: true 
						null_resource.InstallSFOnDDC1 (remote-exec):   HTTPS: false 
						null_resource.InstallSFOnDDC1 (remote-exec):   Insecure: false 
						null_resource.InstallSFOnDDC1 (remote-exec):   NTLM: false 
						null_resource.InstallSFOnDDC1 (remote-exec):   CACert: false 
						null_resource.InstallSFOnDDC1 (remote-exec): Connected. 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.InstallCVADFromCDROMOnDDC2: Provisioning with 'remote-exec'... 
						null_resource.InstallCVADFromCDROMOnDDC2 (remote-exec): Connecting to remote host via WinRM... 
						null_resource.InstallCVADFromCDROMOnDDC2 (remote-exec):   Host: 10.10.175.2 
						null_resource.InstallCVADFromCDROMOnDDC2 (remote-exec):   Port: 5985 
						null_resource.InstallCVADFromCDROMOnDDC2 (remote-exec):   User: administrator 
						null_resource.InstallCVADFromCDROMOnDDC2 (remote-exec):   Password: true 
						null_resource.InstallCVADFromCDROMOnDDC2 (remote-exec):   HTTPS: false 
						null_resource.InstallCVADFromCDROMOnDDC2 (remote-exec):   Insecure: false 
						null_resource.InstallCVADFromCDROMOnDDC2 (remote-exec):   NTLM: false 
						null_resource.InstallCVADFromCDROMOnDDC2 (remote-exec):   CACert: false 
						null_resource.InstallCVADFromCDROMOnDDC2 (remote-exec): Connected. 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.InstallSFOnDDC2: Provisioning with 'remote-exec'... 
						null_resource.InstallSFOnDDC2 (remote-exec): Connecting to remote host via WinRM... 
						null_resource.InstallSFOnDDC2 (remote-exec):   Host: 10.10.175.2 
						null_resource.InstallSFOnDDC2 (remote-exec):   Port: 5985 
						null_resource.InstallSFOnDDC2 (remote-exec):   User: administrator 
						null_resource.InstallSFOnDDC2 (remote-exec):   Password: true 
						null_resource.InstallSFOnDDC2 (remote-exec):   HTTPS: false 
						null_resource.InstallSFOnDDC2 (remote-exec):   Insecure: false 
						null_resource.InstallSFOnDDC2 (remote-exec):   NTLM: false 
						null_resource.InstallSFOnDDC2 (remote-exec):   CACert: false 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.InstallCVADFromCDROMOnDDC1: Provisioning with 'remote-exec'... 
						null_resource.InstallCVADFromCDROMOnDDC1 (remote-exec): Connecting to remote host via WinRM... 
						null_resource.InstallCVADFromCDROMOnDDC1 (remote-exec):   Host: 10.10.175.1 
						null_resource.InstallCVADFromCDROMOnDDC1 (remote-exec):   Port: 5985 
						null_resource.InstallCVADFromCDROMOnDDC1 (remote-exec):   User: administrator 
						null_resource.InstallCVADFromCDROMOnDDC1 (remote-exec):   Password: true 
						null_resource.InstallCVADFromCDROMOnDDC1 (remote-exec):   HTTPS: false 
						null_resource.InstallCVADFromCDROMOnDDC1 (remote-exec):   Insecure: false 
						null_resource.InstallCVADFromCDROMOnDDC1 (remote-exec):   NTLM: false 
						null_resource.InstallCVADFromCDROMOnDDC1 (remote-exec):   CACert: false 
						null_resource.InstallCVADFromCDROMOnDDC1 (remote-exec): Connected. 
						null_resource.InstallSFOnDDC2 (remote-exec): Connected. 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
						null_resource.InstallSFOnDDC1 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/SF-Install.ps1 
						 
						null_resource.InstallCVADFromCDROMOnDDC2 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/CVAD-Install.ps1 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
						null_resource.InstallCVADFromCDROMOnDDC1 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/CVAD-Install.ps1 
						 
						null_resource.InstallSFOnDDC2 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/SF-Install.ps1 
						null_resource.InstallCVADFromCDROMOnDDC2 (remote-exec): New-Item : An item with the specified name C:\TEMP\XDINST already exists. 
						null_resource.InstallCVADFromCDROMOnDDC2 (remote-exec): At C:\TEMP\XDINST\CVAD-Install.ps1:1 char:1 
						null_resource.InstallCVADFromCDROMOnDDC2 (remote-exec): + New-Item -Path 'C:/TEMP/XDINST' -ItemType Directory 
						null_resource.InstallCVADFromCDROMOnDDC2 (remote-exec): + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
						null_resource.InstallCVADFromCDROMOnDDC2 (remote-exec):  + CategoryInfo       : ResourceExists: (C:\TEMP\XDINST:String) [New-Item], IOException 
						null_resource.InstallCVADFromCDROMOnDDC2 (remote-exec):  + FullyQualifiedErrorId : DirectoryExist,Microsoft.PowerShell.Commands.NewItemCommand 
						 
						null_resource.InstallCVADFromCDROMOnDDC1 (remote-exec): New-Item : An item with the specified name C:\TEMP\XDINST already exists. 
						null_resource.InstallCVADFromCDROMOnDDC1 (remote-exec): At C:\TEMP\XDINST\CVAD-Install.ps1:1 char:1 
						null_resource.InstallCVADFromCDROMOnDDC1 (remote-exec): + New-Item -Path 'C:/TEMP/XDINST' -ItemType Directory 
						null_resource.InstallCVADFromCDROMOnDDC1 (remote-exec): + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
						null_resource.InstallCVADFromCDROMOnDDC1 (remote-exec):  + CategoryInfo       : ResourceExists: (C:\TEMP\XDINST:String) [New-Item], IOException 
						null_resource.InstallCVADFromCDROMOnDDC1 (remote-exec):  + FullyQualifiedErrorId : DirectoryExist,Microsoft.PowerShell.Commands.NewItemCommand 
						 
						null_resource.InstallSFOnDDC2: Still creating... [10s elapsed] 
						null_resource.InstallSFOnDDC1: Still creating... [10s elapsed] 
						null_resource.InstallCVADFromCDROMOnDDC2: Still creating... [10s elapsed] 
						null_resource.InstallCVADFromCDROMOnDDC1: Still creating... [10s elapsed] 
						null_resource.InstallSFOnDDC2: Still creating... [20s elapsed] 
						null_resource.InstallSFOnDDC1: Still creating... [20s elapsed] 
						null_resource.InstallCVADFromCDROMOnDDC2: Still creating... [20s elapsed] 
						null_resource.InstallCVADFromCDROMOnDDC1: Still creating... [20s elapsed] 
						... 
						null_resource.InstallSFOnDDC1: Still creating... [1m50s elapsed] 
						null_resource.InstallSFOnDDC2: Still creating... [1m50s elapsed] 
						null_resource.InstallCVADFromCDROMOnDDC2: Still creating... [1m50s elapsed] 
						null_resource.InstallCVADFromCDROMOnDDC1: Still creating... [1m50s elapsed] 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.InstallSFOnDDC1: Creation complete after 1m56s [id=12740271057613761] 
						null_resource.RebootDDC1AfterStoreFrontInstall: Creating... 
						null_resource.RebootDDC1AfterStoreFrontInstall: Provisioning with 'remote-exec'... 
						null_resource.RebootDDC1AfterStoreFrontInstall (remote-exec): Connecting to remote host via WinRM... 
						null_resource.RebootDDC1AfterStoreFrontInstall (remote-exec):   Host: 10.10.175.1 
						null_resource.RebootDDC1AfterStoreFrontInstall (remote-exec):   Port: 5985 
						null_resource.RebootDDC1AfterStoreFrontInstall (remote-exec):   User: administrator 
						null_resource.RebootDDC1AfterStoreFrontInstall (remote-exec):   Password: true 
						null_resource.RebootDDC1AfterStoreFrontInstall (remote-exec):   HTTPS: false 
						null_resource.RebootDDC1AfterStoreFrontInstall (remote-exec):   Insecure: false 
						null_resource.RebootDDC1AfterStoreFrontInstall (remote-exec):   NTLM: false 
						null_resource.RebootDDC1AfterStoreFrontInstall (remote-exec):   CACert: false 
						null_resource.RebootDDC1AfterStoreFrontInstall (remote-exec): Connected. 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
						null_resource.RebootDDC1AfterStoreFrontInstall (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/Reboot.ps1 
						null_resource.RebootDDC1AfterStoreFrontInstall (remote-exec): The argument 'C:/TEMP/XDINST/Reboot.ps1' to the -File parameter does not exist. Provide the path to an existing '.ps1' file as an argument to the -File parameter. 
						null_resource.RebootDDC1AfterStoreFrontInstall (remote-exec): Windows PowerShell 
						null_resource.RebootDDC1AfterStoreFrontInstall (remote-exec): Copyright (C) Microsoft Corporation. All rights reserved. 
						 
						null_resource.RebootDDC1AfterStoreFrontInstall (remote-exec): Install the latest PowerShell for new features and improvements. https://aka.ms/PSWindows 
						 
						null_resource.InstallSFOnDDC2: Still creating... [2m0s elapsed] 
						null_resource.InstallCVADFromCDROMOnDDC1: Still creating... [2m0s elapsed] 
						null_resource.InstallCVADFromCDROMOnDDC2: Still creating... [2m0s elapsed] 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.InstallSFOnDDC2: Creation complete after 2m10s [id=7994408513767760452] 
						null_resource.RebootDDC2AfterStoreFrontInstall: Creating... 
						null_resource.RebootDDC2AfterStoreFrontInstall: Provisioning with 'remote-exec'... 
						null_resource.RebootDDC2AfterStoreFrontInstall (remote-exec): Connecting to remote host via WinRM... 
						null_resource.RebootDDC2AfterStoreFrontInstall (remote-exec):   Host: 10.10.175.2 
						null_resource.RebootDDC2AfterStoreFrontInstall (remote-exec):   Port: 5985 
						null_resource.RebootDDC2AfterStoreFrontInstall (remote-exec):   User: administrator 
						null_resource.RebootDDC2AfterStoreFrontInstall (remote-exec):   Password: true 
						null_resource.RebootDDC2AfterStoreFrontInstall (remote-exec):   HTTPS: false 
						null_resource.RebootDDC2AfterStoreFrontInstall (remote-exec):   Insecure: false 
						null_resource.RebootDDC2AfterStoreFrontInstall (remote-exec):   NTLM: false 
						null_resource.RebootDDC2AfterStoreFrontInstall (remote-exec):   CACert: false 
						null_resource.RebootDDC2AfterStoreFrontInstall (remote-exec): Connected. 
						null_resource.InstallCVADFromCDROMOnDDC2: Still creating... [2m10s elapsed] 
						null_resource.InstallCVADFromCDROMOnDDC1: Still creating... [2m10s elapsed] 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
						null_resource.RebootDDC2AfterStoreFrontInstall (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/Reboot.ps1 
						null_resource.RebootDDC2AfterStoreFrontInstall (remote-exec): The argument 'C:/TEMP/XDINST/Reboot.ps1' to the -File parameter does not exist. Provide the path to an existing '.ps1' file as an argument to the -File parameter. 
						null_resource.RebootDDC2AfterStoreFrontInstall (remote-exec): Windows PowerShell 
						null_resource.RebootDDC2AfterStoreFrontInstall (remote-exec): Copyright (C) Microsoft Corporation. All rights reserved. 
						 
						null_resource.RebootDDC2AfterStoreFrontInstall (remote-exec): Install the latest PowerShell for new features and improvements. https://aka.ms/PSWindows 
						 
						null_resource.InstallCVADFromCDROMOnDDC1: Still creating... [2m20s elapsed] 
						null_resource.InstallCVADFromCDROMOnDDC2: Still creating... [2m20s elapsed] 
						null_resource.InstallCVADFromCDROMOnDDC2: Still creating... [2m30s elapsed] 
						null_resource.InstallCVADFromCDROMOnDDC1: Still creating... [2m30s elapsed]... 
						null_resource.InstallCVADFromCDROMOnDDC2: Still creating... [6m51s elapsed] 
						null_resource.InstallCVADFromCDROMOnDDC1: Still creating... [6m51s elapsed] 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.InstallCVADFromCDROMOnDDC1: Creation complete after 6m54s [id=6022042994126744423] 
						time_sleep.WaitToRebootDDC1AfterCVADInstall: Creating... 
						null_resource.InstallCVADFromCDROMOnDDC2: Still creating... [7m1s elapsed] 
						time_sleep.WaitToRebootDDC1AfterCVADInstall: Still creating... [10s elapsed] 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.InstallCVADFromCDROMOnDDC2: Creation complete after 7m4s [id=3632378795821932960] 
						time_sleep.WaitToRebootDDC2AfterCVADInstall: Creating... 
						time_sleep.WaitToRebootDDC1AfterCVADInstall: Still creating... [20s elapsed] 
						time_sleep.WaitToRebootDDC2AfterCVADInstall: Still creating... [10s elapsed] 
						time_sleep.WaitToRebootDDC1AfterCVADInstall: Still creating... [30s elapsed] 
						time_sleep.WaitToRebootDDC2AfterCVADInstall: Still creating... [20s elapsed] 
						... 
						time_sleep.WaitToRebootDDC1AfterCVADInstall: Still creating... [9m50s elapsed] 
						time_sleep.WaitToRebootDDC2AfterCVADInstall: Still creating... [9m40s elapsed] 
						time_sleep.WaitToRebootDDC1AfterCVADInstall: Creation complete after 10m0s [id=2024-08-20T07:36:42Z] 
						null_resource.RebootDDC1: Creating... 
						null_resource.RebootDDC1: Provisioning with 'file'... 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;time_sleep.WaitToRebootDDC2AfterCVADInstall: Still creating... [9m50s elapsed] 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.RebootDDC1: Provisioning with 'remote-exec'... 
						null_resource.RebootDDC1 (remote-exec): Connecting to remote host via WinRM... 
						null_resource.RebootDDC1 (remote-exec):   Host: 10.10.175.1 
						null_resource.RebootDDC1 (remote-exec):   Port: 5985 
						null_resource.RebootDDC1 (remote-exec):   User: administrator 
						null_resource.RebootDDC1 (remote-exec):   Password: true 
						null_resource.RebootDDC1 (remote-exec):   HTTPS: false 
						null_resource.RebootDDC1 (remote-exec):   Insecure: false 
						null_resource.RebootDDC1 (remote-exec):   NTLM: false 
						null_resource.RebootDDC1 (remote-exec):   CACert: false 
						null_resource.RebootDDC1 (remote-exec): Connected. 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
						null_resource.RebootDDC1 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/Reboot.ps1 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.RebootDDC1: Creation complete after 2s [id=5470883078777869719] 
						time_sleep.WaitForRebootofDDC1: Creating... 
						time_sleep.WaitToRebootDDC2AfterCVADInstall: Creation complete after 10m0s [id=2024-08-20T07:36:52Z] 
						null_resource.RebootDDC2: Creating... 
						null_resource.RebootDDC2: Provisioning with 'file'... 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.RebootDDC2: Provisioning with 'remote-exec'... 
						null_resource.RebootDDC2 (remote-exec): Connecting to remote host via WinRM... 
						null_resource.RebootDDC2 (remote-exec):   Host: 10.10.175.2 
						null_resource.RebootDDC2 (remote-exec):   Port: 5985 
						null_resource.RebootDDC2 (remote-exec):   User: administrator 
						null_resource.RebootDDC2 (remote-exec):   Password: true 
						null_resource.RebootDDC2 (remote-exec):   HTTPS: false 
						null_resource.RebootDDC2 (remote-exec):   Insecure: false 
						null_resource.RebootDDC2 (remote-exec):   NTLM: false 
						null_resource.RebootDDC2 (remote-exec):   CACert: false 
						null_resource.RebootDDC2 (remote-exec): Connected. 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
						null_resource.RebootDDC2 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/Reboot.ps1 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;time_sleep.WaitForRebootofDDC1: Still creating... [10s elapsed] 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.RebootDDC2: Creation complete after 2s [id=3509189007396172312] 
						time_sleep.WaitForRebootofDDC2: Creating... 
						time_sleep.WaitForRebootofDDC1: Still creating... [20s elapsed] 
						time_sleep.WaitForRebootofDDC2: Still creating... [10s elapsed] 
						time_sleep.WaitForRebootofDDC1: Still creating... [30s elapsed] 
						... 
						time_sleep.WaitForRebootofDDC1: Still creating... [1m50s elapsed] 
						time_sleep.WaitForRebootofDDC2: Still creating... [1m40s elapsed] 
						time_sleep.WaitForRebootofDDC1: Creation complete after 2m0s [id=2024-08-20T07:38:45Z] 
						null_resource.CreateCVADDBs: Creating... 
						null_resource.CreateCVADDBs: Provisioning with 'file'... 
						time_sleep.WaitForRebootofDDC2: Still creating... [1m50s elapsed] 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.CreateCVADDBs: Provisioning with 'remote-exec'... 
						null_resource.CreateCVADDBs (remote-exec): Connecting to remote host via WinRM... 
						null_resource.CreateCVADDBs (remote-exec):   Host: 10.10.175.1 
						null_resource.CreateCVADDBs (remote-exec):   Port: 5985 
						null_resource.CreateCVADDBs (remote-exec):   User: administrator 
						null_resource.CreateCVADDBs (remote-exec):   Password: true 
						null_resource.CreateCVADDBs (remote-exec):   HTTPS: false 
						null_resource.CreateCVADDBs (remote-exec):   Insecure: false 
						null_resource.CreateCVADDBs (remote-exec):   NTLM: false 
						null_resource.CreateCVADDBs (remote-exec):   CACert: false 
						null_resource.CreateCVADDBs (remote-exec): Connected. 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
						null_resource.CreateCVADDBs (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/CVAD-DBs.ps1 
						null_resource.CreateCVADDBs: Still creating... [10s elapsed] 
						time_sleep.WaitForRebootofDDC2: Creation complete after 2m0s [id=2024-08-20T07:38:55Z] 
						null_resource.CreateCVADDBs: Still creating... [20s elapsed] 
						null_resource.CreateCVADDBs: Still creating... [30s elapsed] 
						 
						 
						null_resource.CreateCVADDBs (remote-exec): PSComputerName   : localhost 
						null_resource.CreateCVADDBs (remote-exec): RunspaceId       : b58da869-f872-4f87-b893-d034bc6f8cf5 
						null_resource.CreateCVADDBs (remote-exec): DataStore        : Site 
						null_resource.CreateCVADDBs (remote-exec): IntegratedSecurity  : True 
						null_resource.CreateCVADDBs (remote-exec): MirrorServerAddress : 
						null_resource.CreateCVADDBs (remote-exec): Name             : CitrixTACG-TF-NTXSite 
						null_resource.CreateCVADDBs (remote-exec): ServerAddress    : TACG-SQL\CITRIX 
						 
						null_resource.CreateCVADDBs (remote-exec): PSComputerName   : localhost 
						null_resource.CreateCVADDBs (remote-exec): RunspaceId       : b58da869-f872-4f87-b893-d034bc6f8cf5 
						null_resource.CreateCVADDBs (remote-exec): DataStore        : Logging 
						null_resource.CreateCVADDBs (remote-exec): IntegratedSecurity  : True 
						null_resource.CreateCVADDBs (remote-exec): MirrorServerAddress : 
						null_resource.CreateCVADDBs (remote-exec): Name             : CitrixTACG-TF-NTXLogging 
						null_resource.CreateCVADDBs (remote-exec): ServerAddress    : TACG-SQL\CITRIX 
						 
						null_resource.CreateCVADDBs (remote-exec): PSComputerName   : localhost 
						null_resource.CreateCVADDBs (remote-exec): RunspaceId       : b58da869-f872-4f87-b893-d034bc6f8cf5 
						null_resource.CreateCVADDBs (remote-exec): DataStore        : Monitor 
						null_resource.CreateCVADDBs (remote-exec): IntegratedSecurity  : True 
						null_resource.CreateCVADDBs (remote-exec): MirrorServerAddress : 
						null_resource.CreateCVADDBs (remote-exec): Name             : CitrixTACG-TF-NTXMonitoring 
						null_resource.CreateCVADDBs (remote-exec): ServerAddress    : TACG-SQL\CITRIX 
						 
						null_resource.CreateCVADDBs: Still creating... [40s elapsed] 
						null_resource.CreateCVADDBs: Still creating... [50s elapsed] 
						null_resource.CreateCVADDBs: Still creating... [1m0s elapsed] 
						null_resource.CreateCVADDBs: Still creating... [1m10s elapsed] 
						null_resource.CreateCVADDBs: Still creating... [1m20s elapsed] 
						null_resource.CreateCVADDBs: Still creating... [1m30s elapsed] 
						null_resource.CreateCVADDBs: Still creating... [1m40s elapsed] 
						null_resource.CreateCVADDBs: Still creating... [1m50s elapsed] 
						null_resource.CreateCVADDBs: Still creating... [2m0s elapsed] 
						null_resource.CreateCVADDBs: Still creating... [2m10s elapsed] 
						null_resource.CreateCVADDBs: Still creating... [2m20s elapsed] 
						null_resource.CreateCVADDBs: Still creating... [2m30s elapsed] 
						null_resource.CreateCVADDBs: Still creating... [2m40s elapsed] 
						null_resource.CreateCVADDBs: Still creating... [2m50s elapsed] 
						null_resource.CreateCVADDBs: Still creating... [3m0s elapsed] 
						null_resource.CreateCVADDBs (remote-exec): PSComputerName  : localhost 
						null_resource.CreateCVADDBs (remote-exec): RunspaceId      : b58da869-f872-4f87-b893-d034bc6f8cf5 
						null_resource.CreateCVADDBs (remote-exec): Controllers     : {Citrix.XenDesktopPowerShellSdk.ServiceInterfaces.Configuration.Controller} 
						null_resource.CreateCVADDBs (remote-exec): Databases       : {Site, Logging, Monitor} 
						null_resource.CreateCVADDBs (remote-exec): DefaultIconUid  : 1 
						null_resource.CreateCVADDBs (remote-exec): LicenseInformation : PLT 
						null_resource.CreateCVADDBs (remote-exec): Metadata        : {ConfiguredComponents, Studio_SiteConfigurationComplete, Citrix_StoreFront_Cluster_Id, 
						null_resource.CreateCVADDBs (remote-exec):                      Upgrade_PolicyPublishedNamesCompleted} 
						null_resource.CreateCVADDBs (remote-exec): Name            : TACG-TF-NTX 
						 
						 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;
					 

					
						null_resource.CreateCVADDBs: Creation complete after 3m5s [id=2656649310424837110] 
						Apply complete. Resources: 25 added, 0 changed, 0 destroyed.
					 

					
						PS C:\_TACG\_CVADOnNutanix\_CVADOnNutanix-Configuration&gt;
					 
				
			
		
	

	
		 
		After running all scripts, the Citrix Virtual Apps and Desktop software components are installed: 
		
	 

	
		 
		The Databases are created: 
		
	 

	
		 
		This configuration completes the creation and configuration of all initial resources:
	 

	
		
			Installing the needed software on the Delivery Controllers
		
		
			Creating a Citrix Virtual Apps and Desktop site
		
		
			Creating all needed Databases
		
		
			Installing Citrix StoreFront
		
	

	
		The environment is now ready to configure Citrix StoreFront.
	 

	
		 
	 

	
		Module 3: Configuring Citrix StoreFront
	

	
		Terraform installed the StoreFront software in the previous module but did not create a StoreFront cluster that meets our needs.
	

	
		
	 

	
		Because of that, the first script in Module 3 purges all existing StoreFront deployments on the 2 Delivery Controllers to have a clear environment for deploying the StoreFront cluster.
	 

	
		The current version of the Citrix Terraform Provider has limited functionality regarding a StoreFront deployment. If you need further StoreFront configuration, please use the StoreFront configuration scripts Terraform created and uploaded to the Delivery Controllers in Module 2.
	 
	 

	
		Important:  
		 
		This module can only be run on the previously created Administrative VM, as the following modules rely heavily on WinRM. Please install Terraform on the Administrative VM before trying to run the scripts. 
		 
	 

	
		This module is split into the following configuration parts:
	 

	
		
			Configuring a StoreFront cluster on the primary Delivery Controller 
			 
		
	

	
		Important: 
		 
		Before starting the Terraform workflow, please ensure you have configured the variables according to your needs. 
		 
	 

	
		 
		The configuration can be started by following the standard Terraform workflow:
	 

	
		terraform init, 
		terraform plan 
		 
	 

	
		and if no errors occur
	 

	
		terraform apply
	 
	 
	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0200b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\_TACG\_CVADOnNutanix\_CVADOnNutanix-Configuration-Storefront&gt; terraform init 
						Initializing the backend... 
						Initializing provider plugins... 
						- Finding citrix/citrix versions matching "1.0.0"... 
						- Finding nutanix/nutanix versions matching "1.9.5"... 
						- Finding latest version of hashicorp/null... 
						- Finding latest version of hashicorp/local... 
						- Installing citrix/citrix v1.0.0... 
						- Installed citrix/citrix v1.0.0 (self-signed, key ID BD4BD0E690CB7D88) 
						- Installing nutanix/nutanix v1.9.5... 
						- Installed nutanix/nutanix v1.9.5 (signed by a HashiCorp partner, key ID BEA5F795571AD06E) 
						- Installing hashicorp/null v3.2.2... 
						- Installed hashicorp/null v3.2.2 (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\_CVADOnNutanix\_CVADOnNutanix-Configuration-Storefront&gt; terraform plan 
						 
						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_stf_authentication_service.TACG-SF-AuthenticationService will be created 
						  + resource "citrix_stf_authentication_service" "TACG-SF-AuthenticationService" { 
						   + claims_factory_name = "standardClaimsFactory" 
						   + friendly_name    = "Auth" 
						   + site_id          = "1" 
						   + virtual_path     = "/Citrix/Authentication" 
						} 
						 
						  # citrix_stf_deployment.TACG-SF-InitialDeployment will be created 
						  + resource "citrix_stf_deployment" "TACG-SF-InitialDeployment" { 
						   + host_base_url = "https://storefront-ntx.the-austrian-citrix-guy.at" 
						   + site_id    = "1" 
						} 
						 
						  # citrix_stf_store_service.TACG-SF-StoreService will be created 
						  + resource "citrix_stf_store_service" "TACG-SF-StoreService" { 
						   + authentication_service_virtual_path = "/Citrix/Authentication" 
						   + farms                            = [ 
						       + { 
						           + all_failed_bypass_duration  = 0 
						           + bypass_duration             = 60 
						           + farm_name                   = "TACG-NTX-SF" 
						           + farm_type                   = "XenDesktop" 
						           + load_balance                = true 
						           + max_failed_servers_per_request = 0 
						           + port                        = 80 
						           + rade_ticket_time_to_live    = 100 
						           + server_urls                 = [] 
						           + servers                     = [ 
						               + "TACG-NTX-DDC1.the-austrian-citrix-guy.at", 
						               + "TACG-NTX-DDC2.the-austrian-citrix-guy.at", 
						             ] 
						           + ssl_relay_port              = 443 
						           + ticket_time_to_live         = 200 
						           + transport_type              = "HTTPS" 
						           + xml_validation_enabled      = false 
						           + zones                       = [ 
						               + "Primary", 
						             ] 
						             # (4 unchanged attributes hidden) 
						         }, 
						     ] 
						   + friendly_name                    = "Store" 
						   + pna                              = { 
						       + enable = true 
						     } 
						   + site_id                          = "1" 
						   + virtual_path                     = "/Citrix/Store" 
						} 
						 
						  # citrix_stf_webreceiver_service.TACG-SF-WebReceiverService will be created 
						  + resource "citrix_stf_webreceiver_service" "TACG-SF-WebReceiverService" { 
						   + friendly_name   = "ReceiverWeb" 
						   + site_id         = "1" 
						   + store_virtual_path = "/Citrix/Store" 
						   + virtual_path    = "/Citrix/StoreWeb" 
						} 
						 
						  # local_file.StoreFrontDeletionScriptIntoDataDirectory will be created 
						  + resource "local_file" "StoreFrontDeletionScriptIntoDataDirectory" { 
						   + content           = &lt;&lt;-EOT 
						         $PSUsername = 'XXXXXXXXXXXXXXXX' 
						         $PSPassword = '.XXXXXXXXXXXXXXXX..' 
						         $PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force 
						         $PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword) 
						         Invoke-Command -ComputerName localhost -Credential $PSCredential -ScriptBlock { 
						         Add-PSSnapin Citrix.* 
						         #Clear-Existing StoreFront deployments 
						         Clear-STFDeployment -Confirm:$false 
						         Write-Output 'Existing StoreFront deployments deleted.' 
						         } 
						     EOT 
						   + 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          = "./data/SF-Deletion.ps1" 
						   + id                = (known after apply) 
						} 
						 
						  # null_resource.UploadAndRunSFDeletionScriptOnDDC1 will be created 
						  + resource "null_resource" "UploadAndRunSFDeletionScriptOnDDC1" { 
						   + id = (known after apply) 
						}
					 

					
						  # null_resource.UploadAndRunSFDeletionScriptOnDDC2 will be created 
						  + resource "null_resource" "UploadAndRunSFDeletionScriptOnDDC1" { 
						   + id = (known after apply) 
						} 
						 
						Plan: 7 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\_CVADOnNutanix\_CVADOnNutanix-Configuration-Storefront&gt; terraform validate 
						Success. The configuration is valid. 
						 
						PS C:\_TACG\_CVADOnNutanix\_CVADOnNutanix-Configuration-Storefront&gt; terraform apply 
						 
						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_stf_authentication_service.TACG-SF-AuthenticationService will be created 
						  + resource "citrix_stf_authentication_service" "TACG-SF-AuthenticationService" { 
						   + claims_factory_name = "standardClaimsFactory" 
						   + friendly_name    = "Auth" 
						   + site_id          = "1" 
						   + virtual_path     = "/Citrix/Authentication" 
						} 
						 
						  # citrix_stf_deployment.TACG-SF-InitialDeployment will be created 
						  + resource "citrix_stf_deployment" "TACG-SF-InitialDeployment" { 
						   + host_base_url = "https://storefront-ntx.the-austrian-citrix-guy.at" 
						   + site_id    = "1" 
						} 
						 
						  # citrix_stf_store_service.TACG-SF-StoreService will be created 
						  + resource "citrix_stf_store_service" "TACG-SF-StoreService" { 
						   + authentication_service_virtual_path = "/Citrix/Authentication" 
						   + farms                            = [ 
						       + { 
						           + all_failed_bypass_duration  = 0 
						           + bypass_duration             = 60 
						           + farm_name                   = "TACG-NTX-SF" 
						           + farm_type                   = "XenDesktop" 
						           + load_balance                = true 
						           + max_failed_servers_per_request = 0 
						           + port                        = 80 
						           + rade_ticket_time_to_live    = 100 
						           + server_urls                 = [] 
						           + servers                     = [ 
						               + "TACG-NTX-DDC1.the-austrian-citrix-guy.at", 
						               + "TACG-NTX-DDC2.the-austrian-citrix-guy.at", 
						             ] 
						           + ssl_relay_port              = 443 
						           + ticket_time_to_live         = 200 
						           + transport_type              = "HTTPS" 
						           + xml_validation_enabled      = false 
						           + zones                       = [ 
						               + "Primary", 
						             ] 
						             # (4 unchanged attributes hidden) 
						         }, 
						     ] 
						   + friendly_name                    = "Store" 
						   + pna                              = { 
						       + enable = true 
						     } 
						   + site_id                          = "1" 
						   + virtual_path                     = "/Citrix/Store" 
						} 
						 
						  # citrix_stf_webreceiver_service.TACG-SF-WebReceiverService will be created 
						  + resource "citrix_stf_webreceiver_service" "TACG-SF-WebReceiverService" { 
						   + friendly_name   = "ReceiverWeb" 
						   + site_id         = "1" 
						   + store_virtual_path = "/Citrix/Store" 
						   + virtual_path    = "/Citrix/StoreWeb" 
						} 
						 
						  # local_file.StoreFrontDeletionScriptIntoDataDirectory will be created 
						  + resource "local_file" "StoreFrontDeletionScriptIntoDataDirectory" { 
						   + content           = &lt;&lt;-EOT 
						         $PSUsername = 'XXXXXXXXXXXXXXXX' 
						         $PSPassword = '.XXXXXXXXXXXXXXXX..' 
						         $PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force 
						         $PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword) 
						         Invoke-Command -ComputerName localhost -Credential $PSCredential -ScriptBlock { 
						         Add-PSSnapin Citrix.* 
						         #Clear-Existing StoreFront deployments 
						         Clear-STFDeployment -Confirm:$false 
						         Write-Output 'Existing StoreFront deployments deleted.' 
						         } 
						     EOT 
						   + 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"                                                                                                                                                                                                                           + id                = (known after apply) 
						} 
						 
						  # null_resource.UploadAndRunSFDeletionScriptOnDDC1 will be created 
						  + resource "null_resource" "UploadAndRunSFDeletionScriptOnDDC1" { 
						   + id = (known after apply) 
						}
					 

					
						  # null_resource.UploadAndRunSFDeletionScriptOnDDC2 will be created 
						  + resource "null_resource" "UploadAndRunSFDeletionScriptOnDDC1" { 
						   + id = (known after apply) 
						} 
						 
						Plan: 7 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 
						 
						local_file.StoreFrontDeletionScriptIntoDataDirectory: Creating... 
						local_file.StoreFrontDeletionScriptIntoDataDirectory: Creation complete after 0s [id=8c0fc21cb5266c2f9fed8620f2bd1c92c0df016a] 
						null_resource.UploadAndRunSFDeletionScriptOnDDC1: Creating... 
						null_resource.UploadAndRunSFDeletionScriptOnDDC1: Provisioning with 'file'... 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadAndRunSFDeletionScriptOnDDC1: Provisioning with 'remote-exec'... 
						null_resource.UploadAndRunSFDeletionScriptOnDDC1 (remote-exec): Connecting to remote host via WinRM... 
						null_resource.UploadAndRunSFDeletionScriptOnDDC1 (remote-exec):   Host: 10.10.175.1 
						null_resource.UploadAndRunSFDeletionScriptOnDDC1 (remote-exec):   Port: 5985 
						null_resource.UploadAndRunSFDeletionScriptOnDDC1 (remote-exec):   User: administrator 
						null_resource.UploadAndRunSFDeletionScriptOnDDC1 (remote-exec):   Password: true 
						null_resource.UploadAndRunSFDeletionScriptOnDDC1 (remote-exec):   HTTPS: false 
						null_resource.UploadAndRunSFDeletionScriptOnDDC1 (remote-exec):   Insecure: false 
						null_resource.UploadAndRunSFDeletionScriptOnDDC1 (remote-exec):   NTLM: false 
						null_resource.UploadAndRunSFDeletionScriptOnDDC1 (remote-exec):   CACert: false 
						null_resource.UploadAndRunSFDeletionScriptOnDDC1 (remote-exec): Connected. 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
						null_resource.UploadAndRunSFDeletionScriptOnDDC1 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/SF-Deletion.ps1 
						null_resource.UploadAndRunSFDeletionScriptOnDDC1: Still creating... [10s elapsed] 
						null_resource.UploadAndRunSFDeletionScriptOnDDC1 (remote-exec): Existing StoreFront deployments deleted. 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadAndRunSFDeletionScriptOnDDC1: Creation complete after 16s [id=7353090162724962384] 
						null_resource.UploadAndRunSFDeletionScriptOnDDC2: Creating... 
						null_resource.UploadAndRunSFDeletionScriptOnDDC2: Provisioning with 'file'... 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadAndRunSFDeletionScriptOnDDC2: Provisioning with 'remote-exec'... 
						null_resource.UploadAndRunSFDeletionScriptOnDDC2 (remote-exec): Connecting to remote host via WinRM... 
						null_resource.UploadAndRunSFDeletionScriptOnDDC2 (remote-exec):   Host: 10.10.175.2 
						null_resource.UploadAndRunSFDeletionScriptOnDDC2 (remote-exec):   Port: 5985 
						null_resource.UploadAndRunSFDeletionScriptOnDDC2 (remote-exec):   User: administrator 
						null_resource.UploadAndRunSFDeletionScriptOnDDC2 (remote-exec):   Password: true 
						null_resource.UploadAndRunSFDeletionScriptOnDDC2 (remote-exec):   HTTPS: false 
						null_resource.UploadAndRunSFDeletionScriptOnDDC2 (remote-exec):   Insecure: false 
						null_resource.UploadAndRunSFDeletionScriptOnDDC2 (remote-exec):   NTLM: false 
						null_resource.UploadAndRunSFDeletionScriptOnDDC2 (remote-exec):   CACert: false 
						null_resource.UploadAndRunSFDeletionScriptOnDDC2 (remote-exec): Connected. 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
						null_resource.UploadAndRunSFDeletionScriptOnDDC2 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/SF-Deletion.ps1 
						null_resource.UploadAndRunSFDeletionScriptOnDDC2: Still creating... [10s elapsed] 
						null_resource.UploadAndRunSFDeletionScriptOnDDC2 (remote-exec): Existing StoreFront deployments deleted. 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadAndRunSFDeletionScriptOnDDC2: Creation complete after 16s [id=39540223320194421985] 
						 
						citrix_stf_deployment.TACG-SF-InitialDeployment: Creating... 
						citrix_stf_deployment.TACG-SF-InitialDeployment: Still creating... [10s elapsed] 
						citrix_stf_deployment.TACG-SF-InitialDeployment: Still creating... [20s elapsed] 
						citrix_stf_deployment.TACG-SF-InitialDeployment: Creation complete after 23s 
						citrix_stf_authentication_service.TACG-SF-AuthenticationService: Creating... 
						citrix_stf_authentication_service.TACG-SF-AuthenticationService: Creation complete after 5s 
						citrix_stf_store_service.TACG-SF-StoreService: Creating... 
						citrix_stf_store_service.TACG-SF-StoreService: Still creating... [10s elapsed] 
						citrix_stf_store_service.TACG-SF-StoreService: Creation complete after 15s 
						citrix_stf_webreceiver_service.TACG-SF-WebReceiverService: Creating... 
						citrix_stf_webreceiver_service.TACG-SF-WebReceiverService: Creation complete after 3s 
						 
						Apply complete. Resources: 7 added, 0 changed, 0 destroyed. 
						PS C:\_TACG\_CVADOnNutanix\_CVADOnNutanix-Configuration-Storefront&gt;
					 
				
			
		
	

	
		 
		Terraform successfully deployed and configured the StoreFront cluster: 
		
	 

	
		The Terraform flow was completed successfully, and the next module can begin. 
		 
	 

	
		Module 4:  Install and Configure further needs on Nutanix AHV 
		 
		After creating and configuring the StoreFront cluster, some final configuration steps are needed before we can deploy the Citrix Virtual Apps and Desktops-related entities in Module 5. 
		Before running the Terraform scripts of Module 4, IIS has a self-signed certificate installed, and the Citrix Virtual Apps and Desktops site has no valid Licenses installed:
	

	
		
	 

	
		 
	 

	
		The certificate is needed in .pfx format, and the correct License file must reside in this module's DATA directory. 
		
	 

	
		The corresponding Terraform file containing all needed variables point to the DATA directory.
	 
	 

	
		Important:  
		 
		This module can only be run on the previously created Administrative VM, as the following modules rely heavily on WinRM.  
		Please install Terraform on the Administrative VM before trying to run the scripts. 
		 
	 

	
		This module is split into the following configuration parts:
	 

	
		
			Changing the SSL certificate on the Delivery Controllers/StoreFront servers to a publicly signed certificate to avoid certificate issues
		
		
			Uploading a valid license file to a License Server locally installed on the Primary Delivery Controller (optional)
		
		
			Setting the License Configuration of the site to point to a central License Server (optional)
		
	
	 

	
		The configuration can be started by following the standard Terraform workflow:
	 

	
		terraform init, 
		terraform plan 
		 
	 

	
		and if no errors occur
	 

	
		terraform apply
	 

	
		 
		
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0200b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}		
	 

	
		
			
				
					
						PS C:\_TACG\_CVADOnNutanix\_CVADOnNutanix-Configuration-AfterStoreFront&gt; terraform init 
						Initializing the backend... 
						Initializing provider plugins... 
						- Finding citrix/citrix versions matching "1.0.0"... 
						- Finding latest version of hashicorp/local... 
						- Finding latest version of hashicorp/null... 
						- Installing citrix/citrix v1.0.0... 
						- Installed citrix/citrix v1.0.0 (self-signed, key ID BD4BD0E690CB7D88) 
						- 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) 
						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\_CVADOnNutanix\_CVADOnNutanix-Configuration-AfterStoreFront&gt; terraform plan 
						 
						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.WriteIISSSLScriptIntoDataDirectory will be created 
						  + resource "local_file" "WriteIISSSLScriptIntoDataDirectory" { 
						   + 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          = "./data/CVAD-IIS.ps1" 
						   + id                = (known after apply) 
						} 
						 
						  # local_file.WriteLicScriptIntoDataDirectory will be created 
						  + resource "local_file" "WriteLicScriptIntoDataDirectory" { 
						   + 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          = "./data/CVAD-Lic.ps1" 
						   + id                = (known after apply) 
						} 
						 
						  # local_file.WritePFXScriptIntoDataDirectory will be created 
						  + resource "local_file" "WritePFXScriptIntoDataDirectory" { 
						   + 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          = "./data/CVAD-PFX.ps1" 
						   + id                = (known after apply) 
						} 
						 
						  # null_resource.ChangeSSLOnDDC1 will be created 
						  + resource "null_resource" "ChangeSSLOnDDC1" { 
						   + id = (known after apply) 
						} 
						 
						  # null_resource.ChangeSSLOnDDC2 will be created 
						  + resource "null_resource" "ChangeSSLOnDDC2" { 
						   + id = (known after apply) 
						} 
						 
						  # null_resource.CreateCVADLicPFXOnDDC1 will be created 
						  + resource "null_resource" "CreateCVADLicPFXOnDDC1" { 
						   + id = (known after apply) 
						} 
						 
						  # null_resource.CreateCVADPFXOnDDC2 will be created 
						  + resource "null_resource" "CreateCVADPFXOnDDC2" { 
						   + id = (known after apply) 
						} 
						 
						  # null_resource.RebootDDC1-2 will be created 
						  + resource "null_resource" "RebootDDC1-2" { 
						   + id = (known after apply) 
						} 
						 
						  # null_resource.RebootDDC2-2 will be created 
						  + resource "null_resource" "RebootDDC2-2" { 
						   + id = (known after apply) 
						} 
						 
						Plan: 9 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\_CVADOnNutanix\_CVADOnNutanix-Configuration-AfterStoreFront&gt; terraform validate 
						Success. The configuration is valid. 
						 
						PS C:\_TACG\_CVADOnNutanix\_CVADOnNutanix-Configuration-AfterStoreFront&gt; terraform apply 
						 
						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.WriteIISSSLScriptIntoDataDirectory will be created 
						  + resource "local_file" "WriteIISSSLScriptIntoDataDirectory" { 
						   + 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          = "./data/CVAD-IIS.ps1" 
						   + id                = (known after apply) 
						} 
						 
						  # local_file.WriteLicScriptIntoDataDirectory will be created 
						  + resource "local_file" "WriteLicScriptIntoDataDirectory" { 
						   + 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          = "./data/CVAD-Lic.ps1" 
						   + id                = (known after apply) 
						} 
						 
						  # local_file.WritePFXScriptIntoDataDirectory will be created 
						  + resource "local_file" "WritePFXScriptIntoDataDirectory" { 
						   + 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          = "./data/CVAD-PFX.ps1" 
						   + id                = (known after apply) 
						} 
						 
						  # null_resource.ChangeSSLOnDDC1 will be created 
						  + resource "null_resource" "ChangeSSLOnDDC1" { 
						   + id = (known after apply) 
						} 
						 
						  # null_resource.ChangeSSLOnDDC2 will be created 
						  + resource "null_resource" "ChangeSSLOnDDC2" { 
						   + id = (known after apply) 
						} 
						 
						  # null_resource.CreateCVADLicPFXOnDDC1 will be created 
						  + resource "null_resource" "CreateCVADLicPFXOnDDC1" { 
						   + id = (known after apply) 
						} 
						 
						  # null_resource.CreateCVADPFXOnDDC2 will be created 
						  + resource "null_resource" "CreateCVADPFXOnDDC2" { 
						   + id = (known after apply) 
						} 
						 
						  # null_resource.RebootDDC1-2 will be created 
						  + resource "null_resource" "RebootDDC1-2" { 
						   + id = (known after apply) 
						} 
						 
						  # null_resource.RebootDDC2-2 will be created 
						  + resource "null_resource" "RebootDDC2-2" { 
						   + id = (known after apply) 
						} 
						 
						Plan: 9 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 
						 
						local_file.WriteLicScriptIntoDataDirectory: Creating... 
						local_file.WritePFXScriptIntoDataDirectory: Creating... 
						local_file.WriteIISSSLScriptIntoDataDirectory: Creating... 
						local_file.WritePFXScriptIntoDataDirectory: Creation complete after 0s [id=63683eea6a0c276da17943ff455714f492a090d3] 
						local_file.WriteLicScriptIntoDataDirectory: Creation complete after 0s [id=44f278fa7312844bf7d7434c1b8b27b940c0132c] 
						local_file.WriteIISSSLScriptIntoDataDirectory: Creation complete after 0s [id=86bab8825d5d9717e524218d8d52d3eab2c70796] 
						null_resource.CreateCVADPFXOnDDC2: Creating... 
						null_resource.CreateCVADLicPFXOnDDC1: Creating... 
						null_resource.CreateCVADPFXOnDDC2: Provisioning with 'file'... 
						null_resource.CreateCVADLicPFXOnDDC1: Provisioning with 'file'... 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.CreateCVADLicPFXOnDDC1: Provisioning with 'file'... 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.CreateCVADPFXOnDDC2: Provisioning with 'file'... 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.CreateCVADLicPFXOnDDC1: Provisioning with 'file'... 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.CreateCVADPFXOnDDC2: Provisioning with 'file'... 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.CreateCVADLicPFXOnDDC1: Provisioning with 'file'... 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.CreateCVADPFXOnDDC2: Provisioning with 'remote-exec'... 
						null_resource.CreateCVADPFXOnDDC2 (remote-exec): Connecting to remote host via WinRM... 
						null_resource.CreateCVADPFXOnDDC2 (remote-exec):   Host: 10.10.175.2 
						null_resource.CreateCVADPFXOnDDC2 (remote-exec):   Port: 5985 
						null_resource.CreateCVADPFXOnDDC2 (remote-exec):   User: administrator 
						null_resource.CreateCVADPFXOnDDC2 (remote-exec):   Password: true 
						null_resource.CreateCVADPFXOnDDC2 (remote-exec):   HTTPS: false 
						null_resource.CreateCVADPFXOnDDC2 (remote-exec):   Insecure: false 
						null_resource.CreateCVADPFXOnDDC2 (remote-exec):   NTLM: false 
						null_resource.CreateCVADPFXOnDDC2 (remote-exec):   CACert: false 
						null_resource.CreateCVADPFXOnDDC2 (remote-exec): Connected. 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.CreateCVADLicPFXOnDDC1: Provisioning with 'remote-exec'... 
						null_resource.CreateCVADLicPFXOnDDC1 (remote-exec): Connecting to remote host via WinRM... 
						null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   Host: 10.10.175.1 
						null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   Port: 5985 
						null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   User: administrator 
						null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   Password: true 
						null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   HTTPS: false 
						null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   Insecure: false 
						null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   NTLM: false 
						null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   CACert: false 
						null_resource.CreateCVADLicPFXOnDDC1 (remote-exec): Connected. 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
						null_resource.CreateCVADPFXOnDDC2 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/CVAD-PFX.ps1 
						 
						null_resource.CreateCVADPFXOnDDC2 (remote-exec): PSParentPath: Microsoft.PowerShell.Security\Certificate::LocalMachine\Webhosting 
						 
						null_resource.CreateCVADPFXOnDDC2 (remote-exec): Thumbprint                             Subject                             PSComputerName 
						null_resource.CreateCVADPFXOnDDC2 (remote-exec): ----------                             -------                             -------------- 
						null_resource.CreateCVADPFXOnDDC2 (remote-exec): AB4CA8AC77068368F4B41AD650858D07679DD709  CN=*.the-austrian-citrix-guy.at        localhost 
						 
						 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.CreateCVADLicPFXOnDDC1: Provisioning with 'remote-exec'... 
						null_resource.CreateCVADLicPFXOnDDC1 (remote-exec): Connecting to remote host via WinRM... 
						null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   Host: 10.10.175.1 
						null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   Port: 5985 
						null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   User: administrator 
						null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   Password: true 
						null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   HTTPS: false 
						null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   Insecure: false 
						null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   NTLM: false 
						null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   CACert: false 
						null_resource.CreateCVADLicPFXOnDDC1 (remote-exec): Connected. 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.CreateCVADPFXOnDDC2: Creation complete after 6s [id=6486333108724984074] 
						null_resource.ChangeSSLOnDDC2: Creating... 
						null_resource.ChangeSSLOnDDC2: Provisioning with 'remote-exec'... 
						null_resource.ChangeSSLOnDDC2 (remote-exec): Connecting to remote host via WinRM... 
						null_resource.ChangeSSLOnDDC2 (remote-exec):   Host: 10.10.175.2 
						null_resource.ChangeSSLOnDDC2 (remote-exec):   Port: 5985 
						null_resource.ChangeSSLOnDDC2 (remote-exec):   User: administrator 
						null_resource.ChangeSSLOnDDC2 (remote-exec):   Password: true 
						null_resource.ChangeSSLOnDDC2 (remote-exec):   HTTPS: false 
						null_resource.ChangeSSLOnDDC2 (remote-exec):   Insecure: false 
						null_resource.ChangeSSLOnDDC2 (remote-exec):   NTLM: false 
						null_resource.ChangeSSLOnDDC2 (remote-exec):   CACert: false 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.ChangeSSLOnDDC2 (remote-exec): Connected. 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
						null_resource.CreateCVADLicPFXOnDDC1 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/CVAD-PFX.ps1 
						 
						null_resource.ChangeSSLOnDDC2 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/CVAD-IIS.ps1 
						 
						 
						null_resource.CreateCVADLicPFXOnDDC1 (remote-exec): PSParentPath: Microsoft.PowerShell.Security\Certificate::LocalMachine\Webhosting 
						 
						null_resource.CreateCVADLicPFXOnDDC1 (remote-exec): Thumbprint                             Subject                             PSComputerName 
						null_resource.CreateCVADLicPFXOnDDC1 (remote-exec): ----------                             -------                             -------------- 
						null_resource.CreateCVADLicPFXOnDDC1 (remote-exec): AB4CA8AC77068368F4B41AD650858D07679DD709  CN=*.the-austrian-citrix-guy.at        localhost 
						 
						 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.CreateCVADLicPFXOnDDC1: Creation complete after 9s [id=3446257476204133808] 
						null_resource.ChangeSSLOnDDC1: Creating... 
						null_resource.ChangeSSLOnDDC1: Provisioning with 'remote-exec'... 
						null_resource.ChangeSSLOnDDC1 (remote-exec): Connecting to remote host via WinRM... 
						null_resource.ChangeSSLOnDDC1 (remote-exec):   Host: 10.10.175.1 
						null_resource.ChangeSSLOnDDC1 (remote-exec):   Port: 5985 
						null_resource.ChangeSSLOnDDC1 (remote-exec):   User: administrator 
						null_resource.ChangeSSLOnDDC1 (remote-exec):   Password: true 
						null_resource.ChangeSSLOnDDC1 (remote-exec):   HTTPS: false 
						null_resource.ChangeSSLOnDDC1 (remote-exec):   Insecure: false 
						null_resource.ChangeSSLOnDDC1 (remote-exec):   NTLM: false 
						null_resource.ChangeSSLOnDDC1 (remote-exec):   CACert: false 
						null_resource.ChangeSSLOnDDC1 (remote-exec): Connected. 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
						null_resource.ChangeSSLOnDDC1 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/CVAD-IIS.ps1 
						null_resource.ChangeSSLOnDDC2: Still creating... [10s elapsed] 
						 
						null_resource.ChangeSSLOnDDC2 (remote-exec): Attempting stop... 
						 
						null_resource.ChangeSSLOnDDC2 (remote-exec): Internet services successfully stopped 
						 
						null_resource.ChangeSSLOnDDC2 (remote-exec): Attempting start... 
						 
						null_resource.ChangeSSLOnDDC2 (remote-exec): Internet services successfully restarted 
						 
						null_resource.ChangeSSLOnDDC1: Still creating... [10s elapsed] 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.ChangeSSLOnDDC2: Creation complete after 13s [id=2896093573335811323] 
						null_resource.RebootDDC2-2: Creating... 
						null_resource.RebootDDC2-2: Provisioning with 'remote-exec'... 
						null_resource.RebootDDC2-2 (remote-exec): Connecting to remote host via WinRM... 
						null_resource.RebootDDC2-2 (remote-exec):   Host: 10.10.175.2 
						null_resource.RebootDDC2-2 (remote-exec):   Port: 5985 
						null_resource.RebootDDC2-2 (remote-exec):   User: administrator 
						null_resource.RebootDDC2-2 (remote-exec):   Password: true 
						null_resource.RebootDDC2-2 (remote-exec):   HTTPS: false 
						null_resource.RebootDDC2-2 (remote-exec):   Insecure: false 
						null_resource.RebootDDC2-2 (remote-exec):   NTLM: false 
						null_resource.RebootDDC2-2 (remote-exec):   CACert: false 
						null_resource.RebootDDC2-2 (remote-exec): Connected. 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
						null_resource.RebootDDC2-2 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/Reboot.ps1 
						null_resource.RebootDDC2-2: Creation complete after 1s [id=7924398126775855648] 
						 
						null_resource.ChangeSSLOnDDC1 (remote-exec): Attempting stop... 
						 
						null_resource.ChangeSSLOnDDC1 (remote-exec): Internet services successfully stopped 
						 
						null_resource.ChangeSSLOnDDC1 (remote-exec): Attempting start... 
						 
						null_resource.ChangeSSLOnDDC1 (remote-exec): Internet services successfully restarted 
						 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.ChangeSSLOnDDC1: Creation complete after 13s [id=7676343894695474410] 
						null_resource.RebootDDC1-2: Creating... 
						null_resource.RebootDDC1-2: Provisioning with 'remote-exec'... 
						null_resource.RebootDDC1-2 (remote-exec): Connecting to remote host via WinRM... 
						null_resource.RebootDDC1-2 (remote-exec):   Host: 10.10.175.1 
						null_resource.RebootDDC1-2 (remote-exec):   Port: 5985 
						null_resource.RebootDDC1-2 (remote-exec):   User: administrator 
						null_resource.RebootDDC1-2 (remote-exec):   Password: true 
						null_resource.RebootDDC1-2 (remote-exec):   HTTPS: false 
						null_resource.RebootDDC1-2 (remote-exec):   Insecure: false 
						null_resource.RebootDDC1-2 (remote-exec):   NTLM: false 
						null_resource.RebootDDC1-2 (remote-exec):   CACert: false 
						null_resource.RebootDDC1-2 (remote-exec): Connected. 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
						null_resource.RebootDDC1-2 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/Reboot.ps1 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.RebootDDC1-2: Creation complete after 1s [id=8001060474665531508] 
						 
						Apply complete. Resources: 9 added, 0 changed, 0 destroyed. 
						PS C:\_TACG\_CVADOnNutanix\_CVADOnNutanix-Configuration-AfterStoreFront&gt;
					 
				
			
		
	

	
		 
		Terraform has successfully uploaded the public-signed SSL certificate and bound it to IIS: 
		
	 

	
		 
		 
		 
		Terraform has successfully configured a central Citrix License Server for the site: 
		
	 

	
		
	 

	
		The Terraform flow was completed successfully, and the next module can begin.
	 
	 

	
		Module 5: Creating all needed CVAD-related Entities
	

	
		In this module, Terraform finally creates all the entities needed:
	 

	
		
			Uploading and installing the Nutanix AHV Plug-In on the Delivery Controllers
		
		
			Creating a dedicated Hypervisor Connection to Nutanix AHV
		
		
			Creating a dedicated Hypervisor Resource Pool
		
		
			Creating a Machine Catalog based on the referenced Master Image
		
		
			Creating a Delivery Group
		
		
			Creating AutoScale settings and applying these to the newly created Delivery Group
		
	
	 

	
		The Nutanix AHV Plug-In for Citrix allows the Delivery Controllers to manage workloads running on AHV.  
		We need to install it on all Delivery Controllers in the instance where AHV is deployed.
	 

	
		As we want an uninterrupted flow, we must install the Plug-In silently. Therefore, Terraform downloads the Plug-In from Nutanix and afterward, a PowerShell Script to the Delivery Controllers and executes the script using WinRM. 
	 

	
		The command line is quite lengthy  – please be sure to use the correct syntax:
	 

	
		
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0200b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}		
	 

	
		
			
				
					
						cmd /c 'msiexec /i "c:\temp\xdinst\DATA\NutanixAHV_Citrix_plugin.msi" /qn /l*v "c:\temp\xdinst\DATA\NutanixAHVPlugin.log" ALLUSERS="1" IAGREE="Yes" PLUGININSTALLPATH="C:\Program Files\Common Files\Citrix\HCLPlugins\CitrixMachineCreation\v1.0.0.0\" INSTALLFOLDER="C:\Program Files\Common Files\Citrix\HCLPlugins\CitrixMachineCreation\v1.0.0.0\NutanixAHV\" PVSINSTALLFOLDER="C:\Program Files\Common Files\Citrix\HCLPlugins\CitrixMachineCreation\v1.0.0.0\NutanixAHV\" REGISTERPLUGINSTOREPATH="C:\Program Files\Common Files\Citrix\HCLPlugins\CitrixMachineCreation\v1.0.0.0\" ADDLOCAL="F7_9_INSTALLFOLDER" INSTALLERTYPE="mcs"'
					 
				
			
		
	

	
		Before running Module 5, no Plug-in is installed on the Delivery Controller VMs: 
		
	 

	
		 
		Before running Terraform, no Terraform-related entities in the Virtual Apps and Desktops site are available. 
		
	 

	
		
	 

	
		
	 

	
		 
	 

	
		Important: 
		 
		Before starting the Terraform workflow, please ensure you have configured the variables according to your needs. 
		 
	 

	
		The configuration can be started by following the standard Terraform workflow:
	 

	
		terraform init, 
		terraform plan 
		 
	 

	
		and if no errors occur
	 

	
		terraform apply
	 

	
		The Terraform configuration contains some idle time slots to ensure that background operations can be completed before the following configuration steps occur. 
		We have seen different elapsed configuration times related to varying loads on Nutanix AHV. 
	 

	
		
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0200b0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}		
	 

	
		
			
				
					
						PS C:\_TACG\_CVADOnNutanix\_CVADOnNutanix-CVAD-Entities&gt; terraform init 
						Initializing the backend... 
						Initializing provider plugins... 
						- Finding latest version of hashicorp/null... 
						- Finding latest version of hashicorp/time... 
						- Finding citrix/citrix versions matching "1.0.0"... 
						- Finding latest version of hashicorp/local... 
						- Installing hashicorp/null v3.2.2... 
						- Installed hashicorp/null v3.2.2 (signed by HashiCorp) 
						- Installing hashicorp/time v0.12.0... 
						- Installed hashicorp/time v0.12.0 (signed by HashiCorp) 
						- Installing citrix/citrix v1.0.0... 
						- Installed citrix/citrix v1.0.0 (self-signed, key ID BD4BD0E690CB7D88) 
						- 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\_CVADOnNutanix\_CVADOnNutanix-CVAD-Entities&gt; terraform plan 
						 
						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       = [ 
						                   + "Friday", 
						                   + "Monday", 
						                   + "Thursday", 
						                   + "Tuesday", 
						                   + "Wednesday", 
						                 ] 
						               + display_name       = "TACG-TF-CVAD-NTX-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 
						             }, 
						         ] 
						     } 
						   + description              = "" 
						   + desktops                 = [ 
						       + { 
						           + description          = "Terraform-based Delivery Group running on Nutanix" 
						           + enable_session_roaming  = true 
						           + enabled              = true 
						           + published_name       = "DG-TACG-TF-CVAD-NTX" 
						           + restricted_access_users = { 
						               + allow_list = [ 
						                   + "TACG\\vdaallowed", 
						                 ] 
						             } 
						         }, 
						     ] 
						   + id                       = (known after apply) 
						   + minimum_functional_level = "L7_20" 
						   + name                     = "DG-TACG-TF-CVAD-NTX" 
						   + reboot_schedules         = [ 
						       + { 
						           + days_in_week         = [ 
						               + "Sunday", 
						             ] 
						           + frequency            = "Weekly" 
						           + frequency_factor     = 1 
						           + ignore_maintenance_mode = true 
						           + name                 = "TACG-NTX-Reboot Schedule" 
						           + natural_reboot_schedule = false 
						           + reboot_duration_minutes = 0 
						           + reboot_schedule_enabled = true 
						           + start_date           = "2024-01-01" 
						           + start_time           = "02:00" 
						             # (1 unchanged attribute hidden) 
						         }, 
						     ] 
						   + restricted_access_users  = { 
						       + allow_list = [ 
						           + "TACG\\vdaallowed", 
						         ] 
						     } 
						   + scopes                   = [] 
						   + total_machines           = (known after apply) 
						} 
						 
						  # citrix_machine_catalog.CreateNTXMCSCatalog will be created 
						  + resource "citrix_machine_catalog" "CreateNTXMCSCatalog" { 
						   + allocation_type       = "Random" 
						   + description           = "Terraform-based Machine Catalog" 
						   + id                    = (known after apply) 
						   + minimum_functional_level = "L7_20" 
						   + name                  = "MC-TACG-TF-CVAD-NUTANIX" 
						   + provisioning_scheme   = { 
						       + hypervisor                  = (known after apply) 
						       + hypervisor_resource_pool    = (known after apply) 
						       + identity_type               = "ActiveDirectory" 
						       + machine_account_creation_rules = { 
						           + naming_scheme   = "TACG-TF-NTX-C#" 
						           + naming_scheme_type = "Numeric" 
						         } 
						       + machine_domain_identity     = { 
						           + domain                = "the-austrian-citrix-guy.at" 
						           + domain_ou             = "CN=Computers,DC=the-austrian-citrix-guy,DC=at" 
						           + service_account       = "Administrator" 
						           + service_account_password = (sensitive value) 
						         } 
						       + number_of_total_machines    = 1 
						       + nutanix_machine_config      = { 
						           + container        = "default-container-36052048168163" 
						           + cores_per_cpu_count = 2 
						           + cpu_count        = 2 
						           + master_image     = "mi" 
						           + master_image_note   = "" 
						           + memory_mb        = 4096 
						         } 
						     } 
						   + provisioning_type     = "MCS" 
						   + scopes                = [] 
						   + session_support       = "MultiSession" 
						   + zone                  = (known after apply) 
						} 
						 
						  # citrix_nutanix_hypervisor.CreateHypervisorConnection will be created 
						  + resource "citrix_nutanix_hypervisor" "CreateHypervisorConnection" { 
						   + addresses                             = [ 
						       + "nutanix.the-austrian-citrix-guy.at", 
						     ] 
						   + id                                    = (known after apply) 
						   + max_absolute_active_actions           = 100 
						   + max_absolute_new_actions_per_minute   = 10 
						   + max_power_actions_percentage_of_machines = 20 
						   + name                                  = "TACG-TF-CVAD-Nutanix-HypConn" 
						   + password                              = (sensitive value) 
						   + password_format                       = "PlainText" 
						   + scopes                                = [] 
						   + username                              = "admin" 
						   + zone                                  = (known after apply) 
						} 
						 
						  # citrix_nutanix_hypervisor_resource_pool.CreateHypervisorPool will be created 
						  + resource "citrix_nutanix_hypervisor_resource_pool" "CreateHypervisorPool" { 
						   + hypervisor = (known after apply) 
						   + id      = (known after apply) 
						   + name    = "TACG-TF-CVAD-Nutanix-HypConnPool" 
						   + networks   = [ 
						       + "NW-NTX-TACG", 
						     ] 
						} 
						 
						  # citrix_zone.TACG-TF-HYP-Zone will be created 
						  + resource "citrix_zone" "TACG-TF-HYP-Zone" { 
						   + description       = (known after apply) 
						   + id                = (known after apply) 
						   + name              = "TACG-TF-CVAD-NTX-ZONE" 
						   + resource_location_id = (known after apply) 
						} 
						 
						  # local_file.DownloadNTNXPluginInstallationScriptToAVM will be created 
						  + resource "local_file" "DownloadNTNXPluginInstallationScriptToAVM" { 
						   + 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/InstallNutanixAHVPlugInOnCC.ps1" 
						   + id                = (known after apply) 
						} 
						 
						  # local_file.DownloadNTNXPluginToAVM will be created 
						  + resource "local_file" "DownloadNTNXPluginToAVM" { 
						   + 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/DownloadNTNXPluginToAVM.ps1" 
						   + 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.ExecuteDownloadNTNXPluginInstallationScriptToAVM will be created 
						  + resource "null_resource" "ExecuteDownloadNTNXPluginInstallationScriptToAVM" { 
						   + id = (known after apply) 
						} 
						 
						  # null_resource.ExecuteDownloadNutanixAHVPlugInOnAVM will be created 
						  + resource "null_resource" "ExecuteDownloadNutanixAHVPlugInOnAVM" { 
						   + id = (known after apply) 
						} 
						 
						  # null_resource.UploadNutanixAHVToCC1 will be created 
						  + resource "null_resource" "UploadNutanixAHVToCC1" { 
						   + id = (known after apply) 
						} 
						 
						  # null_resource.UploadNutanixAHVToCC2 will be created 
						  + resource "null_resource" "UploadNutanixAHVToCC2" { 
						   + id = (known after apply) 
						} 
						 
						  # time_sleep.wait_30_seconds will be created 
						  + resource "time_sleep" "wait_30_seconds" { 
						   + create_duration = "30s" 
						   + id           = (known after apply) 
						} 
						 
						  # time_sleep.wait_30_seconds1 will be created 
						  + resource "time_sleep" "wait_30_seconds1" { 
						   + create_duration = "30s" 
						   + id           = (known after apply) 
						} 
						 
						  # time_sleep.wait_30_seconds_2 will be created 
						  + resource "time_sleep" "wait_30_seconds_2" { 
						   + create_duration = "30s" 
						   + id           = (known after apply) 
						} 
						 
						Plan: 16 to add, 0 to change, 0 to destroy. 
						 
						Changes to Outputs: 
						  + ZoneID = (known after apply) 
						 
						───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 
						 
						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\_CVADOnNutanix\_CVADOnNutanix-CVAD-Entities&gt; terraform validate 
						Success. The configuration is valid. 
						 
						PS C:\_TACG\_CVADOnNutanix\_CVADOnNutanix-CVAD-Entities&gt; terraform apply 
						 
						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.DownloadNTNXPluginInstallationScriptToAVM will be created 
						  + resource "local_file" "DownloadNTNXPluginInstallationScriptToAVM" { 
						   + 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/InstallNutanixAHVPlugInOnCC.ps1" 
						   + id                = (known after apply) 
						} 
						 
						  # local_file.DownloadNTNXPluginToAVM will be created 
						  + resource "local_file" "DownloadNTNXPluginToAVM" { 
						   + 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/DownloadNTNXPluginToAVM.ps1" 
						   + 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.ExecuteDownloadNTNXPluginInstallationScriptToAVM will be created 
						  + resource "null_resource" "ExecuteDownloadNTNXPluginInstallationScriptToAVM" { 
						   + id = (known after apply) 
						} 
						 
						  # null_resource.ExecuteDownloadNutanixAHVPlugInOnAVM will be created 
						  + resource "null_resource" "ExecuteDownloadNutanixAHVPlugInOnAVM" { 
						   + id = (known after apply) 
						} 
						 
						  # null_resource.UploadNutanixAHVToCC1 will be created 
						  + resource "null_resource" "UploadNutanixAHVToCC1" { 
						   + id = (known after apply) 
						} 
						 
						  # null_resource.UploadNutanixAHVToCC2 will be created 
						  + resource "null_resource" "UploadNutanixAHVToCC2" { 
						   + id = (known after apply) 
						} 
						 
						 
					 

					
						  # 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       = [ 
						                   + "Friday", 
						                   + "Monday", 
						                   + "Thursday", 
						                   + "Tuesday", 
						                   + "Wednesday", 
						                 ] 
						               + display_name       = "TACG-TF-CVAD-NTX-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 
						             }, 
						         ] 
						     } 
						   + description              = "" 
						   + desktops                 = [ 
						       + { 
						           + description          = "Terraform-based Delivery Group running on Nutanix" 
						           + enable_session_roaming  = true 
						           + enabled              = true 
						           + published_name       = "DG-TACG-TF-CVAD-NTX" 
						           + restricted_access_users = { 
						               + allow_list = [ 
						                   + "TACG\\vdaallowed", 
						                 ] 
						             } 
						         }, 
						     ] 
						   + id                       = (known after apply) 
						   + minimum_functional_level = "L7_20" 
						   + name                     = "DG-TACG-TF-CVAD-NTX" 
						   + reboot_schedules         = [ 
						       + { 
						           + days_in_week         = [ 
						               + "Sunday", 
						             ] 
						           + frequency            = "Weekly" 
						           + frequency_factor     = 1 
						           + ignore_maintenance_mode = true 
						           + name                 = "TACG-NTX-Reboot Schedule" 
						           + natural_reboot_schedule = false 
						           + reboot_duration_minutes = 0 
						           + reboot_schedule_enabled = true 
						           + start_date           = "2024-01-01" 
						           + start_time           = "02:00" 
						             # (1 unchanged attribute hidden) 
						         }, 
						     ] 
						   + restricted_access_users  = { 
						       + allow_list = [ 
						           + "TACG\\vdaallowed", 
						         ] 
						     } 
						   + scopes                   = [] 
						   + total_machines           = (known after apply) 
						} 
						 
						  # citrix_machine_catalog.CreateNTXMCSCatalog will be created 
						  + resource "citrix_machine_catalog" "CreateNTXMCSCatalog" { 
						   + allocation_type       = "Random" 
						   + description           = "Terraform-based Machine Catalog" 
						   + id                    = (known after apply) 
						   + minimum_functional_level = "L7_20" 
						   + name                  = "MC-TACG-TF-CVAD-NUTANIX" 
						   + provisioning_scheme   = { 
						       + hypervisor                  = (known after apply) 
						       + hypervisor_resource_pool    = (known after apply) 
						       + identity_type               = "ActiveDirectory" 
						       + machine_account_creation_rules = { 
						           + naming_scheme   = "TACG-TF-NTX-C#" 
						           + naming_scheme_type = "Numeric" 
						         } 
						       + machine_domain_identity     = { 
						           + domain                = "the-austrian-citrix-guy.at" 
						           + domain_ou             = "CN=Computers,DC=the-austrian-citrix-guy,DC=at" 
						           + service_account       = "Administrator" 
						           + service_account_password = (sensitive value) 
						         } 
						       + number_of_total_machines    = 1 
						       + nutanix_machine_config      = { 
						           + container        = "default-container-36052048168163" 
						           + cores_per_cpu_count = 2 
						           + cpu_count        = 2 
						           + master_image     = "mi" 
						           + master_image_note   = "" 
						           + memory_mb        = 4096 
						         } 
						     } 
						   + provisioning_type     = "MCS" 
						   + scopes                = [] 
						   + session_support       = "MultiSession" 
						   + zone                  = (known after apply) 
						} 
						 
						  # citrix_nutanix_hypervisor.CreateHypervisorConnection will be created 
						  + resource "citrix_nutanix_hypervisor" "CreateHypervisorConnection" { 
						   + addresses                             = [ 
						       + "nutanix.the-austrian-citrix-guy.at", 
						     ] 
						   + id                                    = (known after apply) 
						   + max_absolute_active_actions           = 100 
						   + max_absolute_new_actions_per_minute   = 10 
						   + max_power_actions_percentage_of_machines = 20 
						   + name                                  = "TACG-TF-CVAD-Nutanix-HypConn" 
						   + password                              = (sensitive value) 
						   + password_format                       = "PlainText" 
						   + scopes                                = [] 
						   + username                              = "admin" 
						   + zone                                  = (known after apply) 
						} 
						 
						  # citrix_nutanix_hypervisor_resource_pool.CreateHypervisorPool will be created 
						  + resource "citrix_nutanix_hypervisor_resource_pool" "CreateHypervisorPool" { 
						   + hypervisor = (known after apply) 
						   + id      = (known after apply) 
						   + name    = "TACG-TF-CVAD-Nutanix-HypConnPool" 
						   + networks   = [ 
						       + "NW-NTX-TACG", 
						     ] 
						} 
						 
						  # citrix_zone.TACG-TF-HYP-Zone will be created 
						  + resource "citrix_zone" "TACG-TF-HYP-Zone" { 
						   + description       = (known after apply) 
						   + id                = (known after apply) 
						   + name              = "TACG-TF-CVAD-NTX-ZONE" 
						   + resource_location_id = (known after apply) 
						} 
						 
						  # time_sleep.wait_30_seconds will be created 
						  + resource "time_sleep" "wait_30_seconds" { 
						   + create_duration = "30s" 
						   + id           = (known after apply) 
						} 
						 
						  # time_sleep.wait_30_seconds1 will be created 
						  + resource "time_sleep" "wait_30_seconds1" { 
						   + create_duration = "30s" 
						   + id           = (known after apply) 
						} 
						 
						  # time_sleep.wait_30_seconds_2 will be created 
						  + resource "time_sleep" "wait_30_seconds_2" { 
						   + create_duration = "30s" 
						   + id           = (known after apply) 
						} 
						 
						Plan: 8 to add, 0 to change, 0 to destroy. 
						 
						Changes to Outputs: 
						  + ZoneID = (known after apply) 
						 
						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 
						 
						local_file.DownloadNTNXPluginInstallationScriptToAVM: Creating... 
						local_file.DownloadNTNXPluginToAVM: Creating... 
						local_file.DownloadNTNXPluginInstallationScriptToAVM: Creation complete after 0s [id=58441448bd04fa4a85628abf8a218891bcd24463] 
						local_file.DownloadNTNXPluginToAVM: Creation complete after 0s [id=d66e63f653326e57fc72b32aef152762f0b8f1a1] 
						null_resource.ExecuteDownloadNTNXPluginInstallationScriptToAVM: Creating... 
						null_resource.ExecuteDownloadNutanixAHVPlugInOnAVM: Creating... 
						null_resource.ExecuteDownloadNutanixAHVPlugInOnAVM: Provisioning with 'local-exec'... 
						null_resource.ExecuteDownloadNTNXPluginInstallationScriptToAVM: Provisioning with 'local-exec'... 
						null_resource.ExecuteDownloadNutanixAHVPlugInOnAVM (local-exec): Executing: ["PowerShell" "-Command" " c:/temp/xdinst/DATA/DownloadNTNXPluginToAVM.ps1"] 
						null_resource.ExecuteDownloadNTNXPluginInstallationScriptToAVM (local-exec): Executing: ["PowerShell" "-Command" " c:/temp/xdinst/DATA/InstallNutanixAHVPlugInOnCC.ps1"] 
						null_resource.ExecuteDownloadNutanixAHVPlugInOnAVM: Still creating... [10s elapsed] 
						null_resource.ExecuteDownloadNTNXPluginInstallationScriptToAVM: Still creating... [10s elapsed] 
						null_resource.ExecuteDownloadNTNXPluginInstallationScriptToAVM: Still creating... [20s elapsed] 
						null_resource.ExecuteDownloadNutanixAHVPlugInOnAVM: Still creating... [20s elapsed] 
						null_resource.ExecuteDownloadNTNXPluginInstallationScriptToAVM: Still creating... [30s elapsed] 
						null_resource.ExecuteDownloadNutanixAHVPlugInOnAVM: Still creating... [30s elapsed] 
						null_resource.ExecuteDownloadNutanixAHVPlugInOnAVM: Still creating... [40s elapsed] 
						null_resource.ExecuteDownloadNTNXPluginInstallationScriptToAVM: Still creating... [40s elapsed] 
						null_resource.ExecuteDownloadNutanixAHVPlugInOnAVM: Still creating... [50s elapsed] 
						null_resource.ExecuteDownloadNTNXPluginInstallationScriptToAVM: Still creating... [50s elapsed] 
						null_resource.ExecuteDownloadNutanixAHVPlugInOnAVM: Still creating... [1m0s elapsed] 
						null_resource.ExecuteDownloadNTNXPluginInstallationScriptToAVM: Still creating... [1m0s elapsed] 
						null_resource.ExecuteDownloadNutanixAHVPlugInOnAVM: Creation complete after 1m0s [id=3030678197037961348] 
						null_resource.UploadNutanixAHVToCC1: Creating... 
						null_resource.UploadNutanixAHVToCC2: Creating... 
						null_resource.UploadNutanixAHVToCC1: Provisioning with 'file'... 
						null_resource.UploadNutanixAHVToCC2: Provisioning with 'file'... 
						null_resource.ExecuteDownloadNTNXPluginInstallationScriptToAVM: Creation complete after 1m0s [id=1833985232879280472] 
						null_resource.UploadNutanixAHVToCC2: Still creating... [10s elapsed] 
						null_resource.UploadNutanixAHVToCC1: Still creating... [10s elapsed] 
						null_resource.UploadNutanixAHVToCC1: Still creating... [20s elapsed] 
						null_resource.UploadNutanixAHVToCC2: Still creating... [20s elapsed] 
						null_resource.UploadNutanixAHVToCC2: Still creating... [30s elapsed] 
						null_resource.UploadNutanixAHVToCC1: Still creating... [30s elapsed] 
						null_resource.UploadNutanixAHVToCC1: Still creating... [40s elapsed] 
						null_resource.UploadNutanixAHVToCC2: Still creating... [40s elapsed] 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadNutanixAHVToCC2: Provisioning with 'file'... 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadNutanixAHVToCC1: Provisioning with 'file'... 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadNutanixAHVToCC2: Creation complete after 47s [id=8256669184849626355] 
						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.10.175.2 
						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. 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadNutanixAHVToCC1: Creation complete after 47s [id=5135813728942165357] 
						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.10.175.1 
						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. 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
						null_resource.CallRequiredScriptsOnCC2 (remote-exec): C:\Users\Administrator&gt;powershell -File c:/temp/xdinst/DATA/InstallNutanixAHVPlugInOnCC.ps1 
						 
						null_resource.CallRequiredScriptsOnCC1 (remote-exec): C:\Users\Administrator&gt;powershell -File c:/temp/xdinst/DATA/InstallNutanixAHVPlugInOnCC.ps1 
						null_resource.CallRequiredScriptsOnCC2: Still creating... [10s elapsed] 
						null_resource.CallRequiredScriptsOnCC1: Still creating... [10s elapsed] 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.CallRequiredScriptsOnCC2: Creation complete after 14s [id=8129597400070755177] 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.CallRequiredScriptsOnCC1: Creation complete after 14s [id=3838749021188728013]
					 

					
						citrix_zone.TACG-TF-HYP-Zone: Creating... 
						citrix_zone.TACG-TF-HYP-Zone: Creation complete after 1s [id=623722d7-ccaf-4fb1-9c2c-fa697d691bb4] 
						citrix_nutanix_hypervisor.CreateHypervisorConnection: Creating... 
						citrix_nutanix_hypervisor.CreateHypervisorConnection: Still creating... [10s elapsed] 
						citrix_nutanix_hypervisor.CreateHypervisorConnection: Still creating... [20s elapsed] 
						citrix_nutanix_hypervisor.CreateHypervisorConnection: Still creating... [30s elapsed] 
						citrix_nutanix_hypervisor.CreateHypervisorConnection: Still creating... [40s elapsed] 
						citrix_nutanix_hypervisor.CreateHypervisorConnection: Creation complete after 40s [id=55cc2ab6-6fa4-402a-a358-922847600485] 
						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-08-20T13:26:38Z] 
						citrix_nutanix_hypervisor_resource_pool.CreateHypervisorPool: Creating... 
						citrix_nutanix_hypervisor_resource_pool.CreateHypervisorPool: Still creating... [10s elapsed] 
						citrix_nutanix_hypervisor_resource_pool.CreateHypervisorPool: Creation complete after 10s [id=88385cdf-5574-45cb-9ff4-0b439a7f34de] 
						time_sleep.wait_30_seconds1: Creating... 
						time_sleep.wait_30_seconds1: Still creating... [10s elapsed] 
						time_sleep.wait_30_seconds1: Still creating... [20s elapsed] 
						time_sleep.wait_30_seconds1: Still creating... [30s elapsed] 
						time_sleep.wait_30_seconds1: Creation complete after 30s [id=2024-08-20T13:27:19Z] 
						citrix_machine_catalog.CreateNTXMCSCatalog: Creating... 
						citrix_machine_catalog.CreateNTXMCSCatalog: Still creating... [10s elapsed] 
						citrix_machine_catalog.CreateNTXMCSCatalog: Still creating... [20s elapsed] 
						citrix_machine_catalog.CreateNTXMCSCatalog: Still creating... [30s elapsed] 
						citrix_machine_catalog.CreateNTXMCSCatalog: Still creating... [40s elapsed] 
						citrix_machine_catalog.CreateNTXMCSCatalog: Still creating... [50s elapsed] 
						citrix_machine_catalog.CreateNTXMCSCatalog: Still creating... [1m0s elapsed] 
						citrix_machine_catalog.CreateNTXMCSCatalog: Still creating... [1m10s elapsed] 
						citrix_machine_catalog.CreateNTXMCSCatalog: Still creating... [1m20s elapsed] 
						citrix_machine_catalog.CreateNTXMCSCatalog: Still creating... [1m30s elapsed] 
						citrix_machine_catalog.CreateNTXMCSCatalog: Creation complete after 1m31s [id=d30b653e-a979-40e2-8972-dcef7cef8d54] 
						time_sleep.wait_30_seconds_2: Creating... 
						time_sleep.wait_30_seconds_2: Still creating... [10s elapsed] 
						time_sleep.wait_30_seconds_2: Still creating... [20s elapsed] 
						time_sleep.wait_30_seconds_2: Still creating... [30s elapsed] 
						time_sleep.wait_30_seconds_2: Creation complete after 30s [id=2024-08-20T13:29:20Z] 
						citrix_delivery_group.CreateDG: Creating... 
						citrix_delivery_group.CreateDG: Creation complete after 4s [id=79db3c34-8b28-4610-b5e7-84c50481fb4a] 
						 
						Apply complete. Resources: 16 added, 0 changed, 0 destroyed. 
						PS C:\_TACG\_CVADOnNutanix\_CVADOnNutanix-CVAD-Entities&gt;
					 
				
			
		
	

	
		 
		Terraform has successfully installed the Nutanix Plug-In: 
		 
		 
	 

	
		Caution: 
		 
		Please make sure that the installation script has set up the Plug-in in the correct mode – it must be “mcs”. 
		You can check it by using regedit to look into the corresponding Registry key: 
		Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Nutanix Inc.\Citrix MCS Plugin 
		"InstallerType"="mcs" 
		 
	 

	
		
	 

	
		 
	 

	
		Terraform successfully created the Hosting Connection and a Hosting Connection Pool associated with the Hosting Connection: 
		 
		 
		 
		Terraform successfully created the Machine Catalog: 
		
	 

	
		 
		
	 

	
		 
		Terraform successfully created the Delivery Group: 
		
	 

	
		
	 

	
		 
		Terraform successfully created the AutoScale settings: 
		
	 

	
		 
		The Delivery Group is shown on StoreFront and ready to start: 
		
	 

	
		 
		The HDX connection is successfully established: 
		
	 



	That concludes the guide "Using Terraform to deploy a Citrix Virtual Apps and Desktops 2402 LTSR site on Nutanix AHV”.
 


	 
	Appendix
 


	
		Disclaimer
	 

	
		EXCEPT WHERE EXPRESSLY PROVIDED OTHERWISE BY CLOUD SOFTWARE GROUP, THE CONTENT IN THIS DOCUMENT IS PROVIDED “AS IS” AND CLOUD SOFTWARE GROUP HEREBY DISCLAIMS ALL EXPRESS OR IMPLIED REPRESENTATIONS, WARRANTIES, GUARANTIES, AND CONDITIONS, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES OR CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD TO BE LEGALLY INVALID. CLOUD SOFTWARE GROUP MAKES NO REPRESENTATIONS, WARRANTIES, GUARANTIES, OR CONDITIONS AS TO THE QUALITY, SUITABILITY, TRUTH, ACCURACY, OR COMPLETENESS OF ANY OF THE CONTENT CONTAINED ON THE WEBSITE.
	 

	
		The above disclaimer applies to any damages, liability, or injuries caused by any failure of performance, error, omission, interruption, deletion, defect, delay in operation or transmission, computer virus, communication line failure, theft or destruction of or unauthorized access to, alteration of, or use, whether for breach of contract, tort, negligence or any other cause of action. 
		 
	 



	Examples of the Terraform scripts
 


	Module 1: _CVADOnNutanix-Creation



	These are the Terraform configuration files for Module 1 (excerpts):
 


	_CVADOnNutanix-Create-Provider.tf



	
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}	
	
		
			
				
					
						
							
								# Terraform deployment of Citrix Virtual Apps and Desktops on Nutanix AHV
							

							
								## Definition of all required Terraform providers
							
							 

							
								terraform {
							

							
								    required_version = "&gt;= 1.9.4"
							
							 

							
								  required_providers {
							

							
								    nutanix = {
							

							
								      source  = "nutanix/nutanix"
							

							
								      version = "= 1.9.5"
							

							
								    }
							

							
								  }
							

							
								}
							
							 

							
								provider "nutanix" {
							

							
								  username     = var.Nutanix_Provider-Username
							

							
								  password     = var.Nutanix_Provider-Password
							

							
								  endpoint     = var.Nutanix_Provider-Endpoint
							

							
								  port         = var.Nutanix_Provider-Port
							

							
								  insecure     = var.Nutanix_Provider-Insecure
							

							
								  wait_timeout = var.Nutanix_Provider-Timeout
							

							
								}
							
						
					
				
			
		
	



	 
 


	_CVADOnNutanix-Create.tf



	
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}	
	
		
			
				
					
						
							
								# Terraform deployment of Citrix Virtual Apps and Desktops on Nutanix AHV
							

							
								## Definition of all required local variables
							
							 

							
								### Get Nutanix Prism Cluster-ID
							

							
								### We assume that only one cluster is configured
							

							
								data "nutanix_clusters" "NTX-Clusters" {
							

							
								}
							
							 

							
								data "nutanix_cluster" "NTX-Cluster" {
							

							
								   cluster_id = data.nutanix_clusters.NTX-Clusters.entities[0].metadata.uuid
							

							
								}
							
							 

							
								### Get Nutanix Prism Subnet-ID
							

							
								### We assume that only one subnet is configured
							

							
								data "nutanix_subnets" "NTX-Subnets" {
							

							
								}
							
							 

							
								### Set the local variables based on the Data sources
							

							
								locals {
							

							
								  NTX-Cluster-ID = data.nutanix_clusters.NTX-Clusters.entities[0].metadata.uuid
							

							
								  NTX-Subnet-ID = data.nutanix_subnets.NTX-Subnets.entities[1].metadata.uuid
							

							
								  NTX-Cluster-Name = data.nutanix_cluster.NTX-Cluster.name
							

							
								}
							
							 

							
								### Upload ISO-Images
							

							
								resource "nutanix_image" "CVAD2402LTSR" {
							

							
								  name        = "CVAD 2402 LTSR-ISO"
							

							
								  description = "CVAD 2402 LTSR-ISO"
							

							
								  source_path  = "${path.module}/DATA/Citrix_Virtual_Apps_and_Desktops_7_2402_LTSR.iso"
							

							
								}  
							

							
								 
							

							
								### Load and configure AutoUnattend.xml file for customizing the Windows Server-based Image
							

							
								data "template_file" "AutoUnattendXMLCC1" {
							

							
								  template = file("${path.module}/DATA/AutoUnattend.xml")
							

							
								  vars = {
							

							
								    Unattended-VM_NAME              = "${var.CC1-VM-Name}"
							

							
								    Unattended-DomainName           = "${var.Unattended-DomainName}"
							

							
								    Unattended-DomainName-NB        = "${var.Unattended-DomainName-NB}"
							

							
								    Unattended-DomainOU             = "${var.Unattended-DomainOU}"
							

							
								    Unattended-OrgName              = "${var.Unattended-OrgName}"
							

							
								    Unattended-OwnerName            = "${var.Unattended-OwnerName}"
							

							
								    Unattended-DomainAdmin-PW       = textencodebase64(join("", ["${var.Unattended-DomainAdmin-PW}", "AdministratorPassword"]), "UTF-16LE")
							

							
								    Unattended-Admin-PW             = textencodebase64(join("", ["${var.Unattended-Admin-PW}", "AdministratorPassword"]), "UTF-16LE")
							

							
								    Unattended-Autologon-PW         = textencodebase64(join("", ["${var.Unattended-Admin-PW}", "Password"]), "UTF-16LE")
							

							
								    Unattended-DomainUser           = "${var.Unattended-DomainUser}"
							

							
								    Unattended-DomainUser-PW        = "${var.Unattended-DomainUser-PW}"
							

							
								    Unattended-Timezone             = "${var.Unattended-Timezone}"
							

							
								    Unattended-FRScript             = "${var.Unattended-FRScript}"
							

							
								    Unattended-ProductKey           = "${var.Unattended-ProductKey}"
							

							
								  }
							

							
								} 
							
							 

							
								data "template_file" "AutoUnattendXMLCC2" {
							

							
								  template = file("${path.module}/DATA/AutoUnattend.xml")
							

							
								  vars = {
							

							
								    Unattended-VM_NAME              = "${var.CC2-VM-Name}"
							

							
								    Unattended-DomainName           = "${var.Unattended-DomainName}"
							

							
								    Unattended-DomainName-NB        = "${var.Unattended-DomainName-NB}"
							

							
								    Unattended-DomainOU             = "${var.Unattended-DomainOU}"
							

							
								    Unattended-OrgName              = "${var.Unattended-OrgName}"
							

							
								    Unattended-OwnerName            = "${var.Unattended-OwnerName}"
							

							
								    Unattended-DomainAdmin-PW       = textencodebase64(join("", ["${var.Unattended-DomainAdmin-PW}", "AdministratorPassword"]), "UTF-16LE")
							

							
								    Unattended-Admin-PW             = textencodebase64(join("", ["${var.Unattended-Admin-PW}", "AdministratorPassword"]), "UTF-16LE")
							

							
								    Unattended-Autologon-PW         = textencodebase64(join("", ["${var.Unattended-Admin-PW}", "Password"]), "UTF-16LE")
							

							
								    Unattended-DomainUser           = "${var.Unattended-DomainUser}"
							

							
								    Unattended-DomainUser-PW        = "${var.Unattended-DomainUser-PW}"
							

							
								    Unattended-Timezone             = "${var.Unattended-Timezone}"
							

							
								    Unattended-FRScript             = "${var.Unattended-FRScript}"
							

							
								    Unattended-ProductKey           = "${var.Unattended-ProductKey}"
							

							
								  }
							

							
								} 
							
							 

							
								data "template_file" "AutoUnattendXMLAVM" {
							

							
								  template = file("${path.module}/DATA/AutoUnattend.xml")
							

							
								  vars = {
							

							
								    Unattended-VM_NAME              = "${var.AVM-VM-Name}"
							

							
								    Unattended-DomainName           = "${var.Unattended-DomainName}"
							

							
								    Unattended-DomainName-NB        = "${var.Unattended-DomainName-NB}"
							

							
								    Unattended-DomainOU             = "${var.Unattended-DomainOU}"
							

							
								    Unattended-OrgName              = "${var.Unattended-OrgName}"
							

							
								    Unattended-OwnerName            = "${var.Unattended-OwnerName}"
							

							
								    Unattended-DomainAdmin-PW       = textencodebase64(join("", ["${var.Unattended-DomainAdmin-PW}", "AdministratorPassword"]), "UTF-16LE")
							

							
								    Unattended-Admin-PW             = textencodebase64(join("", ["${var.Unattended-Admin-PW}", "AdministratorPassword"]), "UTF-16LE")
							

							
								    Unattended-Autologon-PW         = textencodebase64(join("", ["${var.Unattended-Admin-PW}", "Password"]), "UTF-16LE")
							

							
								    Unattended-DomainUser           = "${var.Unattended-DomainUser}"
							

							
								    Unattended-DomainUser-PW        = "${var.Unattended-DomainUser-PW}"
							

							
								    Unattended-Timezone             = "${var.Unattended-Timezone}"
							

							
								    Unattended-FRScript             = "${var.Unattended-FRScript}"
							

							
								    Unattended-ProductKey           = "${var.Unattended-ProductKey}"
							

							
								  }
							

							
								} 
							
							 

							
								### Create CC1-VM
							

							
								# Virtual machine resource
							

							
								resource "nutanix_virtual_machine" "CC1-VM" {
							

							
								   depends_on = [ nutanix_image.CVAD2402LTSR ]
							

							
								  # General Information
							

							
								  name                 = "${var.CC1-VM-Name}"
							

							
								  description          = "${var.CC1-VM-Description}"
							

							
								  num_vcpus_per_socket = "${var.CC1-VM-CPU}"
							

							
								  num_sockets          = "${var.CC1-VM-Sockets}"
							

							
								  memory_size_mib      = "${var.CC1-VM-RAM}"
							

							
								  boot_type            = "LEGACY"
							

							
								  boot_device_order_list = ["DISK",]  
							

							
								  guest_customization_sysprep = {
							

							
								    install_type = "PREPARED"
							

							
								    unattend_xml = base64encode(data.template_file.AutoUnattendXMLCC1.rendered)
							

							
								  } 
							
							 

							
								  # VM Cluster
							

							
								  cluster_uuid = local.NTX-Cluster-ID
							
							 

							
								  # What networks will this be attached to?
							

							
								  nic_list {
							

							
								    subnet_uuid = local.NTX-Subnet-ID
							

							
								    ip_endpoint_list {
							

							
								      ip =  "10.10.175.1"
							

							
								      type = "ASSIGNED"
							

							
								    } 
							

							
								  }
							
							 

							
								  # What disk/cdrom configuration will this have?
							

							
								  disk_list {
							

							
								    data_source_reference = {
							

							
								      kind = "image"
							

							
								      uuid = "${var.NTX-Image-UUId}"
							

							
								    }
							

							
								    device_properties {
							

							
								      device_type = "DISK"
							

							
								      disk_address = {
							

							
								        device_index = 0
							

							
								        adapter_type = "SCSI"
							

							
								      }
							

							
								    }
							

							
								  }
							
							 

							
								 disk_list {
							

							
								data_source_reference = {
							

							
								      kind = "image"
							

							
								      uuid =nutanix_image.CVAD2402LTSR.id
							

							
								    }
							

							
								    device_properties {
							

							
								      device_type = "CDROM"
							

							
								      disk_address = {
							

							
								        device_index = 2
							

							
								        adapter_type = "IDE"
							

							
								      }
							

							
								    }
							

							
								 }
							

							
								}
							
							 

							
								### Create CC2-VM
							

							
								# Virtual machine resource
							

							
								resource "nutanix_virtual_machine" "CC2-VM" {
							

							
								   depends_on = [ nutanix_image.CVAD2402LTSR ]
							

							
								  # General Information
							

							
								  name                 = "${var.CC2-VM-Name}"
							

							
								  description          = "${var.CC2-VM-Description}"
							

							
								  num_vcpus_per_socket = "${var.CC2-VM-CPU}"
							

							
								  num_sockets          = "${var.CC2-VM-Sockets}"
							

							
								  memory_size_mib      = "${var.CC2-VM-RAM}"
							

							
								  boot_type            = "LEGACY"
							

							
								  boot_device_order_list = ["DISK",]  
							

							
								  guest_customization_sysprep = {
							

							
								    install_type = "PREPARED"
							

							
								    unattend_xml = base64encode(data.template_file.AutoUnattendXMLCC2.rendered)
							

							
								  } 
							
							 

							
								  # VM Cluster
							

							
								  cluster_uuid = local.NTX-Cluster-ID
							
							 

							
								  # What networks will this be attached to?
							

							
								  nic_list {
							

							
								    subnet_uuid = local.NTX-Subnet-ID
							

							
								    ip_endpoint_list {
							

							
								      ip =  "10.10.175.2"
							

							
								      type = "ASSIGNED"
							

							
								    } 
							

							
								  }
							
							 

							
								  # What disk/cdrom configuration will this have?
							

							
								  disk_list {
							

							
								    data_source_reference = {
							

							
								      kind = "image"
							

							
								      uuid =nutanix_image.CVAD2402LTSR.id
							

							
								    }
							

							
								    device_properties {
							

							
								      device_type = "CDROM"
							

							
								      disk_address = {
							

							
								        device_index = 2
							

							
								        adapter_type = "IDE"
							

							
								      }
							

							
								    }
							

							
								  }
							

							
								}
							
							 

							
								### Create AVM-VM
							

							
								# Virtual machine resource
							

							
								resource "nutanix_virtual_machine" "AVM-VM" {
							

							
								  # General Information
							

							
								  name                 = "${var.AVM-VM-Name}"
							

							
								  description          = "${var.AVM-VM-Description}"
							

							
								  num_vcpus_per_socket = "${var.AVM-VM-CPU}"
							

							
								  num_sockets          = "${var.AVM-VM-Sockets}"
							

							
								  memory_size_mib      = "${var.AVM-VM-RAM}"
							

							
								  boot_type            = "LEGACY"
							

							
								  boot_device_order_list = ["DISK",]  
							

							
								  guest_customization_sysprep = {
							

							
								    install_type = "PREPARED"
							

							
								    unattend_xml = base64encode(data.template_file.AutoUnattendXMLAVM.rendered)
							

							
								  } 
							
							 

							
								  # VM Cluster
							

							
								  cluster_uuid = local.NTX-Cluster-ID
							
							 

							
								  # What networks will this be attached to?
							

							
								  nic_list {
							

							
								    subnet_uuid = local.NTX-Subnet-ID
							

							
								    ip_endpoint_list {
							

							
								      ip =  "10.10.175.3"
							

							
								      type = "ASSIGNED"
							

							
								    } 
							

							
								  }
							
							 

							
								  # What disk/cdrom configuration will this have?
							

							
								  disk_list {
							

							
								    data_source_reference = {
							

							
								      kind = "image"
							

							
								      uuid = "${var.NTX-Image-UUId}"
							

							
								    }
							

							
								  }
							

							
								}
							
							 

							
								######################################################################################
							

							
								###  All VMs for creating a Resource Location on Nutanix should have been created  ###
							

							
								######################################################################################
							
						
					
				
			
		
	



	 
 


	Module 2: _CVADOnNutanix-Configuration



	These are the Terraform configuration files for Module 2 (excerpts):
 


	_CVADOnNutanix-InstallCVADandSF.tf



	
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}	
	
		
			
				
					
						
							
								# Terraform deployment of Citrix Virtual Apps and Desktops on Nutanix AHV
							

							
								## Install CVAD and SF with all chosen components and create a new site
							

							
								locals {
							

							
								  installPath       = join("/",[var.CVAD_Install_Source-Drive,var.CVAD_Install_Source-Path])
							

							
								  installSwitches   = join(" ",[var.CVAD_Install_Features-To-Install, var.CVAD_Install_CLI-Switch_NoReboot, var.CVAD_Install_CLI-Switch_ConfigureFirewall, var.CVAD_Install_CLI-Switch_QuietInstall, "/nosql","/Logpath", var.CVAD_Install_LogPath])
							

							
								  completeInstall   = join(" ",[local.installPath,local.installSwitches]) 
							

							
								  SFCompleteInstall = join("",[var.CVAD_Install_Source-Drive,var.SF_Install_Source-Path])
							
							 

							
								  #### Create CVAD-Installation script
							

							
								  CVADScriptcontent     = &lt;&lt;-EOT
							

							
								  New-Item -Path '${var.CVAD_Install_LogPath}' -ItemType Directory
							

							
								  ${local.completeInstall}
							

							
								  #Restart-Computer -Force
							

							
								  EOT
							
							 

							
								  #### Create SF-Installation script
							

							
								  SFScriptcontent     = &lt;&lt;-EOT
							

							
								  ${local.SFCompleteInstall}
							

							
								    EOT
							
							 

							
								  #### Create Reboot script
							

							
								  RebootScriptcontent     = &lt;&lt;-EOT
							

							
								  Restart-Computer -Force
							

							
								  EOT
							
							 

							
								  #### Create CVAD-Database-and-Site-Creation script
							

							
								  #### Need to invoke PowerShell as Domain User as the provisioner does not allow to be run in a Domain Users-context, but CVAD SDK needs to be run as a Domain User
							

							
								  CVADDBScriptcontent     = &lt;&lt;-EOT
							

							
								  $PSUsername = '${var.NTNX_domain_admin_userwithdomain}'
							

							
								  $PSPassword = '${var.NTNX_domain_admin_pw}'
							

							
								  $PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
							

							
								  $PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
							

							
								  Invoke-Command -ComputerName localhost -Credential $PSCredential -ScriptBlock {
							

							
								  Add-PSSnapin Citrix.*
							

							
								  $PSUsername = '${var.NTNX_domain_admin_userwithdomain}'
							

							
								  $PSPassword = '${var.NTNX_domain_admin_pw}'
							

							
								  $PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
							

							
								  $PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
							

							
								  New-XDDatabase -SiteName ${var.CVAD-SiteName} -AllDefaultDatabases -DatabaseServer ${var.CVAD-DB-ServerName} -DatabaseCredentials $PSCredential
							

							
								  
							

							
								  New-XDSite -AdminAddress ${var.CVAD-DDC1-Name} -AllDefaultDatabases -SiteName ${var.CVAD-SiteName} -DatabaseServer ${var.CVAD-DB-ServerName}
							

							
								  #Restart-Computer -Force
							

							
								  #Add-XDController -AdminAddress ${var.CVAD-DDC2-Name} -SiteControllerAddress ${var.CVAD-DDC1-Name}
							

							
								  }
							

							
								  EOT 
							
							 

							
								  #### Create Storefront-Configuration script
							

							
								  #### Need to invoke PowerShell as Domain User as the provisioner does not allow to be run in a Domain Users-context, but CVAD SDK needs to be run as a Domain User
							

							
								  ConfigureStorefrontScriptcontent     = &lt;&lt;-EOT
							

							
								  #$PSUsername = 'tacg\administrator'
							

							
								#$PSPassword = 'XXXXXXXXXX'
							

							
								#$PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
							

							
								#$PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
							

							
								#Invoke-Command -ComputerName localhost -Credential $PSCredential -ScriptBlock {
							

							
								Add-PSSnapin Citrix.*
							
							 

							
								#Clear-Existing StoreFront deployments
							

							
								Clear-STFDeployment -Confirm:$false
							

							
								Write-Output 'Existing Deployments deleted.'
							
							 

							
								#Add new StoreFront deployment
							

							
								Add-STFDeployment -HostBaseUrl 'https://storefront.the-austrian-citrix-guy.at' -Confirm:$false
							

							
								Write-Output 'New deployment created.'
							
							 

							
								#Add new Authentication Service
							

							
								Add-STFAuthenticationService '/Citrix/Authentication'
							

							
								$authentication = Get-STFAuthenticationService
							

							
								Write-Output 'Authentication service created.'
							
							 

							
								Start-Sleep -Seconds 120
							
							 

							
								#Enable Access through a NetScaler Gateway
							

							
								Enable-STFAuthenticationServiceProtocol -Name CitrixAGBasic -AuthenticationService ($authentication)
							

							
								$protocols = Get-STFAuthenticationServiceProtocol -AuthenticationService ($authentication)
							
							 

							
								#Add NetScaler Gateway
							

							
								Add-STFRoamingGateway -Name TACG-TF-NSGW -LogonType Domain -GatewayUrl 'https://ns.the-austrian-citrix-guy.at' -CallbackUrl 'https://ns.the-austrian-citrix-guy.at/CitrixAuthService/AuthService.asmx' -SessionReliability:$true -SecureTicketAuthorityUrls 'http://TACG-NTX-DDC1.the-austrian-citrix-guy.at/scripts/ctxsta.dll,http://TACG-NTX-DDC2.the-austrian-citrix-guy.at/scripts/ctxsta.dll' -IsCloudGateway:$false
							

							
								Write-Output 'NSGW created.'
							

							
								#Add Beacons for NetScaler Gateway
							

							
								Set-STFRoamingBeacon -Internal 'http://TACG-NTX-DDC1.the-austrian-citrix-guy.at' -External https://ns.the-austrian-citrix-guy.at,https://news.orf.at
							

							
								Write-Output 'Beacons created.'
							
							 

							
								#Add new Store Service
							

							
								Add-STFStoreService -VirtualPath '/Citrix/Store' -AuthenticationService $authentication -FarmName 'TACG-SF' -FarmType 'XenDesktop' -Servers 'TACG-NTX-DDC1.the-austrian-citrix-guy.at' -FriendlyName 'TACG-SF' -TransportType HTTPS -Port 443
							

							
								Write-Output 'Store service created.'
							
							 

							
								#Register NetScaler Gateway and Beacons
							

							
								$store = Get-STFStoreService -VirtualPath '/Citrix/Store'
							

							
								$gateway = Get-STFRoamingGateway -Name TACG-TF-NSGW
							

							
								Register-STFStoreGateway -Gateway $gateway -StoreService $store -DefaultGateway -UseFullVpn:$false
							

							
								Write-Output 'NSGW registered.'
							
							 

							
								#Enable WebReceiver
							

							
								Add-STFWebReceiverService -VirtualPath '/Citrix/StoreWeb' -StoreService $store
							

							
								$receiver = Get-STFWebReceiverService -VirtualPath '/Citrix/StoreWeb'
							

							
								Write-Output 'Web Receiver site created.'
							
							 

							
								#Enable PNAGent site
							

							
								Enable-STFStorePna -StoreService $store -AllowUserPasswordChange -DefaultPnaService
							

							
								Write-Output 'PNA site created.'
							
							 

							
								#Enable HTML5Fallback
							

							
								Set-STFWebReceiverPluginAssistant -WebReceiverService $receiver -Enabled $true -Html5Enabled "Fallback"
							

							
								Write-Output 'HTML5 site created.'
							

							
								Write-Output 'Storefront configuration complete.'
							

							
								Write-Output 'Registering Storefront in CVAD Site configuration.'
							
							 

							
								$PSUsername = 'tacg\administrator'
							

							
								$PSPassword = 'XXXXXXXXXX'
							

							
								$PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
							

							
								$PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
							

							
								Invoke-Command -ComputerName localhost -Credential $PSCredential -ScriptBlock {
							

							
								Add-PSSnapin Citrix.*
							
							 

							
								#Add StoreFront configuration to CVAD site
							

							
								$configurationSlot = Get-BrokerConfigurationSlot -Name "RS"
							

							
								$storefrontUrl = 'https://storefront.the-austrian-citrix-guy.at/Citrix/StoreWeb'
							

							
								$configuration = New-BrokerStorefrontAddress -Description "Citrix Storefront Address" -Enabled $true -Name "Citrix StoreFront" -Url $storefrontUrl
							

							
								New-BrokerMachineConfiguration -Policy $configuration -ConfigurationSlotUid $configurationSlot.Uid -Description "Citrix Storefront Address Configuration" -LeafName 'TACG-SF'
							

							
								Write-Output 'Registration in CVAD Site complete.'
							

							
								Write-Output 'Reboot needed...'
							

							
								}
							

							
								  EOT
							
							 

							
								#### Create script to schedule a Task on DDC1 to enable replication as invoking PowerShell is not supported for this cmdlet
							

							
								CreateStoreFrontReplicationTaskonDDC1Scriptcontent     = &lt;&lt;-EOT
							

							
								 $time = (Get-Date).addMinutes(2)
							

							
								 $trigger = New-JobTrigger -Once -At $time
							

							
								 $JobName= -join('TACG-TF-StartSFReplication-' ,(Get-Date -Format "ddmmyyHHmmss"))
							

							
								 $PSUsername = '${var.NTNX_domain_admin_userwithdomain}'
							

							
								 $PSPassword = '${var.NTNX_domain_admin_pw}'
							

							
								 $PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
							

							
								 $PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
							

							
								 Register-ScheduledJob -file '${var.CVAD_Install_LogPath}/SF-CMD-EnableReplication.ps1' -Trigger $trigger -Credential $PSCredential -Name $JobName
							

							
								 Write-Output 'Replication-Job scheduled.'
							

							
								  }
							

							
								  EOT
							
							 

							
								#### Create script to schedule a Task on DDC2 to enable replication as invoking PowerShell is not supported for this cmdlet
							

							
								CreateStoreFrontReplicationTaskonDDC2Scriptcontent     = &lt;&lt;-EOT
							

							
								 $time = (Get-Date).addMinutes(2)
							

							
								 $trigger = New-JobTrigger -Once -At $time
							

							
								 $JobName= -join('TACG-TF-StartSFReplication-' ,(Get-Date -Format "ddmmyyHHmmss"))
							

							
								 $PSUsername = '${var.NTNX_domain_admin_userwithdomain}'
							

							
								 $PSPassword = '${var.NTNX_domain_admin_pw}'
							

							
								 $PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
							

							
								 $PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
							

							
								 Register-ScheduledJob -file '${var.CVAD_Install_LogPath}/SF-CMD-StartReplication.ps1' -Trigger $trigger -Credential $PSCredential -Name $JobName
							

							
								 Write-Output 'Replication-Job scheduled.'
							

							
								  }
							

							
								  EOT
							
							 

							
								#### Create script being started by DDC1 SF-Replication task
							

							
								CreateStoreFrontEnableReplicationTaskonDDC1Scriptcontent     = &lt;&lt;-EOT
							

							
								Add-PSSnapin Citrix.Storefront.*
							

							
								Start-Service -Name CitrixClusterService
							

							
								$JoinObject = Start-STFServerGroupJoin -IsAuthorizingServer -Confirm:$false
							

							
								$JoinPassCode = ($JoinObject | Select -ExpandProperty 'Passcode')
							

							
								$JoinPassCode | Out-File -Force -FilePath '${var.CVAD_Install_LogPath}/pc.txt' 
							

							
								Copy-Item -Path ${var.CVAD_Install_LogPath}/pc.txt -Destination ${var.CVAD-SMB-DDC2}
							

							
								Write-Output 'Replication enabled.'
							

							
								  }
							

							
								  EOT
							
							 

							
								#### Create script being started by DDC2 SF-Replication task
							

							
								CreateStoreFrontStartReplicationTaskonDDC2Scriptcontent     = &lt;&lt;-EOT
							

							
								Add-PSSnapin Citrix.Storefront.*
							

							
								Start-Service -Name CitrixClusterService
							

							
								$Passcode = Get-Content -Path '${var.CVAD_Install_LogPath}/pc.txt'
							

							
								Start-STFServerGroupJoin -AuthorizerHostName ${var.CVAD-DDC1-Name}.the-austrian-citrix-guy.at -Passcode $Passcode -Confirm:$false
							

							
								Wait-STFServerGroupJoin -WaitTimeout 20
							

							
								Write-Output 'Replication complete.'
							

							
								  }
							

							
								  EOT
							
							 

							
								#### Create the Add-a-DNS-Host-Entry script
							

							
								#### Need to invoke PowerShell as Domain User as the provisioner does not allow to be run in a Domain Users-context, but CVAD SDK needs to be run as a Domain User
							

							
								CreateDNSARecordScriptcontent     = &lt;&lt;-EOT
							

							
								$PSUsername = '${var.NTNX_domain_admin_userwithdomain}'
							

							
								$PSPassword = '${var.NTNX_domain_admin_pw}'
							

							
								$PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
							

							
								$PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
							

							
								Invoke-Command -ComputerName localhost -Credential $PSCredential -ScriptBlock {
							

							
								#Create DNS-A-Record for SF-HostBaseURL-Loadbalancing-IP
							

							
								Add-DnsServerResourceRecordA -Name '${var.CVAD-DDC1-Name}' -ZoneName 'the-austrian-citrix-guy.at' -IPv4Address ${var.CVAD-SF-FarmName-LBIP}
							

							
								} 
							

							
								EOT
							
							 

							
								}
							
							 

							
								#### Write CVAD Installation script into local data-directory
							

							
								resource "local_file" "WriteCVADScriptIntoDataDirectory" {
							

							
								  #depends_on = [ NTNX_virtual_machine.ddc1-vm, NTNX_virtual_machine.ddc2-vm ]
							

							
								  filename = "${path.module}/data/CVAD-Install.ps1"
							

							
								  content  = local.CVADScriptcontent
							

							
								}
							
							 

							
								#### Write StoreFront Installation script into local data-directory
							

							
								resource "local_file" "WriteSFScriptIntoDataDirectory" {
							

							
								  #depends_on = [ NTNX_virtual_machine.ddc1-vm, NTNX_virtual_machine.ddc2-vm ]
							

							
								  filename = "${path.module}/data/SF-Install.ps1"
							

							
								  content  = local.SFScriptcontent
							

							
								}
							
							 

							
								#### Write StoreFront configuration script into local data-directory
							

							
								resource "local_file" "StoreFrontConfigurationScriptIntoDataDirectory" {
							

							
								  #depends_on = [ NTNX_virtual_machine.ddc1-vm, NTNX_virtual_machine.ddc2-vm ]
							

							
								  filename = "${path.module}/data/SF-Config.ps1"
							

							
								  content  = local.ConfigureStorefrontScriptcontent
							

							
								}
							
							 

							
								#### Write Reboot script into local data-directory
							

							
								resource "local_file" "WriteRebootScriptIntoDataDirectory" {
							

							
								  #depends_on = [ NTNX_virtual_machine.ddc1-vm, NTNX_virtual_machine.ddc2-vm ]
							

							
								  filename = "${path.module}/data/Reboot.ps1"
							

							
								  content  = local.RebootScriptcontent
							

							
								}
							
							 

							
								#### Write the Add-a-DNS-Host-Entry script into local data-directory
							

							
								resource "local_file" "WriteLicScriptIntoDataDirectory" {
							

							
								  filename = "${path.module}/data/CVAD-CreateDNSARecordOnDC.ps1"
							

							
								  content  = local.CreateDNSARecordScriptcontent
							

							
								}
							
							 

							
								#### Write Database script into local data-directory
							

							
								resource "local_file" "WriteCVADDBScriptIntoDataDirectory" {
							

							
								  #depends_on = [ NTNX_virtual_machine.ddc1-vm, NTNX_virtual_machine.ddc2-vm ]
							

							
								  filename = "${path.module}/data/CVAD-DBs.ps1"
							

							
								  content  = local.CVADDBScriptcontent
							

							
								}
							
							 

							
								#### Write DDC1-Task-Creation script into local data-directory
							

							
								resource "local_file" "WriteDDC1SFTaskScriptIntoDataDirectory" {
							

							
								  #depends_on = [ NTNX_virtual_machine.ddc1-vm, NTNX_virtual_machine.ddc2-vm ]
							

							
								  filename = "${path.module}/data/SF-Task-StartReplication-DDC1.ps1"
							

							
								  content  = local.CreateStoreFrontReplicationTaskonDDC1Scriptcontent
							

							
								}
							
							 

							
								#### Write DDC2-Task-Creation script into local data-directory
							

							
								resource "local_file" "WriteDDC2SFTaskScriptIntoDataDirectory" {
							

							
								  #depends_on = [ NTNX_virtual_machine.ddc1-vm, NTNX_virtual_machine.ddc2-vm ]
							

							
								  filename = "${path.module}/data/SF-Task-StartReplication-DDC2.ps1"
							

							
								  content  = local.CreateStoreFrontReplicationTaskonDDC2Scriptcontent
							

							
								}
							
							 

							
								#### Write DDC1-Task-Run script script into local data-directory
							

							
								resource "local_file" "WriteDDC1SFTaskRunScriptIntoDataDirectory" {
							

							
								  #depends_on = [ NTNX_virtual_machine.ddc1-vm, NTNX_virtual_machine.ddc2-vm ]
							

							
								  filename = "${path.module}/data/SF-CMD-EnableReplication.ps1"
							

							
								  content  = local.CreateStoreFrontEnableReplicationTaskonDDC1Scriptcontent
							

							
								}
							
							 

							
								#### Write DDC2-Task-Run script into local data-directory
							

							
								resource "local_file" "WriteDDC2SFTaskRunScriptIntoDataDirectory" {
							

							
								  #depends_on = [ NTNX_virtual_machine.ddc1-vm, NTNX_virtual_machine.ddc2-vm ]
							

							
								  filename = "${path.module}/data/SF-CMD-StartReplication.ps1"
							

							
								  content  = local.CreateStoreFrontStartReplicationTaskonDDC2Scriptcontent
							

							
								}
							
							 

							
								##### Upload CVAD-Installation scripts to DDC1 and execute it
							

							
								###### Set the Provisioner-Connection
							

							
								resource "null_resource" "InstallCVADFromCDROMOnDDC1" {
							

							
								  depends_on = [ local_file.WriteCVADScriptIntoDataDirectory ]
							

							
								  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 CVAD-Installation script to DDC1
							

							
								  provisioner "file" {
							

							
								    source      = "${path.module}/data/CVAD-Install.ps1"
							

							
								    destination = "${var.CVAD_Install_LogPath}/CVAD-Install.ps1"
							

							
								    
							

							
								  }
							
							 

							
								###### Execute the CVAD Installation script
							

							
								 provisioner "remote-exec" {
							

							
								    inline = [
							

							
								      "powershell -File ${var.CVAD_Install_LogPath}/CVAD-Install.ps1"
							

							
								    ]
							

							
								  }
							

							
								}
							
							 

							
								##### Upload CVAD Installation script to DDC2 and execute it
							

							
								###### Set the Provisioner-Connection
							

							
								resource "null_resource" "InstallCVADFromCDROMOnDDC2" {
							

							
								  depends_on = [ local_file.WriteCVADScriptIntoDataDirectory ]
							

							
								  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 CVAD-Installation script to DDC2
							

							
								  provisioner "file" {
							

							
								    source      = "${path.module}/data/CVAD-Install.ps1"
							

							
								    destination = "${var.CVAD_Install_LogPath}/CVAD-Install.ps1"
							

							
								    
							

							
								  }
							
							 

							
								###### Execute the CVAD Installation script
							

							
								  provisioner "remote-exec" {
							

							
								    inline = [
							

							
								      "powershell -File ${var.CVAD_Install_LogPath}/CVAD-Install.ps1"
							

							
								    ]
							

							
								  }
							

							
								} 
							
							 

							
								### Wait for 2 minutes to complete reboot and settling of all tasks on DDC-VMs
							

							
								resource "time_sleep" "WaitToRebootDDC1AfterCVADInstall" {
							

							
								  depends_on = [ null_resource.InstallCVADFromCDROMOnDDC1 ]
							

							
								  create_duration = "600s"
							

							
								}
							
							 

							
								resource "time_sleep" "WaitToRebootDDC2AfterCVADInstall" {
							

							
								  depends_on = [ null_resource.InstallCVADFromCDROMOnDDC2 ]
							

							
								  create_duration = "600s"
							

							
								} 
							
							 

							
								##### Reboot DDC1 
							

							
								###### Set the Provisioner-Connection
							

							
								resource "null_resource" "RebootDDC1" {
							

							
								  depends_on = [ time_sleep.WaitToRebootDDC1AfterCVADInstall ]
							
							 

							
								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 Reboot script to DDC1
							

							
								  provisioner "file" {
							

							
								    source      = "${path.module}/data/Reboot.ps1"
							

							
								    destination = "${var.CVAD_Install_LogPath}/Reboot.ps1"
							

							
								    
							

							
								  }
							
							 

							
								###### Execute the Reboot script
							

							
								  provisioner "remote-exec" {
							

							
								    inline = [
							

							
								      "powershell -File ${var.CVAD_Install_LogPath}/Reboot.ps1"
							

							
								    ]
							

							
								  }
							

							
								}
							
							 

							
								##### Reboot DDC2 
							

							
								###### Set the Provisioner-Connection
							

							
								resource "null_resource" "RebootDDC2" {
							

							
								  depends_on = [ time_sleep.WaitToRebootDDC2AfterCVADInstall ]
							
							 

							
								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 Reboot script to DDC2
							

							
								  provisioner "file" {
							

							
								    source      = "${path.module}/data/Reboot.ps1"
							

							
								    destination = "${var.CVAD_Install_LogPath}/Reboot.ps1"
							

							
								    
							

							
								  }
							
							 

							
								###### Execute the Reboot script
							

							
								  provisioner "remote-exec" {
							

							
								    inline = [
							

							
								      "powershell -File ${var.CVAD_Install_LogPath}/Reboot.ps1"
							

							
								    ]
							

							
								  }
							

							
								}
							
							 

							
								### Wait for 2 minutes to complete reboot and settling of all tasks on DDC-VMs
							

							
								resource "time_sleep" "WaitForRebootofDDC1" {
							

							
								  depends_on = [ null_resource.RebootDDC1 ]
							

							
								  create_duration = "120s"
							

							
								}
							
							 

							
								resource "time_sleep" "WaitForRebootofDDC2" {
							

							
								  depends_on = [ null_resource.RebootDDC2 ]
							

							
								  create_duration = "120s"
							

							
								} 
							
							 

							
								### Create the databases for CVAD  
							

							
								#### Upload DB-Creation script to DDC1 and execute it
							

							
								###### Set the Provisioner-Connection
							

							
								resource "null_resource" "CreateCVADDBs" {
							

							
								 depends_on = [ time_sleep.WaitForRebootofDDC1 ]
							

							
								 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 DB-Creation script to DDC1
							

							
								  provisioner "file" {
							

							
								    source      = "${path.module}/data/CVAD-DBs.ps1"
							

							
								    destination = "${var.CVAD_Install_LogPath}/CVAD-DBs.ps1"
							

							
								    
							

							
								  }
							
							 

							
								###### Execute the DB-Creation script to DDC1
							

							
								 provisioner "remote-exec" {
							

							
								    inline = [
							

							
								      "powershell -File ${var.CVAD_Install_LogPath}/CVAD-DBs.ps1"
							

							
								    ]
							

							
								  }
							

							
								}  
							
							 

							
								### Install Storefront on DDC1  
							

							
								#### Upload SF-Installation script to DDC1 and execute it
							

							
								###### Set the Provisioner-Connection
							

							
								resource "null_resource" "InstallSFOnDDC1" {
							

							
								 #depends_on = [ null_resource.CreateCVADDBs ]
							

							
								 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 SF-Installation and SF-Configuration scripts to DDC1
							

							
								  provisioner "file" {
							

							
								    source      = "${path.module}/data/SF-Install.ps1"
							

							
								    destination = "${var.CVAD_Install_LogPath}/SF-Install.ps1"
							

							
								    
							

							
								  }
							
							 

							
								/*   provisioner "file" {
							

							
								    source      = "${path.module}/data/SF-Config.ps1"
							

							
								    destination = "${var.CVAD_Install_LogPath}/SF-Config.ps1"
							

							
								    
							

							
								  } */
							
							 

							
								###### Execute the SF-Installation script on DDC1
							

							
								 provisioner "remote-exec" {
							

							
								    inline = [
							

							
								      "powershell -File ${var.CVAD_Install_LogPath}/SF-Install.ps1"
							

							
								    ]
							

							
								  }
							

							
								} 
							

							
								### Install Storefront on DDC2  
							

							
								#### Upload SF-Installation script to DDC2 and execute it
							

							
								###### Set the Provisioner-Connection
							

							
								resource "null_resource" "InstallSFOnDDC2" {
							

							
								 #depends_on = [ null_resource.CreateCVADDBs ]
							

							
								 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 SF-Installation and SF-Configuration scripts to DDC2
							

							
								  provisioner "file" {
							

							
								    source      = "${path.module}/data/SF-Install.ps1"
							

							
								    destination = "${var.CVAD_Install_LogPath}/SF-Install.ps1"
							

							
								    
							

							
								  }
							
							 

							
								/*   provisioner "file" {
							

							
								    source      = "${path.module}/data/SF-Config.ps1"
							

							
								    destination = "${var.CVAD_Install_LogPath}/SF-Config.ps1"
							

							
								    
							

							
								  } */
							
							 

							
								###### Execute the SF-Installation script on DDC2
							

							
								 provisioner "remote-exec" {
							

							
								    inline = [
							

							
								      "powershell -File ${var.CVAD_Install_LogPath}/SF-Install.ps1"
							

							
								    ]
							

							
								  }
							

							
								} 
							
							 

							
								### Reboot DDC1 and DDC2 after Storefront-Installation   
							

							
								##### Set the Provisioner-Connection
							

							
								resource "null_resource" "RebootDDC1AfterStoreFrontInstall" {
							

							
								 depends_on = [ null_resource.InstallSFOnDDC1 ]
							

							
								 connection {
							

							
								    type            = var.Provisioner_Type
							

							
								    user            = var.Provisioner_Admin-Username
							

							
								    password        = var.Provisioner_Admin-Password
							

							
								    host            = var.Provisioner_DDC1-IP
							

							
								    timeout         = var.Provisioner_Timeout
							
							 

							
								  }
							
							 

							
								###### Execute the Reboot script on DDC1
							

							
								 provisioner "remote-exec" {
							

							
								    inline = [
							

							
								      "powershell -File ${var.CVAD_Install_LogPath}/Reboot.ps1"
							

							
								    ]
							

							
								  }
							

							
								} 
							
							 

							
								##### Set the Provisioner-Connection
							

							
								resource "null_resource" "RebootDDC2AfterStoreFrontInstall" {
							

							
								 depends_on = [ null_resource.InstallSFOnDDC2 ]
							

							
								 connection {
							

							
								    type            = var.Provisioner_Type
							

							
								    user            = var.Provisioner_Admin-Username
							

							
								    password        = var.Provisioner_Admin-Password
							

							
								    host            = var.Provisioner_DDC2-IP
							

							
								    timeout         = var.Provisioner_Timeout
							
							 

							
								  }
							
							 

							
								##### Execute the Reboot script on DDC2
							

							
								 provisioner "remote-exec" {
							

							
								    inline = [
							

							
								      "powershell -File ${var.CVAD_Install_LogPath}/Reboot.ps1"
							

							
								    ]
							

							
								  }
							

							
								} 
							
							 

							
								### Wait for 2 minutes to complete reboot and settling of all tasks on DDC-VMs
							

							
								resource "time_sleep" "WaitForRebootofDDC1AfterStoreFrontInstall" {
							

							
								  depends_on = [ null_resource.RebootDDC1AfterStoreFrontInstall ]
							

							
								  create_duration = "120s"
							

							
								}
							
							 

							
								resource "time_sleep" "WaitForRebootofDDC2AfterStorefrontInstall" {
							

							
								  depends_on = [ null_resource.RebootDDC2AfterStoreFrontInstall ]
							

							
								  create_duration = "120s"
							

							
								} 
							
							 

							
								/* ### Configure Storefront on DDC1  
							

							
								#### Set the Provisioner-Connection
							

							
								resource "null_resource" "ConfigureSFOnDDC1" {
							

							
								 depends_on = [ time_sleep.WaitForRebootofDDC1AfterStoreFrontInstall ]
							

							
								 connection {
							

							
								    type            = var.Provisioner_Type
							

							
								    user            = var.Provisioner_Admin-Username
							

							
								    password        = var.Provisioner_Admin-Password
							

							
								    host            = var.Provisioner_DDC1-IP
							

							
								    timeout         = var.Provisioner_Timeout
							
							 

							
								  }
							
							 

							
								##### Execute the SF-Configuration script on DDC1
							

							
								 provisioner "remote-exec" {
							

							
								    inline = [
							

							
								      "powershell -File ${var.CVAD_Install_LogPath}/SF-Config.ps1"
							

							
								    ]
							

							
								  }
							

							
								} 
							
							 

							
								### Reboot DDC1 after Storefront-Configuration   
							

							
								#### Set the Provisioner-Connection
							

							
								resource "null_resource" "RebootDDC1AfterStoreFrontConfiguration" {
							

							
								 depends_on = [ null_resource.ConfigureSFOnDDC1 ]
							

							
								 connection {
							

							
								    type            = var.Provisioner_Type
							

							
								    user            = var.Provisioner_Admin-Username
							

							
								    password        = var.Provisioner_Admin-Password
							

							
								    host            = var.Provisioner_DDC1-IP
							

							
								    timeout         = var.Provisioner_Timeout
							
							 

							
								  }
							
							 

							
								##### Execute the Reboot script on DDC1
							

							
								 provisioner "remote-exec" {
							

							
								    inline = [
							

							
								      "powershell -File ${var.CVAD_Install_LogPath}/Reboot.ps1"
							

							
								    ]
							

							
								  }
							

							
								}
							
							 

							
								### Wait for 2 minutes to complete reboot and settling of all tasks on DDC-VMs
							

							
								resource "time_sleep" "WaitForRebootofDDC1AfterStoreFrontConfiguration" {
							

							
								  depends_on = [ null_resource.RebootDDC1AfterStoreFrontConfiguration ]
							

							
								  create_duration = "120s"
							

							
								}
							

							
								 */
							

							
								########### This completes the Storefront configuration. 
							

							
								########### We need to start the replication of the Storefront CLuster. 
							
							 

							
								/* ##### Upload Task-Creation and Task-Run scripts for enabling replication to DDC1 and execute them
							

							
								###### Set the Provisioner-Connection
							

							
								resource "null_resource" "SFTasksOnDDC1" {
							

							
								  depends_on = [ local_file.WriteCVADScriptIntoDataDirectory ]
							

							
								  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 Task-Creation script to DDC1
							

							
								  provisioner "file" {
							

							
								    source      = "${path.module}/data/SF-Task-StartReplication-DDC1.ps1"
							

							
								    destination = "${var.CVAD_Install_LogPath}/data/SF-Task-StartReplication-DDC1.ps1"
							

							
								    
							

							
								  }
							
							 

							
								###### Upload Task-Run script to DDC1
							

							
								  provisioner "file" {
							

							
								    source      = "${path.module}/data/SF-CMD-EnableReplication.ps1"
							

							
								    destination = "${var.CVAD_Install_LogPath}/data/SF-CMD-EnableReplication.ps1"
							

							
								    
							

							
								  }
							
							 

							
								###### Execute the Task-Creation script
							

							
								 provisioner "remote-exec" {
							

							
								    inline = [
							

							
								      "powershell -File ${var.CVAD_Install_LogPath}/SF-Task-StartReplication-DDC1.ps1"
							

							
								    ]
							

							
								  }
							
							 

							
								###### Execute the Task-Run script
							

							
								 provisioner "remote-exec" {
							

							
								    inline = [
							

							
								      "powershell -File ${var.CVAD_Install_LogPath}/SF-CMD-EnableReplication.ps1"
							

							
								    ]
							

							
								  }
							
							 

							
								}
							
							 

							
								##### Upload Task-Creation and Task-Run scripts for enabling replication to DDC2 and execute them
							

							
								###### Set the Provisioner-Connection
							

							
								resource "null_resource" "SFTasksOnDDC2" {
							

							
								  depends_on = [ local_file.WriteCVADScriptIntoDataDirectory ]
							

							
								  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 Task-Creation script to DDC2
							

							
								  provisioner "file" {
							

							
								    source      = "${path.module}/data/SF-Task-StartReplication-DDC2.ps1"
							

							
								    destination = "${var.CVAD_Install_LogPath}/data/SF-Task-StartReplication-DDC2.ps1"
							

							
								    
							

							
								  }
							
							 

							
								###### Upload Task-Run script to DDC1
							

							
								  provisioner "file" {
							

							
								    source      = "${path.module}/data/SF-CMD-StartReplication.ps1"
							

							
								    destination = "${var.CVAD_Install_LogPath}/data/SF-CMD-StartReplication.ps1"
							

							
								    
							

							
								  }
							
							 

							
								###### Execute the Task-Creation script
							

							
								 provisioner "remote-exec" {
							

							
								    inline = [
							

							
								      "powershell -File ${var.CVAD_Install_LogPath}/SF-Task-StartReplication-DDC2.ps1"
							

							
								    ]
							

							
								  }
							
							 

							
								###### Execute the Task-Run script
							

							
								 provisioner "remote-exec" {
							

							
								    inline = [
							

							
								      "powershell -File ${var.CVAD_Install_LogPath}/SF-CMD-StartReplication.ps1"
							

							
								    ]
							

							
								  }
							
							 

							
								}
							
							 

							
								### Reboot DDC1 and DDC2 after Storefront-Replication   
							

							
								##### Set the Provisioner-Connection
							

							
								resource "null_resource" "RebootDDC1AfterStoreFrontReplication" {
							

							
								 depends_on = [ null_resource.SFTasksOnDDC1 ]
							

							
								 connection {
							

							
								    type            = var.Provisioner_Type
							

							
								    user            = var.Provisioner_Admin-Username
							

							
								    password        = var.Provisioner_Admin-Password
							

							
								    host            = var.Provisioner_DDC1-IP
							

							
								    timeout         = var.Provisioner_Timeout
							
							 

							
								  }
							
							 

							
								###### Execute the Reboot script on DDC1
							

							
								 provisioner "remote-exec" {
							

							
								    inline = [
							

							
								      "powershell -File ${var.CVAD_Install_LogPath}/Reboot.ps1"
							

							
								    ]
							

							
								  }
							

							
								} 
							
							 

							
								### Reboot DDC1 and DDC2 after Storefront-Installation   
							

							
								##### Set the Provisioner-Connection
							

							
								resource "null_resource" "RebootDDC2AfterStoreFrontReplication" {
							

							
								 depends_on = [ null_resource.SFTasksOnDDC2 ]
							

							
								 connection {
							

							
								    type            = var.Provisioner_Type
							

							
								    user            = var.Provisioner_Admin-Username
							

							
								    password        = var.Provisioner_Admin-Password
							

							
								    host            = var.Provisioner_DDC2-IP
							

							
								    timeout         = var.Provisioner_Timeout
							
							 

							
								  }
							
							 

							
								###### Execute the Reboot script on DDC1
							

							
								 provisioner "remote-exec" {
							

							
								    inline = [
							

							
								      "powershell -File ${var.CVAD_Install_LogPath}/Reboot.ps1"
							

							
								    ]
							

							
								  }
							

							
								} 
							
							 

							
								### Upload and execute the Add-a-DNS-Host-Entry script on DC 
							

							
								resource "null_resource" "CreateDNSRecord" {
							

							
								connection {
							

							
								    type            = var.Provisioner_Type
							

							
								    user            = var.Provisioner_Admin-Username
							

							
								    password        = var.Provisioner_Admin-Password
							

							
								    host            = var.Provisioner_DC-IP
							

							
								    timeout         = var.Provisioner_Timeout
							
							 

							
								  }
							
							 

							
								   provisioner "file" {
							

							
								    source      = "${path.module}/data/CVAD-CreateDNSARecordOnDC.ps1"
							

							
								    destination = "${var.CVAD_Install_LogPath}/CVAD-CreateDNSARecordOnDC.ps1"
							

							
								    
							

							
								  }
							
							 

							
								provisioner "remote-exec" {
							

							
								    inline = [
							

							
								      "powershell -File ${var.CVAD_Install_LogPath}/CVAD-CreateDNSARecordOnDC.ps1"
							

							
								    ]
							

							
								  }
							

							
								}
							
							 

							
								###########################################################################################
							

							
								########### This finally completes the Storefront part of the Terraform script. ###########
							

							
								###########################################################################################
							

							
								 */ 
							
						
					
				
			
		

		
			 
		 

		
			Module 3: _CVADOnNutanix-Configuration-Storefront
		

		
			These are the Terraform configuration files for Module 3 (excerpts):
		 

		
			_CVADOnNutanix-Configuration-StoreFront.tf
		

		
			
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}			
			
				
					
						
							
								
									
										
											
												# Terraform deployment of Citrix Virtual Apps and Desktops on Nutanix AHV
											

											
												## Definition of all required Terraform providers
											
											 

											
												terraform {
											

											
												    required_version = "&gt;= 1.9.4"
											
											 

											
												  required_providers {
											

											
												     nutanix = {
											

											
												      source  = "nutanix/nutanix"
											

											
												      version = "= 1.9.5"
											

											
												    }
											
											 

											
												    citrix = {
											

											
												      source  = "citrix/citrix"
											

											
												      version = "=1.0.0"
											

											
												    }
											
											 

											
												  }
											

											
												}
											
											 

											
												provider "citrix" {
											

											
												  cvad_config = {
											

											
												     hostname                  = var.CVAD_Provider-FQDN
											

											
												     client_id                 = var.CVAD_Provider-UN
											

											
												     client_secret             = var.CVAD_Provider-PW
											

											
												     disable_ssl_verification  = true
											

											
												  }
											
											 

											
												      storefront_remote_host = {
											

											
												      computer_name              = var.CVAD_Provider-FQDN
											

											
												      ad_admin_username          = var.CVAD_Provider-UN
											

											
												      ad_admin_password          = var.CVAD_Provider-PW
											

											
												    }
											

											
												}
											
											 

											
												provider "nutanix" {
											

											
												  username                  = var.Nutanix_Provider-Username
											

											
												  password                  = var.Nutanix_Provider-Password
											

											
												  endpoint                  = var.Nutanix_Provider-Endpoint
											

											
												  port                      = var.Nutanix_Provider-Port
											

											
												  insecure                  = var.Nutanix_Provider-Insecure
											

											
												  wait_timeout              = var.Nutanix_Provider-Timeout
											
											 

											
												}
											
										
									
								
							
						
					
				
			
		

		
			_CVADOnNutanix-Configuration-StoreFront.tf
		

		
			
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}			
		

		
			
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}			
			
				
					
						
							
								
									
										
											
												# Terraform deployment of Citrix Virtual Apps and Desktops on Nutanix AHV
											

											
												## Install CVAD and SF with all chosen components and create a new site
											

											
												locals {
											
											 

											
												### Delete existing SF deployments
											

											
												#### Create Storefront-Configuration script#### Need to invoke PowerShell as Domain User as the provisioner does not allow to be run in a Domain Users-context, but CVAD SDK needs to be run as a Domain User
											

											
												ClearStorefrontScriptcontent     = &lt;&lt;-EOT
											

											
												$PSUsername = 'tacg\administrator'
											

											
												$PSPassword = 'XXXXXXXXXX'
											

											
												$PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
											

											
												$PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
											

											
												Invoke-Command -ComputerName localhost -Credential $PSCredential -ScriptBlock {
											

											
												Add-PSSnapin Citrix.*
											

											
												#Clear-Existing StoreFront deployments
											

											
												Clear-STFDeployment -Confirm:$false
											

											
												Write-Output 'Existing StoreFront deployments deleted.'
											

											
												}
											

											
												EOT
											
											 

											
												}
											
											 

											
												#### Write StoreFront deletion script into local data-directory
											

											
												resource "local_file" "StoreFrontDeletionScriptIntoDataDirectory" {
											

											
												  filename = "${path.module}/data/SF-Deletion.ps1"
											

											
												  content  = local.ClearStorefrontScriptcontent
											

											
												}
											
											 

											
												##### Upload StoreFront deletion script to DDC1 and execute it
											

											
												###### Set the Provisioner-Connection
											

											
												resource "null_resource" "UploadAndRunSFDeletionScriptOnDDC1" {
											

											
												  depends_on = [ local_file.StoreFrontDeletionScriptIntoDataDirectory ]
											

											
												  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 CVAD-Installation script to DDC1
											

											
												  provisioner "file" {
											

											
												    source      = "${path.module}/data/SF-Deletion.ps1"
											

											
												    destination = "${var.CVAD_Install_LogPath}/SF-Deletion.ps1"
											

											
												    
											

											
												  }
											
											 

											
												###### Execute the CVAD Installation script
											

											
												 provisioner "remote-exec" {
											

											
												    inline = [
											

											
												      "powershell -File ${var.CVAD_Install_LogPath}/SF-Deletion.ps1"
											

											
												    ]
											

											
												  }
											

											
												}
											
											 

											
												##### Upload StoreFront deletion script to DDC2 and execute it
											

											
												###### Set the Provisioner-Connection
											

											
												resource "null_resource" "UploadAndRunSFDeletionScriptOnDDC2" {
											

											
												  depends_on = [ local_file.StoreFrontDeletionScriptIntoDataDirectory ]
											

											
												  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 CVAD-Installation script to DDC1
											

											
												  provisioner "file" {
											

											
												    source      = "${path.module}/data/SF-Deletion.ps1"
											

											
												    destination = "${var.CVAD_Install_LogPath}/SF-Deletion.ps1"
											

											
												    
											

											
												  }
											
											 

											
												###### Execute the CVAD Installation script
											

											
												 provisioner "remote-exec" {
											

											
												    inline = [
											

											
												      "powershell -File ${var.CVAD_Install_LogPath}/SF-Deletion.ps1"
											

											
												    ]
											

											
												  }
											

											
												}
											
											 

											
												### Create a deployment 
											

											
												resource "citrix_stf_deployment" "TACG-SF-InitialDeployment" {
											

											
												  depends_on = [ null_resource.UploadAndRunSFDeletionScriptOnDDC1 ]
											

											
												    site_id       = var.TACG-SF-SiteID
											

											
												    host_base_url = var.TACG-SF-BaseURL
											

											
												}
											
											 

											
												### Create an authentication service
											

											
												resource "citrix_stf_authentication_service" "TACG-SF-AuthenticationService" {
											

											
												  depends_on = [ citrix_stf_deployment.TACG-SF-InitialDeployment ]
											

											
												  site_id       = citrix_stf_deployment.TACG-SF-InitialDeployment.site_id
											

											
												  friendly_name = var.TACG-SF-AuthService-FriendlyName
											

											
												  virtual_path  = var.TACG-SF-AuthService-VirtualPath
											

											
												}
											
											 

											
												### Create a store service
											

											
												resource "citrix_stf_store_service" "TACG-SF-StoreService" {
											

											
												  depends_on = [ citrix_stf_authentication_service.TACG-SF-AuthenticationService ]
											

											
												  site_id      = citrix_stf_deployment.TACG-SF-InitialDeployment.site_id
											

											
												  friendly_name = var.TACG-SF-StoreService-FriendlyName
											

											
												  virtual_path  = var.TACG-SF-StoreService-VirtualPath
											

											
												  authentication_service_virtual_path = "${citrix_stf_authentication_service.TACG-SF-AuthenticationService.virtual_path}"
											

											
												  
											

											
												  pna = {
											

											
												      enable = var.TACG-SF-IsPNAEnabled
											

											
												    }
											
											 

											
												  farms = [
											

											
												    {
											

											
												      farm_name = var.TACG-SF-Farm-Name
											

											
												      farm_type = var.TACG-SF-Farm-Type
											

											
												      servers   = var.TACG-SF-Farm-Controller-FQDNs 
											

											
												      port      = 80
											

											
												      zones     = ["Primary"]
											

											
												    }
											

											
												  ]
											
											 

											
												}
											
											 

											
												### Create a webreceiver service
											

											
												resource "citrix_stf_webreceiver_service" "TACG-SF-WebReceiverService"{
											

											
												  depends_on = [ citrix_stf_store_service.TACG-SF-StoreService ]
											

											
												  site_id      = citrix_stf_deployment.TACG-SF-InitialDeployment.site_id
											

											
												  friendly_name = var.TACG-SF-WebReceiverService-FriendlyName
											

											
												  virtual_path  = var.TACG-SF-WebReceiverService-VirtualPath
											

											
												  store_virtual_path = "${citrix_stf_store_service.TACG-SF-StoreService.virtual_path}"
											
											 

											
												 }
											
										
									
								
							
						
					
				
			
		

		
			 
		 

		
			Module 4: _CVADOnNutanix-Configuration-AfterStorefront
		

		
			These are the Terraform configuration files for Module 4 (excerpts):
		 

		
			_CVADOnNutanix-Configuration-AfterStoreFront-scriptsa.tf
		

		
			
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}			
		

		
			
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}			
			
				
					
						
							
								
									
										
											
												# Terraform deployment of Citrix Virtual Apps and Desktops on Nutanix AHV
											

											
												## Run PowerShell-Scripts on the DDCs
											
											 

											
												### Install CVAD and SF with all chosen components and create a new site
											

											
												locals {
											

											
												  #### Create License- and PFX-Import script
											

											
												  #### Need to invoke PowerShell as Domain User as the provisioner does not allow to be run in a Domain Users-context, but CVAD SDK needs to be run as a Domain User
											

											
												  LicScriptcontent     = &lt;&lt;-EOT
											

											
												  $PSUsername = '${var.NTNX_domain_admin_userwithdomain}'
											

											
												  $PSPassword = '${var.NTNX_domain_admin_pw}'
											

											
												  $PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
											

											
												  $PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
											

											
												  Invoke-Command -ComputerName localhost -Credential $PSCredential -ScriptBlock {
											

											
												  Add-PSSnapin Citrix.*
											

											
												  Set-ConfigSite -LicenseServerUri '${var.CVAD-Lic-ServerAddress}' -LicenseServerPort '${var.CVAD-Lic-Port}' -ProductEdition '${var.CVAD-Lic-ProductEdition}'
											

											
												  Restart-Service -Name 'Citrix Licensing'
											

											
												  }
											

											
												  EOT
											
											 

											
												  #### Create PFX-Import script
											

											
												  #### Need to invoke PowerShell as Domain User as the provisioner does not allow to be run in a Domain Users-context, but CVAD SDK needs to be run as a Domain User
											

											
												  PFXScriptcontent     = &lt;&lt;-EOT
											

											
												  $PSUsername = '${var.NTNX_domain_admin_userwithdomain}'
											

											
												  $PSPassword = '${var.NTNX_domain_admin_pw}'
											

											
												  $PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
											

											
												  $PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
											

											
												  Invoke-Command -ComputerName localhost -Credential $PSCredential -ScriptBlock {
											

											
												  Add-PSSnapin Citrix.*
											

											
												  Import-PfxCertificate -CertStoreLocation Cert:\LocalMachine\Webhosting -FilePath '${var.CVAD_Install_LogPath}/${var.CVAD-PFX-Name}'
											

											
												  }
											

											
												  EOT
											
											 

											
												  #### Create script to change IIS certificate from self-signed certificate to public certificate 
											

											
												  #### Need to invoke PowerShell as Domain User as the provisioner does not allow to be run in a Domain Users-context, but CVAD SDK needs to be run as a Domain User
											

											
												  ChangeIISSSLScriptcontent     = &lt;&lt;-EOT
											

											
												  $PSUsername = '${var.NTNX_domain_admin_userwithdomain}'
											

											
												  $PSPassword = '${var.NTNX_domain_admin_pw}'
											

											
												  $PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
											

											
												  $PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
											

											
												  Invoke-Command -ComputerName localhost -Credential $PSCredential -ScriptBlock {
											

											
												  Remove-WebBinding -Name '${var.CVAD-IIS-SiteName}' -IP '*' -Port 443 -Protocol https
											

											
												  New-WebBinding -Name '${var.CVAD-IIS-SiteName}' -IP '*' -Port 443 -Protocol https
											

											
												  $SSLCert = Get-ChildItem -Path Cert:\LocalMachine\WebHosting | Where-Object {$_.Subject.Contains('${var.CVAD-IIS-CertSubject}')}
											

											
												  $Binding = Get-WebBinding -Name '${var.CVAD-IIS-SiteName}' -Protocol "https"
											

											
												  $Binding.AddSslCertificate($SSLCert.GetCertHashString(), "WebHosting")
											

											
												  IISReset
											

											
												  }
											

											
												  EOT
											
											 

											
												}
											
											 

											
												#### Write License- and PFX-Import script into local data-directory
											

											
												resource "local_file" "WriteLicScriptIntoDataDirectory" {
											

											
												  filename = "${path.module}/data/CVAD-Lic.ps1"
											

											
												  content  = local.LicScriptcontent
											

											
												}
											
											 

											
												#### Write PFX-Import script into local data-directory
											

											
												resource "local_file" "WritePFXScriptIntoDataDirectory" {
											

											
												  filename = "${path.module}/data/CVAD-PFX.ps1"
											

											
												  content  = local.PFXScriptcontent
											

											
												} 
											
											 

											
												#### Write PFX-Import script into local data-directory
											

											
												resource "local_file" "WriteIISSSLScriptIntoDataDirectory" {
											

											
												  filename = "${path.module}/data/CVAD-IIS.ps1"
											

											
												  content  = local.ChangeIISSSLScriptcontent
											

											
												}
											
											 

											
												### Upload license file, PFX certificate and Lic script to DDC1 and execute it
											

											
												#### Set the Provisioner-Connection
											

											
												 resource "null_resource" "CreateCVADLicPFXOnDDC1" {
											

											
												 depends_on = [ local_file.WriteLicScriptIntoDataDirectory, local_file.WritePFXScriptIntoDataDirectory, local_file.WriteIISSSLScriptIntoDataDirectory ]
											

											
												 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 License script to DDC1
											

											
												  provisioner "file" {
											

											
												    source      = "${path.module}/data/CVAD-Lic.ps1"
											

											
												    destination = "${var.CVAD_Install_LogPath}/CVAD-Lic.ps1"
											

											
												    
											

											
												  }
											
											 

											
												###### Upload PFX script to DDC1
											

											
												  provisioner "file" {
											

											
												    source      = "${path.module}/data/CVAD-PFX.ps1"
											

											
												    destination = "${var.CVAD_Install_LogPath}/CVAD-PFX.ps1"
											

											
												    
											

											
												  }
											
											 

											
												###### Upload PFX file to DDC1
											

											
												  provisioner "file" {
											

											
												    source      = "${path.module}/data/${var.CVAD-PFX-Name}"
											

											
												    destination = "${var.CVAD_Install_LogPath}/${var.CVAD-PFX-Name}"
											

											
												    
											

											
												  }
											
											 

											
												###### Upload IIS-SSLscript to DDC1
											

											
												  provisioner "file" {
											

											
												    source      = "${path.module}/data/CVAD-IIS.ps1"
											

											
												    destination = "${var.CVAD_Install_LogPath}/CVAD-IIS.ps1"
											

											
												    
											

											
												  }
											

											
												 
											

											
												###### Execute the License script to DDC1
											

											
												 provisioner "remote-exec" {
											

											
												    inline = [
											

											
												      "powershell -File ${var.CVAD_Install_LogPath}/CVAD-Lic.ps1"
											

											
												    ]
											

											
												  }
											
											 

											
												###### Execute the PFX script to DDC1
											

											
												 provisioner "remote-exec" {
											

											
												    inline = [
											

											
												      "powershell -File ${var.CVAD_Install_LogPath}/CVAD-PFX.ps1"
											

											
												    ]
											

											
												  }
											

											
												}
											

											
												 
											

											
												### Upload PFX certificate and PFX script to DDC2 and execute it
											

											
												#### Set the Provisioner-Connection
											

											
												 resource "null_resource" "CreateCVADPFXOnDDC2" {
											

											
												 depends_on = [ local_file.WriteLicScriptIntoDataDirectory, local_file.WritePFXScriptIntoDataDirectory, local_file.WriteIISSSLScriptIntoDataDirectory ]
											

											
												 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 PFX script to DDC2
											

											
												  provisioner "file" {
											

											
												    source      = "${path.module}/data/CVAD-PFX.ps1"
											

											
												    destination = "${var.CVAD_Install_LogPath}/CVAD-PFX.ps1"
											

											
												    
											

											
												  }
											
											 

											
												###### Upload PFX file to DDC2
											

											
												  provisioner "file" {
											

											
												    source      = "${path.module}/data/${var.CVAD-PFX-Name}"
											

											
												    destination = "${var.CVAD_Install_LogPath}/${var.CVAD-PFX-Name}"
											

											
												    
											

											
												  }
											
											 

											
												###### Upload IIS-SSLscript to DDC2
											

											
												  provisioner "file" {
											

											
												    source      = "${path.module}/data/CVAD-IIS.ps1"
											

											
												    destination = "${var.CVAD_Install_LogPath}/CVAD-IIS.ps1"
											

											
												    
											

											
												  }
											
											 

											
												###### Execute the PFX script onto DDC2
											

											
												 provisioner "remote-exec" {
											

											
												    inline = [
											

											
												      "powershell -File ${var.CVAD_Install_LogPath}/CVAD-PFX.ps1"
											

											
												    ]
											

											
												  }
											

											
												}
											
											 

											
												### Execute IIS-SSL script on DDC1 
											

											
												#### Set the Provisioner-Connection
											

											
												 resource "null_resource" "ChangeSSLOnDDC1" {
											

											
												 depends_on = [ null_resource.CreateCVADLicPFXOnDDC1 ]
											

											
												 connection {
											

											
												    type            = var.Provisioner_Type
											

											
												    user            = var.Provisioner_Admin-Username
											

											
												    password        = var.Provisioner_Admin-Password
											

											
												    host            = var.Provisioner_DDC1-IP
											

											
												    timeout         = var.Provisioner_Timeout
											
											 

											
												  }
											

											
												 
											

											
												###### Execute the IIS-SSL script on DDC1
											

											
												 provisioner "remote-exec" {
											

											
												    inline = [
											

											
												      "powershell -File ${var.CVAD_Install_LogPath}/CVAD-IIS.ps1"
											

											
												    ]
											

											
												  }
											

											
												}
											

											
												 
											

											
												### Execute IIS-SSL script on DDC2d execute it
											

											
												#### Set the Provisioner-Connection
											

											
												 resource "null_resource" "ChangeSSLOnDDC2" {
											

											
												 depends_on = [ null_resource.CreateCVADPFXOnDDC2 ]
											

											
												connection {
											

											
												    type            = var.Provisioner_Type
											

											
												    user            = var.Provisioner_Admin-Username
											

											
												    password        = var.Provisioner_Admin-Password
											

											
												    host            = var.Provisioner_DDC2-IP
											

											
												    timeout         = var.Provisioner_Timeout
											
											 

											
												  }
											

											
												 
											

											
												###### Execute the IIS-SSL script on DDC2
											

											
												 provisioner "remote-exec" {
											

											
												    inline = [
											

											
												      "powershell -File ${var.CVAD_Install_LogPath}/CVAD-IIS.ps1"
											

											
												    ]
											

											
												  }
											

											
												}
											
											 

											
												##### Reboot DDC1 
											

											
												###### Set the Provisioner-Connection
											

											
												resource "null_resource" "RebootDDC1-2" {
											

											
												  depends_on = [ null_resource.ChangeSSLOnDDC1 ]
											
											 

											
												connection {
											

											
												    type            = var.Provisioner_Type
											

											
												    user            = var.Provisioner_Admin-Username
											

											
												    password        = var.Provisioner_Admin-Password
											

											
												    host            = var.Provisioner_DDC1-IP
											

											
												    timeout         = var.Provisioner_Timeout
											
											 

											
												  }
											
											 

											
												###### Execute the Reboot script
											

											
												  provisioner "remote-exec" {
											

											
												    inline = [
											

											
												      "powershell -File ${var.CVAD_Install_LogPath}/Reboot.ps1"
											

											
												    ]
											

											
												  }
											

											
												}
											
											 

											
												##### Reboot DDC2 
											

											
												###### Set the Provisioner-Connection
											

											
												resource "null_resource" "RebootDDC2-2" {
											

											
												  depends_on = [ null_resource.ChangeSSLOnDDC2 ]
											
											 

											
												connection {
											

											
												    type            = var.Provisioner_Type
											

											
												    user            = var.Provisioner_Admin-Username
											

											
												    password        = var.Provisioner_Admin-Password
											

											
												    host            = var.Provisioner_DDC2-IP
											

											
												    timeout         = var.Provisioner_Timeout
											
											 

											
												  }
											
											 

											
												###### Execute the Reboot script
											

											
												  provisioner "remote-exec" {
											

											
												    inline = [
											

											
												      "powershell -File ${var.CVAD_Install_LogPath}/Reboot.ps1"
											

											
												    ]
											

											
												  }
											

											
												}
											
											 
											 
											 
											
												#########################################################################################################################################
											

											
												#########################################################################################################################################
											

											
												##                                                  That completes the initial site setup                                              ##
											

											
												#########################################################################################################################################
											

											
												#########################################################################################################################################
											
										
									
								
							
						
					
				
			
		

		
			Module 5: _CVADOnNutanix-CVAD-Entities
		

		
			These are the Terraform configuration files for Module 1 (excerpts):
		 

		
			_CVADOnNutanix-CVAD-Entities.tf
		

		
			
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}			
		

		
			
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}			
			
				
					
						
							
								
									
										
											
												# Terraform deployment of Citrix Virtual Apps and Desktops on Nutanix AHV
											

											
												## Create the Hosting Connection, a Machine Catalog and a Delivery Group
											

											
												### Create PowerShell file for downloading the Nutanix AHV-Plugin for Citrix on AVM 
											

											
												resource "local_file" "DownloadNTNXPluginToAVM" {
											

											
												content  = &lt;&lt;-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 "`nDownload of utanix AHV Plugin for Citrix started."
											

											
												# Download Nutanix AHV Plugin for Citrix
											

											
												Invoke-WebRequest '${var.CVAD_Install_AHVPluginURI}' -OutFile '${var.CC_Install_LogPath}/DATA/NutanixAHV_Citrix_Plugin.msi'
											

											
												Add-Content ${var.CC_Install_LogPath}/log.txt "`Nutanix AHV Plugin for Citrix downloaded."
											

											
												# 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/DownloadNTNXPluginToAVM.ps1"
											

											
												}
											
											 

											
												### Create PowerShell file for downloading the Nutanix AHV-Plugin installation script on AVM 
											

											
												resource "local_file" "DownloadNTNXPluginInstallationScriptToAVM" {
											

											
												content  = &lt;&lt;-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 "`nDownload of Nutanix AHV Plugin for Citrix started."
											

											
												# Download Nutanix AHV Plugin for Citrix
											

											
												Invoke-WebRequest '${var.CVAD_Install_AHVPluginInstallationScriptURI}' -OutFile '${var.CC_Install_LogPath}/DATA/InstallNutanixAHVPlugInOnCC.ps1'
											

											
												Add-Content ${var.CC_Install_LogPath}/log.txt "`Nutanix AHV Plugin installation script downloaded."
											

											
												# 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/InstallNutanixAHVPlugInOnCC.ps1"
											

											
												}
											
											 

											
												#### Execute Nutanix AHV-Plugin download script on AVM
											

											
												resource "null_resource" "ExecuteDownloadNutanixAHVPlugInOnAVM" {
											

											
												  depends_on = [ local_file.DownloadNTNXPluginToAVM ]
											

											
												  provisioner "local-exec" {
											

											
												    command = " ${var.CC_Install_LogPath}/DATA/DownloadNTNXPluginToAVM.ps1"
											

											
												    interpreter = ["PowerShell", "-Command"]
											

											
												  }
											

											
												}
											
											 

											
												#### Execute Nutanix AHV-Plugin installation script download script on AVM
											

											
												resource "null_resource" "ExecuteDownloadNTNXPluginInstallationScriptToAVM" {
											

											
												  depends_on = [ local_file.DownloadNTNXPluginInstallationScriptToAVM ]
											

											
												  provisioner "local-exec" {
											

											
												    command = " ${var.CC_Install_LogPath}/DATA/InstallNutanixAHVPlugInOnCC.ps1"
											

											
												    interpreter = ["PowerShell", "-Command"]
											

											
												  }
											

											
												}
											
											 

											
												### Install Nutanix AHV Plugin on CC1 
											

											
												#### Upload required components to CC1
											

											
												###### Set the Provisioner-Connection
											

											
												resource "null_resource" "UploadNutanixAHVToCC1" {
											

											
												 depends_on = [ null_resource.ExecuteDownloadNutanixAHVPlugInOnAVM ]
											

											
												 connection {
											

											
												    type            = var.Provisioner_Type
											

											
												    user            = var.Provisioner_Admin-Username
											

											
												    password        = var.Provisioner_Admin-Password
											

											
												    host            = var.Provisioner_CC1-IP
											

											
												  }
											
											 

											
												###### Upload Nutanix AHV-PlugIn file to CC1
											

											
												  provisioner "file" {
											

											
												    source      = "${var.CC_Install_LogPath}/DATA/NutanixAHV_Citrix_plugin.msi"
											

											
												    destination = "${var.CC_Install_LogPath}/DATA/NutanixAHV_Citrix_plugin.msi"
											

											
												    
											

											
												  } 
											
											 

											
												###### Upload Nutanix AHV-PlugIn installation script to CC1
											

											
												  provisioner "file" {
											

											
												    source      = "${var.CC_Install_LogPath}/DATA/InstallNutanixAHVPlugInOnCC.ps1"
											

											
												    destination = "${var.CC_Install_LogPath}/DATA/InstallNutanixAHVPlugInOnCC.ps1"
											

											
												    
											

											
												  } 
											

											
												}
											
											 

											
												### Install Nutanix AHV Plugin on CC2 
											

											
												#### Upload required components to CC2
											

											
												###### Set the Provisioner-Connection
											

											
												resource "null_resource" "UploadNutanixAHVToCC2" {
											

											
												 depends_on = [ null_resource.ExecuteDownloadNutanixAHVPlugInOnAVM ]
											

											
												 connection {
											

											
												    type            = var.Provisioner_Type
											

											
												    user            = var.Provisioner_Admin-Username
											

											
												    password        = var.Provisioner_Admin-Password
											

											
												    host            = var.Provisioner_CC2-IP
											

											
												  }
											
											 

											
												###### Upload Nutanix AHV-PlugIn file to CC2
											

											
												  provisioner "file" {
											

											
												    source      = "${var.CC_Install_LogPath}/DATA/NutanixAHV_Citrix_plugin.msi"
											

											
												    destination = "${var.CC_Install_LogPath}/DATA/NutanixAHV_Citrix_plugin.msi"
											

											
												    
											

											
												  } 
											
											 

											
												###### Upload Nutanix AHV-PlugIn installation script to CC2
											

											
												  provisioner "file" {
											

											
												    source      = "${var.CC_Install_LogPath}/DATA/InstallNutanixAHVPlugInOnCC.ps1"
											

											
												    destination = "${var.CC_Install_LogPath}/DATA/InstallNutanixAHVPlugInOnCC.ps1"
											

											
												    
											

											
												  } 
											

											
												}
											
											 

											
												#### Execute the PlugIn installation script on CC1
											

											
												resource "null_resource" "CallRequiredScriptsOnCC1" {
											

											
												 depends_on = [ null_resource.UploadNutanixAHVToCC1 ]
											

											
												 connection {
											

											
												    type            = var.Provisioner_Type
											

											
												    user            = var.Provisioner_Admin-Username
											

											
												    password        = var.Provisioner_Admin-Password
											

											
												    host            = var.Provisioner_CC1-IP
											

											
												}
											

											
												 
											

											
												  provisioner "remote-exec" {
											

											
												    inline = [
											

											
												      "powershell -File ${var.CC_Install_LogPath}/DATA/InstallNutanixAHVPlugInOnCC.ps1"
											

											
												    ]
											

											
												  } 
											

											
												} 
											
											 

											
												#### Execute the PlugIn installation script on CC2
											

											
												resource "null_resource" "CallRequiredScriptsOnCC2" {
											

											
												 depends_on = [ null_resource.UploadNutanixAHVToCC2 ]
											

											
												 connection {
											

											
												    type            = var.Provisioner_Type
											

											
												    user            = var.Provisioner_Admin-Username
											

											
												    password        = var.Provisioner_Admin-Password
											

											
												    host            = var.Provisioner_CC2-IP
											

											
												}
											

											
												 
											

											
												  provisioner "remote-exec" {
											

											
												    inline = [
											

											
												      "powershell -File ${var.CC_Install_LogPath}/DATA/InstallNutanixAHVPlugInOnCC.ps1"
											

											
												    ]
											

											
												  } 
											

											
												}
											
											 

											
												### Create the necessary zone
											

											
												resource "citrix_zone" "TACG-TF-HYP-Zone" {
											

											
												  depends_on = [ null_resource.CallRequiredScriptsOnCC1, null_resource.CallRequiredScriptsOnCC2 ]
											

											
												    name        = "${var.CVAD_HC-ZoneName}"
											

											
												}
											
											 

											
												output "ZoneID" {
											

											
												    value       = citrix_zone.TACG-TF-HYP-Zone.id
											

											
												    
											

											
												    } 
											

											
												#### Creating the Hypervisor Connection
											

											
												resource "citrix_nutanix_hypervisor" "CreateHypervisorConnection" {
											

											
												  depends_on = [ citrix_zone.TACG-TF-HYP-Zone]
											

											
												    name                        = var.CVAD_Nutanix-HypConn-Name
											

											
												    zone                        = citrix_zone.TACG-TF-HYP-Zone.id
											

											
												    username                    = var.CVAD_Nutanix-HypConn-UN
											

											
												    password                    = var.CVAD_Nutanix-HypConn-PW
											

											
												    password_format             = var.CVAD_Nutanix-HypConn-PWFormat
											

											
												    addresses                   = var.CVAD_Nutanix-HypConn-IP
											

											
												} 
											
											 

											
												#### Sleep 30s to let Background processes settle
											

											
												resource "time_sleep" "wait_30_seconds" {
											

											
												  depends_on = [ citrix_nutanix_hypervisor.CreateHypervisorConnection ]
											

											
												  create_duration = "30s"
											

											
												}
											
											 

											
												#### Creating the Hypervisor Resource Pool
											

											
												resource "citrix_nutanix_hypervisor_resource_pool" "CreateHypervisorPool" {
											

											
												  depends_on = [ time_sleep.wait_30_seconds]
											

											
												    name                        = var.CVAD_Nutanix-HypConnPool-Name
											

											
												    hypervisor                  = citrix_nutanix_hypervisor.CreateHypervisorConnection.id
											

											
												    networks                    = var.CVAD_Nutanix-HypConnPool-Networks       
											

											
												}  
											
											 

											
												#### Sleep 30s to let Background processes settle
											

											
												resource "time_sleep" "wait_30_seconds1" {
											

											
												  depends_on = [ citrix_nutanix_hypervisor_resource_pool.CreateHypervisorPool ]
											

											
												  create_duration = "30s"
											

											
												}
											
											 

											
												### Creating a Machine Catalog
											

											
												#### Create the Machine Catalog
											

											
												resource "citrix_machine_catalog" "CreateNTXMCSCatalog" {
											

											
												  depends_on            = [ time_sleep.wait_30_seconds1 ]
											

											
												    name                        = var.CVAD_Nutanix-MC-Name
											

											
												    description                 = var.CVAD_Nutanix-MC-Description
											

											
												    allocation_type             = var.CVAD_Nutanix-MC-AllocationType
											

											
												    session_support             = var.CVAD_Nutanix-MC-SessionType
											

											
												    provisioning_type           = "MCS"
											

											
												    zone                        = citrix_zone.TACG-TF-HYP-Zone.id
											

											
												    provisioning_scheme         =   {
											

											
												        hypervisor               = citrix_nutanix_hypervisor.CreateHypervisorConnection.id
											

											
												        hypervisor_resource_pool = citrix_nutanix_hypervisor_resource_pool.CreateHypervisorPool.id
											

											
												        identity_type            = var.CVAD_Nutanix-MC-IDPType
											

											
												        machine_domain_identity  = {
											

											
												            domain                   = var.CVAD_Nutanix-MC-Domain
											

											
												            domain_ou                = var.CVAD_Nutanix-MC-DomainOU
											

											
												            service_account          = var.CVAD_Nutanix-MC-DomainAdmin-Username-UPN
											

											
												            service_account_password = var.CVAD_Nutanix-MC-DomainAdmin-Password
											

											
												        }
											

											
												        nutanix_machine_config = {
											

											
												            master_image        = var.CVAD_Nutanix-MC-MasterImage
											

											
												            container           = var.CVAD_Nutanix-MC-Container
											

											
												            cpu_count           = var.CVAD_Nutanix-MC-CPUCount
											

											
												            cores_per_cpu_count = var.CVAD_Nutanix-MC-CoresPerCPU
											

											
												            memory_mb           = var.CVAD_Nutanix-MC-Memory
											

											
												        }
											

											
												        number_of_total_machines =  var.CVAD_Nutanix-MC-Machine_Count
											
											 

											
												        machine_account_creation_rules = {
											

											
												            naming_scheme      = var.CVAD_Nutanix-MC-Naming_Scheme_Name
											

											
												            naming_scheme_type = var.CVAD_Nutanix-MC-Naming_Scheme_Type
											

											
												        }
											

											
												    }
											

											
												}
											
											 

											
												#### Sleep 60s to let CC Background processes settle
											

											
												resource "time_sleep" "wait_30_seconds_2" {
											

											
												  depends_on = [ citrix_machine_catalog.CreateNTXMCSCatalog ]
											

											
												  create_duration = "30s"
											

											
												}   
											
											 

											
												#### Create the Delivery Group based on the Machine Catalog
											

											
												resource "citrix_delivery_group" "CreateDG" {
											

											
												  depends_on = [ time_sleep.wait_30_seconds_2]
											

											
												    name                                    = var.CVAD_Nutanix-DG-Name
											

											
												    associated_machine_catalogs             = [
											

											
												        {
											

											
												            machine_catalog                 = citrix_machine_catalog.CreateNTXMCSCatalog.id
											

											
												            machine_count                   = var.CVAD_Nutanix-MC-Machine_Count
											

											
												        }
											

											
												    ]
											

											
												    desktops                                = [
											

											
												        {
											

											
												            published_name                  = var.CVAD_Nutanix-DG-PublishedDesktopName
											

											
												            description                     = var.CVAD_Nutanix-DG-Description
											

											
												            restricted_access_users         = {
											

											
												                                               allow_list = [ "TACG\\vdaallowed" ]
											

											
												                                              }
											

											
												            enabled                         = true
											

											
												            enable_session_roaming          = var.CVAD_Nutanix-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.CVAD_Nutanix-DG-AS-Name
											

											
												                display_name                = var.CVAD_Nutanix-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\\vdaallowed" ]
											

											
												                                              }
											

											
												    reboot_schedules                        = [
											

											
												                                               {
											

											
												                                                 name = "TACG-NTX-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
											

											
												                                               }
											

											
												  ]
											

											
												}
											
											 
											 
											 
										
									
								
							
						
					
				
			
		

		
			 
		 
	


.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_08/sf-provider.png.ddcd88727407b958713c7153012284bf.png" length="66472" type="image/png"/><pubDate>Thu, 22 Aug 2024 12:43:00 +0000</pubDate></item><item><title>Deployment Guide: Using Terraform to deploy a Citrix Cloud Resource Location on Nutanix AHV</title><link>https://community.stage.citrix.com/tech-zone/automation/citrix-terraform-nutanix/</link><description><![CDATA[Using Terraform to deploy a Citrix Cloud Resource Location on Nutanix AHV



	Overview



	This guide will showcase the possibility of deploying a Citrix DaaS Resource Location on Nutanix AHV using Terraform. We want to reduce manual interventions to the absolute minimum. 
	 
	In the end, you will have created:
 


	
		A new Citrix Cloud Resource Location (RL) running on-premise on Nutanix AHV
	
	
		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 on Nutanix AHV
	
	
		A Machine Catalog and a Delivery Group in the just-created Resource Location
	



	 
 


	
		Important:
	 

	
		The related guide, "Using Terraform to deploy Citrix Virtual Apps and Desktops 2402 LTSR on Nutanix AHV", will soon be available on Citrix Tech Zone. 
		 
	 



	 
 


	Installation and Configuration of Terraform



	Please find a description of installing Terraform and initial configurations in our Tech Zone Deployment Guide Installing Terraform and Configuring the Citrix Terraform Provider.
 


	All Terraform configuration files can be found later on GitHub.
 


	 
	Deployment overview 
	 
	We use an existing domain in this guide and will not deploy a new one. 
	For further instructions on 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 primary Domain Controller using IPSec-based Site-to-Site VPNs. Each Resource Location has its subdomain. 
	 
 


	
		Note: 
	 

	
		We did not use loop-constructs or count keywords —each resource is created explicitly—for easier reading and understanding. 
		 
	 



	 
	The Terraform flow is split into different parts:
 


	
		Module One - this part can be run on any computer where Terraform is installed :

		
			
				Creating the initially needed Resources on Nutanix AHV:

				
					
						Creating two Windows Server 2022-based VMs used as Cloud Connector VMs in Module 2
					
					
						Creating a Windows Server 2022-based VM acting as an Administrative workstation for running Terraform Modules 2, 3, and 4 is necessary because WinRM is used for further configuration and deployment in these Modules.
					
					
						Creating all necessary scripts for joining the VMs to the existing sub-domain
					
					
						Putting the VMs into the existing sub-domain
					
				
			
		
	



	
		Important:
	 

	
		The Master Image must be configured before Terraform uses it to create the Machine Catalog. 
		The Administrative VM must have Terraform installed - use the Chocolately Guidance provided in our Tech Zone Deployment Guide Installing Terraform and Configuring the Citrix Terraform Provider. 
		 
	 



	
		Module Two - this part can only be run on the previously created Administrative VM as the deployment of Modules 2 and 3 relies heavily on WinRM:

		
			
				Configuring the three previously created Virtual Machines on Nutanix AHV:

				
					
						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 
						 
					
				
			
		
	
	
		Module Three - this part can only be run after the completion of Module Two:
		
			
				Installing the Nutanix AHV PlugIn for Citrix on the Cloud Connector VMs – this is a requirement for further configurations 
				 
			
		
	
	
		Module Four:
		
			
				Creating the Hypervisor Connection and the Hypervisor Resource Pool in Citrix Cloud:
				
					
						Retrieving the Site- and Zone-ID of the Resource Location
					
					
						Creating a dedicated Hypervisor Connection to Nutanix AHV
					
					
						Creating a dedicated Hypervisor Resource Pool
					
					
						Creating a Machine Catalog based on the referenced Master Image
					
					
						Creating a Delivery Group
					
					
						Creating AutoScale settings and applying these to the newly created Delivery Group
					
				
			
		
	



	
		Important:
	 

	
		Make sure that all Terraform-related VMs can communicate using WinRM. 
		The deployment fails if the Admin-VM cannot connect to the CCs using WinRM. 
		Various configuration guides for WinRM can be found on the Internet. 
		We rely on GPOs related to WinRM to configure all Domain Members accordingly. 
		 
	 






	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 td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\TACG&gt; test-wsman -ComputerName &lt;IP-Address of the computer you want to reach&gt; -Credential &lt;IP-Address of the computer you want to reach&gt;\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 &lt;IP-Address of the computer you want to reach&gt; -Credential &lt;IP-Address of the computer you wish to reach&gt;\administrator  
					 

					
						The response should look like:   
						[172.31.22.104]: PS C:\Users\Administrator\Documents&gt;
					 
				
			
		
	



	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 #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}	
	
		
			
				
					
						
							locals {
						 

						
							  #### Test the WinRM communication
						 

						
							  #### Need to invoke PowerShell as Domain User as the provisioner does not allow it to be run in a Domain Users-context
						 

						
							  TerraformTestWinRMScript     = &lt;&lt;-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 td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;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!  
					 

					
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
						null_resource.CreateTestScriptOnCC1 (remote-exec): C:\Users\Administrator&gt;powershell -File c:/temp/xdinst/DATA/Terraform-Test-WinRM.ps1
					 

					
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;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 that must be set. Some Configuration settings are propagated throughout the whole Terraform configuration...
 


	You must start each of the three 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.
 


	
		Important:
	 

	
		Each module/step must be completed successfully before the next module can be started. 
		 
	 



	 
 


	File System structure



	Root-Directory
 


	Module 1: _CCRLonNutanix-Creation:
 


	 
 


	
		
			
				
					Filename                                                                                         
				 
			
			
				
					Purpose                                                
				 
			
		
	
	
		
			
				
					_CCRLonNutanix-Creation-Create.tf
				 
			
			
				
					Resource configuration and primary flow definition
				 
			
		
		
			
				
					_CCRLonNutanix-Creation-Create-variables.tf
				 
			
			
				
					Definition of Variables
				 
			
		
		
			
				
					_CCRLonNutanix-Creation-Create.auto.tfvars.json
				 
			
			
				
					Setting the values of the Variables
				 
			
		
		
			
				
					_CCRLonNutanix-Creation-Provider.tf
				 
			
			
				
					Provider definition and configuration
				 
			
		
		
			
				
					_CCRLonNutanix-Creation-Provider-variables.tf
				 
			
			
				
					Definition of Variables
				 
			
		
		
			
				
					_CCRLonNutanix-Creation-Provider.auto.tfvars.json
				 
			
			
				
					Setting the values of the Variables
				 
			
		
	



	 
	Module 2: _CCRLonNutanix-Install:
 


	
		
			
				
					Filename                                                                                                        
				 
			
			
				
					Purpose                                               
				 
			
		
	
	
		
			
				
					_CCRLonNutanix-Install-CreatePreReqs.tf
				 
			
			
				
					Resource configuration and primary flow definition
				 
			
		
		
			
				
					_CCRLonNutanix-Install-CreatePreReqs-variables.tf
				 
			
			
				
					Definition of Variables
				 
			
		
		
			
				
					_CCRLonNutanix-Install-CreatePreReqs.auto.tfvars.json
				 
			
			
				
					Setting the values of the Variables
				 
			
		
		
			
				
					_CCRLonNutanix-Install-Provider.tf
				 
			
			
				
					Provider definition and configuration
				 
			
		
		
			
				
					_CCRLonNutanix-Install-Provider-variables.tf
				 
			
			
				
					Definition of Variables
				 
			
		
		
			
				
					_CCRLonNutanix-Install-Provider.auto.tfvars.json
				 
			
			
				
					Setting the values of the Variables
				 
			
		
		
			
				
					DATA Directory
				 
			
			
				
					Contains dynamically created scripts which will be uploaded and executed on the Cloud Connector VMs
				 
			
		
		
			
				
					 
				 
			
			
				
					 
				 
			
		
	



	Module 3: _CCRLonNutanix- NTNXPluginInstallation:
 


	
		
			
				
					Filename                                                                                                        
				 
			
			
				
					Purpose                                               
				 
			
		
	
	
		
			
				
					CCRLOnNutanix-Install-NTNXPluginInstallation.tf
				 
			
			
				
					Resource configuration and primary flow definition
				 
			
		
		
			
				
					CCRLOnNutanix-Install-NTNXPluginInstallation-variables.tf
				 
			
			
				
					Definition of Variables
				 
			
		
		
			
				
					CCRLOnNutanix-Install-NTNXPluginInstallation.auto.tfvars.json
				 
			
			
				
					Setting the values of the Variables
				 
			
		
		
			
				
					_CCRLonNutanix-Install-Provider.tf
				 
			
			
				
					Provider definition and configuration
				 
			
		
		
			
				
					_CCRLonNutanix-Install-Provider-variables.tf
				 
			
			
				
					Definition of Variables
				 
			
		
		
			
				
					_CCRLonNutanix-Install-Provider.auto.tfvars.json
				 
			
			
				
					Setting the values of the Variables
				 
			
		
	



	 
 


	Module 4: _CCRLonNutanix-CCStuff:
 


	
		
			
				
					Filename                                                                                                        
				 
			
			
				
					Purpose                                               
				 
			
		
	
	
		
			
				
					_CCRLonNutanix-CCStuff-CreateCCEntities.tf
				 
			
			
				
					Resource configuration and primary flow definition
				 
			
		
		
			
				
					_CCRLonNutanix-CCStuff-CreateCCEntities-variables.tf
				 
			
			
				
					Definition of Variables
				 
			
		
		
			
				
					_CCRLonNutanix-CCStuff-CreateCCEntities.auto.tfvars.json
				 
			
			
				
					Setting the values of the Variables
				 
			
		
		
			
				
					terraform.tvars
				 
			
			
				
					Setting the values of Variables that can not be set by a .json-based file
				 
			
		
		
			
				
					_CCRLonNutanix-CCStuff-Provider.tf
				 
			
			
				
					Provider definition and configuration
				 
			
		
		
			
				
					_CCRLonNutanix-CCStuff-Provider-variables.tf
				 
			
			
				
					Definition of Variables
				 
			
		
		
			
				
					_CCRLonNutanix-CCStuff-Provider.auto.tfvars.json
				 
			
			
				
					Setting the values of the Variables
				 
			
		
	



	 
 


	
		Caution:
	 

	
		All Terraform-related directories and files (.terraform, terraform.lock.hcl, terraform.tfvars, terraform.tfstate) must not be changed or deleted - doing so might break the deployment. 
		 
	 



	Change the settings in the .json or .tfvars files to match your needs.
 


	To ensure a smooth and error-free build, the following prerequisites must be met before setting the corresponding settings or running the Terraform workflow.
 


	 
 


	Software Components for Configuration and Deployment



	
		Important: 
		 
		The Cloud Connector Installer and the CVAD PowerShell SDK Installer must be manually uploaded to a location where Terraform can download them during the configuration run. 
		 
	 



	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 Installe
	
	
		Nutanix AHV PlugIn for Citrix: NutanixAHV_Citrix_Plugin.msi. Terraform downloads this file automatically.
	



	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 to which all necessary software is uploaded. 
	The URIs of the Storage Repository can be set in the corresponding variables:
 


	 
 


	
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}	
	
		
			
				
					
						
							...
						 

						
							"CC_Install_CWCURI":"https://XXXX.blob.core.windows.net/tfdata/cwcconnector.exe", 
							"CC_Install_RPoSHURI":"https://XXXX.blob.core.windows.net/tfdata/CitrixPoshSdk.exe",       
							"CC_Install_AHVPluginURI":"https://download.nutanix.com/citrixAhv/2.7.2.0/NutanixAHV_Citrix_plugin.msi
						 

						
							...
						 
					
				
			
		
	



	We also need to determine various information outside the Nutanix Cluster to set specific variables correctly—e.g., Network names, Master Image names, etc.
 


	The Nutanix Cmdlets are a PowerShell extension specifically for accessing a Nutanix environment. 
	You need to install Nutanix Cmdlets to proceed with the following steps.
 


	A detailed guide for installing Nutanix Cmdlets can be found at https://portal.nutanix.com/page/documents/details?targetId=PS-Cmdlets-AOS-v6_8:PS-Cmdlets-AOS-v6_8
 


	After successful installation, you should find a new icon on your Desktop: 
 


	The first step is to connect to the Nutanix Cluster:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\Windows\system32&gt; Connect-NutanixCluster -Server nutanix.the-austrian-citrix-guy.at -UserName XXXXXXXXXX -Password (Read-Host "Password: " -AsSecureString) -AcceptInvalidSSLCerts
					 

					
						Password:: *******************
					 

					
						 
					 

					
						Server                : nutanix.the-austrian-citrix-guy.at
					 

					
						UserName              : XXXXXXXXXX
					 

					
						Password              : System.Security.SecureString
					 

					
						AcceptInvalidSSLCerts : True
					 

					
						ForcedConnection      : False
					 

					
						ParameterSetName      : __AllParameterSets
					 

					
						MyInvocation          : System.Management.Automation.InvocationInfo
					 

					
						PagingParameters      :
					 

					
						InvokeCommand         : System.Management.Automation.CommandInvocationIntrinsics
					 

					
						Host                  : System.Management.Automation.Internal.Host.InternalHost
					 

					
						SessionState          : System.Management.Automation.SessionState
					 

					
						Events                : System.Management.Automation.PSLocalEventManager
					 

					
						JobRepository         : System.Management.Automation.JobRepository
					 

					
						JobManager            : System.Management.Automation.JobManager
					 

					
						InvokeProvider        : System.Management.Automation.ProviderIntrinsics
					 

					
						Stopping              : False
					 

					
						CommandRuntime        : Connect-NutanixCluster
					 

					
						CurrentPSTransaction  :
					 

					
						CommandOrigin         : Runspace
					 

					
						lastAccessTimestamp   : 02.07.2024 12:02:23
					 

					
						IsConnected           : True
					 

					
						 
					 

					
						PS C:\Windows\system32&gt;
					 
				
			
		
	



	 
	Now we need to get some Cluster Data – especially the Name:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\Windows\system32&gt; Get-NTNXCluster
					 

					
						 
					 

					
						id                                   : 00061bb2-8c9f-2210-41e4-48210b5c6d75::4747999213917072757
					 

					
						uuid                                 : 00061bb2-8c9f-2210-41e4-48210b5c6d75
					 

					
						clusterIncarnationId                 : 1719303537631760
					 

					
						clusterUuid                          : 00061bb2-8c9f-2210-41e4-48210b5c6d75
					 

					
						name                                 : TACG-NTX
					 

					
						clusterExternalIPAddress             :
					 

					
						isNSEnabled                          : False
					 

					
						clusterExternalDataServicesIPAddress :
					 

					
						segmentedIscsiDataServicesIPAddress  :
					 

					
						clusterMasqueradingIPAddress         :
					 

					
						clusterMasqueradingPort              :
					 

					
						timezone                             : UTC
					 

					
						supportVerbosityType                 : BASIC_COREDUMP
					 

					
						operationMode                        : Normal
					 

					
						encrypted                            : True
					 

					
						storageType                          : mixed
					 

					
						clusterFunctions                     : {NDFS}
					 

					
						isLTS                                : True
					 

					
						isRegisteredToPC                     :
					 

					
						numNodes                             : 1
					 

					
						blockSerials                         : {da6ea61d}
					 

					
						version                              : 6.5.2
					 

					
						fullVersion                          : el7.3-release-fraser-6.5.2-stable-f2ce4db7d67f495ebfd6208bef9ab0afec9c74af
					 

					
						targetVersion                        : 6.5.2
					 

					
						externalSubnet                       : 10.10.0.0/255.255.0.0
					 

					
						internalSubnet                       : 192.168.5.0/255.255.255.128
					 

					
						nccVersion                           : ncc-4.6.2.1
					 

					
						enableLockDown                       : False
					 

					
						enablePasswordRemoteLoginToCluster   : True
					 

					
						fingerprintContentCachePercentage    : 100
					 

					
						ssdPinningPercentageLimit            : 25
					 

					
						enableShadowClones                   : True
					 

					
						globalNfsWhiteList                   : {}
					 

					
						nameServers                          : {10.10.0.1, 10.10.100.1}
					 

					
						ntpServers                           : {1.pool.ntp.org, 0.pool.ntp.org}
					 

					
						hypervisorTypes                      : {kKvm}
					 

					
						multicluster                         : False
					 

					
						cloudcluster                         : False
					 

					
						hasSelfEncryptingDrive               : False
					 

					
						isUpgradeInProgress                  : False
					 

					
						clusterArch                          : X86_64
					 

					
						isSspEnabled                         : False
					 

					
						domain                               :
					 

					
						nosClusterAndHostsDomainJoined       : False
					 

					
						allHypervNodesInFailoverCluster      : False
					 

					
						enforceRackableUnitAwarePlacement    : False
					 

					
						disableDegradedNodeMonitoring        : False
					 

					
						commonCriteriaMode                   : False
					 

					
						enableOnDiskDedup                    : False
					 

					
						faultToleranceDomainType             : DISK
					 

					
						clusterGpus                          :
					 

					
						gpuDriverVersion                     :
					 

					
						 
					 

					
						PS C:\Windows\system32&gt;
					 
				
			
		
	



	 
	Now we need the Network Names:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	



	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\Windows\system32&gt; Get-NTNXNetwork
					 

					
						 
					 

					
						logicalTimestamp : 29
					 

					
						vlanId           : 0
					 

					
						uuid             : c78019a8-ad3b-4096-9f0c-e6aa1e5ce3a0
					 

					
						name             : NW-NTX-TACG
					 

					
						vswitchName      : br0
					 

					
						annotation       :
					 

					
						networkType      :
					 

					
						 
					 

					
						PS C:\Windows\system32&gt;
					 
				
			
		
	



	 
	You can deploy the Machine Catalog from a Template VM or a Snapshot. 
	We can determine both:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\Windows\system32&gt; Get-NTNXImage | FT Name, imagetype, uuid
					 

					
						name                imageType  uuid
					 

					
						----                ---------  ----
					 

					
						IMG-W2K22-NoSecBoot DISK_IMAGE 57d43449-fc0b-4afc-bf49-7c05cc8f92cf
					 

					
						W2K22               ISO_IMAGE  080549c9-352a-4661-97a8-fc6d5c161cea
					 

					
						CVAD2402            ISO_IMAGE  fbcb9af3-b656-4437-9b8a-82de0d08d864
					 

					
						W11                 ISO_IMAGE  0ddf73af-2b2f-478c-a76b-5283c3dff241
					 

					
						VirtIO              ISO_IMAGE  dcf8e574-40ff-4a0d-ab07-289af0e548d7
					 

					
						 
					 

					
						PS C:\Windows\system32&gt;
					 
				
			
		
	



	In this case, the Image name we need is „IMG-W2K22-NoSecBoot“.
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\Windows\system32&gt; Get-NTNXVM | FT vmname,uuid
					 

					
						 
					 

					
						vmName              uuid
					 

					
						------              ----
					 

					
						TACG-NTX-CC1        084d27f2-c425-4713-aa50-89601da860d9
					 

					
						NTNX-da6ea61d-A-CVM ec66b4b8-9aa8-422d-a6cc-1178d75c7708
					 

					
						TACG-NTX-CC2        52864356-8179-451b-8561-7c1d0a2b9d4b
					 

					
						TACG-NTX-AVM        958ed855-7e1b-4c3a-910e-c6be16184b10
					 

					
						TACG-NTX-WK22-MI    a14204c5-3717-4636-a802-d4b404f1ecad
					 

					
						IMG-W2K22-NoSecBoot d61461f2-fe11-4732-b27e-087e49dfbe78
					 

					
						 
					 

					
						PS C:\Windows\system32&gt; Get-NTNXSnapshot | FT SnapshotName, vmuuid
					 

					
						 
					 

					
						snapshotName           vmUuid
					 

					
						------------           ------
					 

					
						after config            958ed855-7e1b-4c3a-910e-c6be16184b10
					 

					
						mi                     a14204c5-3717-4636-a802-d4b404f1ecad
					 

					
						after config plugin    084d27f2-c425-4713-aa50-89601da860d9
					 

					
						after config plugin    52864356-8179-451b-8561-7c1d0a2b9d4b 
						 
					 

					
						PS C:\Windows\system32&gt;
					 
				
			
		
	



	In this case, the Snapshot name we need is „mi“.
 


	The next entity value we need is the Storage Container name:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\Windows\system32&gt; Get-NTNXContainer | FT id,vStoreNameList 
						 
					 

					
						id                                        vstoreNameList
					 

					
						--                                        --------------
					 

					
						00061bb2-8c9f-2210-41e4-48210b5c6d75::4   {default-container-36052048168163}
					 

					
						00061bb2-8c9f-2210-41e4-48210b5c6d75::5   {NutanixManagementShare}
					 

					
						00061bb2-8c9f-2210-41e4-48210b5c6d75::776 {SelfServiceContainer}
					 

					
						 
					 

					
						PS C:\Windows\system32&gt;
					 
				
			
		
	



	We now have determined all the values needed and can start the deployment.
 


	
		Important: 
	 

	
		Please do not forget to put all previously determined entity values in the corresponding .auto.tfvars.json variable files. 
		 
	 



	 
 


	Module 1: Create the initially needed Resources on Nutanix AHV



	This module is split into the following configuration parts:
 


	
		Creating the initially needed Resources on Nutanix AHV:

		
			
				Creating an AutoUnattend.xml setup file. So Nutanix can put the VMs into the domain
			
			
				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 Modules 2, 3, and 4 is necessary because we will use WinRM for further configuration and deployment in Modules 2, 3, and 4.
			
			
				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
			
		
	



	Terraform automatically does all these steps.
 


	
		Important:
	 

	
		This first part is separated from the following steps as we assume no Admin-VM is installed in the corresponding environment. 
		The Admin-VM is created now in Module 1, and all subsequent modules must be run from it. 
		Working with WinRM calls over the Internet would pose a security threat and significantly reduce the performance of the Terraform-based deployment.  
		These are the reasons for the split approach. 
		 
	 



	 
 


	
		Important:
	 

	
		Before starting the Terraform workflow, please ensure 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 td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\_TACG\_CCRLOnNutanix-Create&gt; terraform init 
						Initializing the backend... 
						Initializing provider plugins... 
						- Finding citrix/citrix versions matching "0.6.1"... 
						- Finding latest version of citrix/citrixadc... 
						- Finding latest version of hashicorp/template... 
						- Finding nutanix/nutanix versions matching "1.9.5"... 
						- Finding mastercard/restapi versions matching "1.18.2"... 
						- Installing mastercard/restapi v1.18.2... 
						- Installed mastercard/restapi v1.18.2 (self-signed, key ID DCB8C431D71C30AB) 
						- Installing citrix/citrix v0.6.1... 
						- Installed citrix/citrix v0.6.1 (self-signed, key ID BD4BD0E690CB7D88) 
						- Installing citrix/citrixadc v1.39.0... 
						- Installed citrix/citrixadc v1.39.0 (signed by a HashiCorp partner, key ID 25D62DD8407EA386) 
						- Installing hashicorp/template v2.2.0... 
						- Installed hashicorp/template v2.2.0 (signed by HashiCorp) 
						- Installing nutanix/nutanix v1.9.5... 
						- Installed nutanix/nutanix v1.9.5 (signed by a HashiCorp partner, key ID BEA5F795571AD06E) 
						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\_CCRLOnNutanix-Create&gt; terraform validate 
						Success! The configuration is valid. 
						 
						PS C:\_TACG\_CCRLOnNutanix-Create&gt; terraform plan 
						data.template_file.AutoUnattendXMLAVM: Reading... 
						data.template_file.AutoUnattendXMLCC1: Reading... 
						data.template_file.AutoUnattendXMLCC2: Reading... 
						data.template_file.AutoUnattendXMLCC1: Read complete after 0s [id=25874adf0f57c5ea73492890e2ef426d9bce8704b6a7528d1a22e82677572f79] 
						data.template_file.AutoUnattendXMLAVM: Read complete after 0s [id=6e382d6b4dd4d570081a0529fee241efb9fddebee9362fb8caccb78b76486682] 
						data.template_file.AutoUnattendXMLCC2: Read complete after 0s [id=58938910dbd0eea5ab148aa2ed05d885febedd315231c2aeb39e5409a134d430] 
						data.nutanix_subnets.NTX-Subnets: Reading... 
						data.nutanix_clusters.NTX-Clusters: Reading... 
						data.nutanix_clusters.NTX-Clusters: Read complete after 0s [id=terraform-20240709104628435200000001] 
						data.nutanix_cluster.NTX-Cluster: Reading... 
						data.nutanix_subnets.NTX-Subnets: Read complete after 1s [id=terraform-20240709104628535100000002] 
						data.nutanix_cluster.NTX-Cluster: Read complete after 1s [id=00061bb2-8c9f-2210-41e4-48210b5c6d75] 
						 
						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: 
						 
						  # nutanix_virtual_machine.AVM-VM will be created 
						  + resource "nutanix_virtual_machine" "AVM-VM" { 
						      + api_version                                      = (known after apply) 
						      + availability_zone_reference                      = (known after apply) 
						      + boot_device_disk_address                         = (known after apply) 
						      + boot_device_mac_address                          = (known after apply) 
						      + boot_device_order_list                           = [ 
						          + "DISK", 
						        ] 
						      + boot_type                                        = "LEGACY" 
						      + cloud_init_cdrom_uuid                            = (known after apply) 
						      + cluster_name                                     = (known after apply) 
						      + cluster_uuid                                     = "00061bb2-8c9f-2210-41e4-48210b5c6d75" 
						      + description                                      = "TACG-NTX Admin VM" 
						      + enable_cpu_passthrough                           = false 
						      + enable_script_exec                               = (known after apply) 
						      + guest_customization_cloud_init_custom_key_values = (known after apply) 
						      + guest_customization_cloud_init_meta_data         = (known after apply) 
						      + guest_customization_cloud_init_user_data         = (known after apply) 
						      + guest_customization_is_overridable               = (known after apply) 
						      + guest_customization_sysprep                      = { 
						          + "install_type" = "PREPARED" 
						          + "unattend_xml" = "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHVuYXR0ZW5kIHhtbG5zPSJ1cm46c2NoZW1hcy1taWNyb3NvZnQtY29tOnVuYXR0ZW5kIj4KICAgIDxzZXR0aW5ncyBwYXNzPSJvb2JlU3lzdGVtIj4KICAgICAgICA8Y29tcG9uZW50IG5hbWU9Ik1pY3Jvc29mdC1XaW5kb3dzLVNoZWxsLVNldHVwIiBwcm9jZXNzb3JBcmNoaXRlY3R1cmU9ImFtZDY0IiBwdWJsaWNLZXlUb2tlbj0iMzFiZjM4NTZhZDM2NGUzNSIgbGFuZ3VhZ2U9Im5ldXRyYWwiIHZlcnNpb25TY29wZT0ibm9uU3hTIiB4bWxuczp3Y209Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vV01JQ29uZmlnLzIwMDIvU3RhdGUiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiPgogICAgICAgICAgICA8T09CRT4KICAgICAgICAgICAgICAgIDxIaWRlRVVMQVBhZ2U+dHJ1ZTwvSGlkZUVVTEFQYWdlPgogICAgICAgICAgICAgICAgPFNraXBVc2VyT09CRT50cnVlPC9Ta2lwVXNlck9PQkU+CiAgICAgICAgICAgICAgICA8SGlkZU9FTVJlZ2lzdHJhdGlvblNjcmVlbj50cnVlPC9IaWRlT0VNUmVnaXN0cmF0aW9uU2NyZWVuPgogICAgICAgICAgICAgICAgPEhpZGVPbmxpbmVBY2NvdW50U2NyZWVucz50cnVlPC9IaWRlT25saW5lQWNjb3VudFNjcmVlbnM+CiAgICAgICAgICAgICAgICA8SGlkZVdpcmVsZXNzU2V0dXBJbk9PQkU+dHJ1ZTwvSGlkZVdpcmVsZXNzU2V0dXBJbk9PQkU+CiAgICAgICAgICAgICAgICA8TmV0d29ya0xvY2F0aW9uPldvcms8L05ldHdvcmtMb2NhdGlvbj4KICAgICAgICAgICAgICAgIDxQcm90ZWN0WW91clBDPjE8L1Byb3RlY3RZb3VyUEM+CiAgICAgICAgICAgICAgICA8SGlkZUxvY2FsQWNjb3VudFNjcmVlbj50cnVlPC9IaWRlTG9jYWxBY2NvdW50U2NyZWVuPgogICAgICAgICAgICA8L09PQkU+CiAgICAgICAgICAgIDxVc2VyQWNjb3VudHM+CiAgICAgICAgICAgICAgICA8QWRtaW5pc3RyYXRvclBhc3N3b3JkPgogICAgICAgICAgICAgICAgICAgIDxWYWx1ZT5JUUJFQUdrQWJRQnVBRUVBVUFCWEFDRUFRUUJpQURJQU1BQXlBRFFBSVFBeEFDRUFJUUJCQUdRQWJRQnBBRzRBYVFCekFIUUFjZ0JoQUhRQWJ3QnlBRkFBWVFCekFITUFkd0J2QUhJQVpBQT08L1ZhbHVlPgogICAgICAgICAgICAgICAgICAgIDxQbGFpblRleHQ+ZmFsc2U8L1BsYWluVGV4dD4KICAgICAgICAgICAgICAgIDwvQWRtaW5pc3RyYXRvclBhc3N3b3JkPgogICAgICAgICAgICA8L1VzZXJBY2NvdW50cz4KICAgICAgICAgICAgPEF1dG9Mb2dvbj4KICAgICAgICAgICAgICAgIDxQYXNzd29yZD4KICAgICAgICAgICAgICAgICAgICA8VmFsdWU+SVFCRUFHa0FiUUJ1QUVFQVVBQlhBQ0VBUVFCaUFESUFNQUF5QURRQUlRQXhBQ0VBSVFCUUFHRUFjd0J6QUhjQWJ3QnlBR1FBPC9WYWx1ZT4KICAgICAgICAgICAgICAgICAgICA8UGxhaW5UZXh0PmZhbHNlPC9QbGFpblRleHQ+CiAgICAgICAgICAgICAgICA8L1Bhc3N3b3JkPgogICAgICAgICAgICAgICAgPEVuYWJsZWQ+dHJ1ZTwvRW5hYmxlZD4KICAgICAgICAgICAgICAgIDxMb2dvbkNvdW50PjE8L0xvZ29uQ291bnQ+CiAgICAgICAgICAgICAgICA8VXNlcm5hbWU+QWRtaW5pc3RyYXRvcjwvVXNlcm5hbWU+CiAgICAgICAgICAgIDwvQXV0b0xvZ29uPgogICAgICAgICAgICA8Rmlyc3RMb2dvbkNvbW1hbmRzPgogICAgICAgICAgICAgICAgPFN5bmNocm9ub3VzQ29tbWFuZCB3Y206YWN0aW9uPSJhZGQiPgogICAgICAgICAgICAgICAgICAgIDxDb21tYW5kTGluZT5wb3dlcnNoZWxsLmV4ZSAtTm9Qcm9maWxlIC1FeGVjdXRpb25Qb2xpY3kgQnlwYXNzIC1Db21tYW5kICJJbnZva2UtRXhwcmVzc2lvbiAoSW52b2tlLVdlYlJlcXVlc3QgLVVzZUJhc2ljUGFyc2luZyAtVXJpICdodHRwczovL3dtd2Jsb2IuYmxvYi5jb3JlLndpbmRvd3MubmV0L3RmZGF0YS9FbmFibGVXaW5STUZpcnN0UnVuLnBzMScpIjwvQ29tbWFuZExpbmU+CiAgICAgICAgICAgICAgICAgICAgPERlc2NyaXB0aW9uPnBvd2Vyc2hlbGxfcnVub25jZTwvRGVzY3JpcHRpb24+CiAgICAgICAgICAgICAgICAgICAgPE9yZGVyPjE8L09yZGVyPgogICAgICAgICAgICAgICAgPC9TeW5jaHJvbm91c0NvbW1hbmQ+CiAgICAgICAgICAgIDwvRmlyc3RMb2dvbkNvbW1hbmRzPgogICAgICAgIDwvY29tcG9uZW50PgogICAgICAgIDxjb21wb25lbnQgbmFtZT0iTWljcm9zb2Z0LVdpbmRvd3MtSW50ZXJuYXRpb25hbC1Db3JlIiBwcm9jZXNzb3JBcmNoaXRlY3R1cmU9ImFtZDY0IiBwdWJsaWNLZXlUb2tlbj0iMzFiZjM4NTZhZDM2NGUzNSIgbGFuZ3VhZ2U9Im5ldXRyYWwiIHZlcnNpb25TY29wZT0ibm9uU3hTIiB4bWxuczp3Y209Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vV01JQ29uZmlnLzIwMDIvU3RhdGUiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiPgogICAgICAgICAgICA8SW5wdXRMb2NhbGU+ZGUtQVQ8L0lucHV0TG9jYWxlPgogICAgICAgICAgICA8U3lzdGVtTG9jYWxlPmRlLUFUPC9TeXN0ZW1Mb2NhbGU+CiAgICAgICAgICAgIDxVSUxhbmd1YWdlPmVuLVVTPC9VSUxhbmd1YWdlPgogICAgICAgICAgICA8VXNlckxvY2FsZT5kZS1BVDwvVXNlckxvY2FsZT4KICAgICAgICAgICAgPFVJTGFuZ3VhZ2VGYWxsYmFjaz5kZS1ERTwvVUlMYW5ndWFnZUZhbGxiYWNrPgogICAgICAgIDwvY29tcG9uZW50PgogICAgPC9zZXR0aW5ncz4KICAgIDxzZXR0aW5ncyBwYXNzPSJzcGVjaWFsaXplIj4KICAgICAgICA8Y29tcG9uZW50IG5hbWU9Ik1pY3Jvc29mdC1XaW5kb3dzLVNoZWxsLVNldHVwIiBwcm9jZXNzb3JBcmNoaXRlY3R1cmU9ImFtZDY0IiBwdWJsaWNLZXlUb2tlbj0iMzFiZjM4NTZhZDM2NGUzNSIgbGFuZ3VhZ2U9Im5ldXRyYWwiIHZlcnNpb25TY29wZT0ibm9uU3hTIiB4bWxuczp3Y209Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vV01JQ29uZmlnLzIwMDIvU3RhdGUiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiPgogICAgICAgICAgICA8UmVnaXN0ZXJlZE9yZ2FuaXphdGlvbj5UQUNHIE51dGFuaXg8L1JlZ2lzdGVyZWRPcmdhbml6YXRpb24+CiAgICAgICAgICAgIDxSZWdpc3RlcmVkT3duZXI+VEFDRyBOdXRhbml4PC9SZWdpc3RlcmVkT3duZXI+CiAgICAgICAgICAgIDxUaW1lWm9uZT5HTVQgU3RhbmRhcmQgVGltZTwvVGltZVpvbmU+CiAgICAgICAgICAgIDxQcm9kdWN0S2V5Pk5SVjRRLVQ3WTczLUhNSjZWLTI4SlA4LTI5MlhZPC9Qcm9kdWN0S2V5PgogICAgICAgICAgICA8Q29tcHV0ZXJOYW1lPlRBQ0ctTlRYLUFWTTwvQ29tcHV0ZXJOYW1lPgogICAgICAgIDwvY29tcG9uZW50PgogICAgICAgIDxjb21wb25lbnQgbmFtZT0iTWljcm9zb2Z0LVdpbmRvd3MtVW5hdHRlbmRlZEpvaW4iIHByb2Nlc3NvckFyY2hpdGVjdHVyZT0iYW1kNjQiIHB1YmxpY0tleVRva2VuPSIzMWJmMzg1NmFkMzY0ZTM1IiBsYW5ndWFnZT0ibmV1dHJhbCIgdmVyc2lvblNjb3BlPSJub25TeFMiIHhtbG5zOndjbT0iaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS9XTUlDb25maWcvMjAwMi9TdGF0ZSIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSI+CiAgICAgICAgICAgIDxJZGVudGlmaWNhdGlvbj4KICAgICAgICAgICAgICAgIDxDcmVkZW50aWFscz4KICAgICAgICAgICAgICAgICAgICA8RG9tYWluPlRBQ0c8L0RvbWFpbj4KICAgICAgICAgICAgICAgICAgICA8VXNlcm5hbWU+YWRtaW48L1VzZXJuYW1lPgogICAgICAgICAgICAgICAgICAgIDxQYXNzd29yZD4hRGltbkFQVyFBYjIwMjQhMSEhPC9QYXNzd29yZD4KICAgICAgICAgICAgICAgIDwvQ3JlZGVudGlhbHM+CiAgICAgICAgICAgICAgICA8Sm9pbkRvbWFpbj50aGUtYXVzdHJpYW4tY2l0cml4LWd1eS5hdDwvSm9pbkRvbWFpbj4KICAgICAgICAgICAgICAgIDxNYWNoaW5lT2JqZWN0T1U+T1U9Q29tcHV0ZXJzLGRjPXRoZSxkYz1hdXN0cmlhbixkYz1jaXRyaXgsZGM9Z3V5LGRjPWF0PC9NYWNoaW5lT2JqZWN0T1U+CiAgICAgICAgICAgICAgICA8VW5zZWN1cmVKb2luIC8+CiAgICAgICAgICAgICAgICA8VGltZW91dFBlcmlvZEluTWludXRlcyAvPgogICAgICAgICAgICAgICAgPERlYnVnSm9pbk9ubHlPblRoaXNFcnJvciAvPgogICAgICAgICAgICAgICAgPERlYnVnSm9pbiAvPgogICAgICAgICAgICA8L0lkZW50aWZpY2F0aW9uPgogICAgICAgIDwvY29tcG9uZW50PgogICAgPC9zZXR0aW5ncz4KPC91bmF0dGVuZD4K" 
						        } 
						      + guest_customization_sysprep_custom_key_values    = (known after apply) 
						      + guest_os_id                                      = (known after apply) 
						      + hardware_clock_timezone                          = (known after apply) 
						      + host_reference                                   = (known after apply) 
						      + hypervisor_type                                  = (known after apply) 
						      + id                                               = (known after apply) 
						      + is_vcpu_hard_pinned                              = false 
						      + machine_type                                     = (known after apply) 
						      + memory_size_mib                                  = 8192 
						      + metadata                                         = (known after apply) 
						      + name                                             = "TACG-NTX-AVM" 
						      + ngt_credentials                                  = (known after apply) 
						      + ngt_enabled_capability_list                      = (known after apply) 
						      + nic_list_status                                  = (known after apply) 
						      + num_sockets                                      = 1 
						      + num_vcpus_per_socket                             = 2 
						      + num_vnuma_nodes                                  = (known after apply) 
						      + nutanix_guest_tools                              = (known after apply) 
						      + owner_reference                                  = (known after apply) 
						      + parent_reference                                 = (known after apply) 
						      + power_state                                      = (known after apply) 
						      + power_state_mechanism                            = (known after apply) 
						      + project_reference                                = (known after apply) 
						      + should_fail_on_script_failure                    = (known after apply) 
						      + state                                            = (known after apply) 
						      + use_hot_add                                      = true 
						      + vga_console_enabled                              = (known after apply) 
						 
						      + categories (known after apply) 
						 
						      + disk_list { 
						          + data_source_reference  = { 
						              + "kind" = "image" 
						              + "uuid" = "57d43449-fc0b-4afc-bf49-7c05cc8f92cf" 
						            } 
						          + disk_size_bytes        = 0 
						          + disk_size_mib          = 0 
						          + uuid                   = (known after apply) 
						          + volume_group_reference = (known after apply) 
						        } 
						 
						      + gpu_list (known after apply) 
						 
						      + nic_list { 
						          + is_connected                     = "true" 
						          + mac_address                      = (known after apply) 
						          + model                            = (known after apply) 
						          + network_function_chain_reference = (known after apply) 
						          + network_function_nic_type        = (known after apply) 
						          + nic_type                         = (known after apply) 
						          + num_queues                       = (known after apply) 
						          + subnet_name                      = (known after apply) 
						          + subnet_uuid                      = "5584d26a-f126-493e-84c5-9254c3d4378e" 
						          + uuid                             = (known after apply) 
						 
						          + ip_endpoint_list { 
						              + ip   = "10.10.174.3" 
						              + type = "ASSIGNED" 
						            } 
						        } 
						    } 
						 
						  # nutanix_virtual_machine.CC1-VM will be created 
						  + resource "nutanix_virtual_machine" "CC1-VM" { 
						      + api_version                                      = (known after apply) 
						      + availability_zone_reference                      = (known after apply) 
						      + boot_device_disk_address                         = (known after apply) 
						      + boot_device_mac_address                          = (known after apply) 
						      + boot_device_order_list                           = [ 
						          + "DISK", 
						        ] 
						      + boot_type                                        = "LEGACY" 
						      + cloud_init_cdrom_uuid                            = (known after apply) 
						      + cluster_name                                     = (known after apply) 
						      + cluster_uuid                                     = "00061bb2-8c9f-2210-41e4-48210b5c6d75" 
						      + description                                      = "TACG-NTX-Cloud Connector 1" 
						      + enable_cpu_passthrough                           = false 
						      + enable_script_exec                               = (known after apply) 
						      + guest_customization_cloud_init_custom_key_values = (known after apply) 
						      + guest_customization_cloud_init_meta_data         = (known after apply) 
						      + guest_customization_cloud_init_user_data         = (known after apply) 
						      + guest_customization_is_overridable               = (known after apply) 
						      + guest_customization_sysprep                      = { 
						          + "install_type" = "PREPARED" 
						          + "unattend_xml" = "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHVuYXR0ZW5kIHhtbG5zPSJ1cm46c2NoZW1hcy1taWNyb3NvZnQtY29tOnVuYXR0ZW5kIj4KICAgIDxzZXR0aW5ncyBwYXNzPSJvb2JlU3lzdGVtIj4KICAgICAgICA8Y29tcG9uZW50IG5hbWU9Ik1pY3Jvc29mdC1XaW5kb3dzLVNoZWxsLVNldHVwIiBwcm9jZXNzb3JBcmNoaXRlY3R1cmU9ImFtZDY0IiBwdWJsaWNLZXlUb2tlbj0iMzFiZjM4NTZhZDM2NGUzNSIgbGFuZ3VhZ2U9Im5ldXRyYWwiIHZlcnNpb25TY29wZT0ibm9uU3hTIiB4bWxuczp3Y209Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vV01JQ29uZmlnLzIwMDIvU3RhdGUiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiPgogICAgICAgICAgICA8T09CRT4KICAgICAgICAgICAgICAgIDxIaWRlRVVMQVBhZ2U+dHJ1ZTwvSGlkZUVVTEFQYWdlPgogICAgICAgICAgICAgICAgPFNraXBVc2VyT09CRT50cnVlPC9Ta2lwVXNlck9PQkU+CiAgICAgICAgICAgICAgICA8SGlkZU9FTVJlZ2lzdHJhdGlvblNjcmVlbj50cnVlPC9IaWRlT0VNUmVnaXN0cmF0aW9uU2NyZWVuPgogICAgICAgICAgICAgICAgPEhpZGVPbmxpbmVBY2NvdW50U2NyZWVucz50cnVlPC9IaWRlT25saW5lQWNjb3VudFNjcmVlbnM+CiAgICAgICAgICAgICAgICA8SGlkZVdpcmVsZXNzU2V0dXBJbk9PQkU+dHJ1ZTwvSGlkZVdpcmVsZXNzU2V0dXBJbk9PQkU+CiAgICAgICAgICAgICAgICA8TmV0d29ya0xvY2F0aW9uPldvcms8L05ldHdvcmtMb2NhdGlvbj4KICAgICAgICAgICAgICAgIDxQcm90ZWN0WW91clBDPjE8L1Byb3RlY3RZb3VyUEM+CiAgICAgICAgICAgICAgICA8SGlkZUxvY2FsQWNjb3VudFNjcmVlbj50cnVlPC9IaWRlTG9jYWxBY2NvdW50U2NyZWVuPgogICAgICAgICAgICA8L09PQkU+CiAgICAgICAgICAgIDxVc2VyQWNjb3VudHM+CiAgICAgICAgICAgICAgICA8QWRtaW5pc3RyYXRvclBhc3N3b3JkPgogICAgICAgICAgICAgICAgICAgIDxWYWx1ZT5JUUJFQUdrQWJRQnVBRUVBVUFCWEFDRUFRUUJpQURJQU1BQXlBRFFBSVFBeEFDRUFJUUJCQUdRQWJRQnBBRzRBYVFCekFIUUFjZ0JoQUhRQWJ3QnlBRkFBWVFCekFITUFkd0J2QUhJQVpBQT08L1ZhbHVlPgogICAgICAgICAgICAgICAgICAgIDxQbGFpblRleHQ+ZmFsc2U8L1BsYWluVGV4dD4KICAgICAgICAgICAgICAgIDwvQWRtaW5pc3RyYXRvclBhc3N3b3JkPgogICAgICAgICAgICA8L1VzZXJBY2NvdW50cz4KICAgICAgICAgICAgPEF1dG9Mb2dvbj4KICAgICAgICAgICAgICAgIDxQYXNzd29yZD4KICAgICAgICAgICAgICAgICAgICA8VmFsdWU+SVFCRUFHa0FiUUJ1QUVFQVVBQlhBQ0VBUVFCaUFESUFNQUF5QURRQUlRQXhBQ0VBSVFCUUFHRUFjd0J6QUhjQWJ3QnlBR1FBPC9WYWx1ZT4KICAgICAgICAgICAgICAgICAgICA8UGxhaW5UZXh0PmZhbHNlPC9QbGFpblRleHQ+CiAgICAgICAgICAgICAgICA8L1Bhc3N3b3JkPgogICAgICAgICAgICAgICAgPEVuYWJsZWQ+dHJ1ZTwvRW5hYmxlZD4KICAgICAgICAgICAgICAgIDxMb2dvbkNvdW50PjE8L0xvZ29uQ291bnQ+CiAgICAgICAgICAgICAgICA8VXNlcm5hbWU+QWRtaW5pc3RyYXRvcjwvVXNlcm5hbWU+CiAgICAgICAgICAgIDwvQXV0b0xvZ29uPgogICAgICAgICAgICA8Rmlyc3RMb2dvbkNvbW1hbmRzPgogICAgICAgICAgICAgICAgPFN5bmNocm9ub3VzQ29tbWFuZCB3Y206YWN0aW9uPSJhZGQiPgogICAgICAgICAgICAgICAgICAgIDxDb21tYW5kTGluZT5wb3dlcnNoZWxsLmV4ZSAtTm9Qcm9maWxlIC1FeGVjdXRpb25Qb2xpY3kgQnlwYXNzIC1Db21tYW5kICJJbnZva2UtRXhwcmVzc2lvbiAoSW52b2tlLVdlYlJlcXVlc3QgLVVzZUJhc2ljUGFyc2luZyAtVXJpICdodHRwczovL3dtd2Jsb2IuYmxvYi5jb3JlLndpbmRvd3MubmV0L3RmZGF0YS9FbmFibGVXaW5STUZpcnN0UnVuLnBzMScpIjwvQ29tbWFuZExpbmU+CiAgICAgICAgICAgICAgICAgICAgPERlc2NyaXB0aW9uPnBvd2Vyc2hlbGxfcnVub25jZTwvRGVzY3JpcHRpb24+CiAgICAgICAgICAgICAgICAgICAgPE9yZGVyPjE8L09yZGVyPgogICAgICAgICAgICAgICAgPC9TeW5jaHJvbm91c0NvbW1hbmQ+CiAgICAgICAgICAgIDwvRmlyc3RMb2dvbkNvbW1hbmRzPgogICAgICAgIDwvY29tcG9uZW50PgogICAgICAgIDxjb21wb25lbnQgbmFtZT0iTWljcm9zb2Z0LVdpbmRvd3MtSW50ZXJuYXRpb25hbC1Db3JlIiBwcm9jZXNzb3JBcmNoaXRlY3R1cmU9ImFtZDY0IiBwdWJsaWNLZXlUb2tlbj0iMzFiZjM4NTZhZDM2NGUzNSIgbGFuZ3VhZ2U9Im5ldXRyYWwiIHZlcnNpb25TY29wZT0ibm9uU3hTIiB4bWxuczp3Y209Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vV01JQ29uZmlnLzIwMDIvU3RhdGUiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiPgogICAgICAgICAgICA8SW5wdXRMb2NhbGU+ZGUtQVQ8L0lucHV0TG9jYWxlPgogICAgICAgICAgICA8U3lzdGVtTG9jYWxlPmRlLUFUPC9TeXN0ZW1Mb2NhbGU+CiAgICAgICAgICAgIDxVSUxhbmd1YWdlPmVuLVVTPC9VSUxhbmd1YWdlPgogICAgICAgICAgICA8VXNlckxvY2FsZT5kZS1BVDwvVXNlckxvY2FsZT4KICAgICAgICAgICAgPFVJTGFuZ3VhZ2VGYWxsYmFjaz5kZS1ERTwvVUlMYW5ndWFnZUZhbGxiYWNrPgogICAgICAgIDwvY29tcG9uZW50PgogICAgPC9zZXR0aW5ncz4KICAgIDxzZXR0aW5ncyBwYXNzPSJzcGVjaWFsaXplIj4KICAgICAgICA8Y29tcG9uZW50IG5hbWU9Ik1pY3Jvc29mdC1XaW5kb3dzLVNoZWxsLVNldHVwIiBwcm9jZXNzb3JBcmNoaXRlY3R1cmU9ImFtZDY0IiBwdWJsaWNLZXlUb2tlbj0iMzFiZjM4NTZhZDM2NGUzNSIgbGFuZ3VhZ2U9Im5ldXRyYWwiIHZlcnNpb25TY29wZT0ibm9uU3hTIiB4bWxuczp3Y209Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vV01JQ29uZmlnLzIwMDIvU3RhdGUiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiPgogICAgICAgICAgICA8UmVnaXN0ZXJlZE9yZ2FuaXphdGlvbj5UQUNHIE51dGFuaXg8L1JlZ2lzdGVyZWRPcmdhbml6YXRpb24+CiAgICAgICAgICAgIDxSZWdpc3RlcmVkT3duZXI+VEFDRyBOdXRhbml4PC9SZWdpc3RlcmVkT3duZXI+CiAgICAgICAgICAgIDxUaW1lWm9uZT5HTVQgU3RhbmRhcmQgVGltZTwvVGltZVpvbmU+CiAgICAgICAgICAgIDxQcm9kdWN0S2V5Pk5SVjRRLVQ3WTczLUhNSjZWLTI4SlA4LTI5MlhZPC9Qcm9kdWN0S2V5PgogICAgICAgICAgICA8Q29tcHV0ZXJOYW1lPlRBQ0ctTlRYLUNDMTwvQ29tcHV0ZXJOYW1lPgogICAgICAgIDwvY29tcG9uZW50PgogICAgICAgIDxjb21wb25lbnQgbmFtZT0iTWljcm9zb2Z0LVdpbmRvd3MtVW5hdHRlbmRlZEpvaW4iIHByb2Nlc3NvckFyY2hpdGVjdHVyZT0iYW1kNjQiIHB1YmxpY0tleVRva2VuPSIzMWJmMzg1NmFkMzY0ZTM1IiBsYW5ndWFnZT0ibmV1dHJhbCIgdmVyc2lvblNjb3BlPSJub25TeFMiIHhtbG5zOndjbT0iaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS9XTUlDb25maWcvMjAwMi9TdGF0ZSIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSI+CiAgICAgICAgICAgIDxJZGVudGlmaWNhdGlvbj4KICAgICAgICAgICAgICAgIDxDcmVkZW50aWFscz4KICAgICAgICAgICAgICAgICAgICA8RG9tYWluPlRBQ0c8L0RvbWFpbj4KICAgICAgICAgICAgICAgICAgICA8VXNlcm5hbWU+YWRtaW48L1VzZXJuYW1lPgogICAgICAgICAgICAgICAgICAgIDxQYXNzd29yZD4hRGltbkFQVyFBYjIwMjQhMSEhPC9QYXNzd29yZD4KICAgICAgICAgICAgICAgIDwvQ3JlZGVudGlhbHM+CiAgICAgICAgICAgICAgICA8Sm9pbkRvbWFpbj50aGUtYXVzdHJpYW4tY2l0cml4LWd1eS5hdDwvSm9pbkRvbWFpbj4KICAgICAgICAgICAgICAgIDxNYWNoaW5lT2JqZWN0T1U+T1U9Q29tcHV0ZXJzLGRjPXRoZSxkYz1hdXN0cmlhbixkYz1jaXRyaXgsZGM9Z3V5LGRjPWF0PC9NYWNoaW5lT2JqZWN0T1U+CiAgICAgICAgICAgICAgICA8VW5zZWN1cmVKb2luIC8+CiAgICAgICAgICAgICAgICA8VGltZW91dFBlcmlvZEluTWludXRlcyAvPgogICAgICAgICAgICAgICAgPERlYnVnSm9pbk9ubHlPblRoaXNFcnJvciAvPgogICAgICAgICAgICAgICAgPERlYnVnSm9pbiAvPgogICAgICAgICAgICA8L0lkZW50aWZpY2F0aW9uPgogICAgICAgIDwvY29tcG9uZW50PgogICAgPC9zZXR0aW5ncz4KPC91bmF0dGVuZD4K" 
						        } 
						      + guest_customization_sysprep_custom_key_values    = (known after apply) 
						      + guest_os_id                                      = (known after apply) 
						      + hardware_clock_timezone                          = (known after apply) 
						      + host_reference                                   = (known after apply) 
						      + hypervisor_type                                  = (known after apply) 
						      + id                                               = (known after apply) 
						      + is_vcpu_hard_pinned                              = false 
						      + machine_type                                     = (known after apply) 
						      + memory_size_mib                                  = 192 
						      + metadata                                         = (known after apply) 
						      + name                                             = "TACG-NTX-CC1" 
						      + ngt_credentials                                  = (known after apply) 
						      + ngt_enabled_capability_list                      = (known after apply) 
						      + nic_list_status                                  = (known after apply) 
						      + num_sockets                                      = 1 
						      + num_vcpus_per_socket                             = 2 
						      + num_vnuma_nodes                                  = (known after apply) 
						      + nutanix_guest_tools                              = (known after apply) 
						      + owner_reference                                  = (known after apply) 
						      + parent_reference                                 = (known after apply) 
						      + power_state                                      = (known after apply) 
						      + power_state_mechanism                            = (known after apply) 
						      + project_reference                                = (known after apply) 
						      + should_fail_on_script_failure                    = (known after apply) 
						      + state                                            = (known after apply) 
						      + use_hot_add                                      = true 
						      + vga_console_enabled                              = (known after apply) 
						 
						      + categories (known after apply) 
						 
						      + disk_list { 
						          + data_source_reference  = { 
						              + "kind" = "image" 
						              + "uuid" = "57d43449-fc0b-4afc-bf49-7c05cc8f92cf" 
						            } 
						          + disk_size_bytes        = 0 
						          + disk_size_mib          = 0 
						          + uuid                   = (known after apply) 
						          + volume_group_reference = (known after apply) 
						        } 
						 
						      + gpu_list (known after apply) 
						 
						      + nic_list { 
						          + is_connected                     = "true" 
						          + mac_address                      = (known after apply) 
						          + model                            = (known after apply) 
						          + network_function_chain_reference = (known after apply) 
						          + network_function_nic_type        = (known after apply) 
						          + nic_type                         = (known after apply) 
						          + num_queues                       = (known after apply) 
						          + subnet_name                      = (known after apply) 
						          + subnet_uuid                      = "5584d26a-f126-493e-84c5-9254c3d4378e" 
						          + uuid                             = (known after apply) 
						 
						          + ip_endpoint_list { 
						              + ip   = "10.10.174.1" 
						              + type = "ASSIGNED" 
						            } 
						        } 
						    } 
						 
						  # nutanix_virtual_machine.CC2-VM will be created 
						  + resource "nutanix_virtual_machine" "CC2-VM" { 
						      + api_version                                      = (known after apply) 
						      + availability_zone_reference                      = (known after apply) 
						      + boot_device_disk_address                         = (known after apply) 
						      + boot_device_mac_address                          = (known after apply) 
						      + boot_device_order_list                           = [ 
						          + "DISK", 
						        ] 
						      + boot_type                                        = "LEGACY" 
						      + cloud_init_cdrom_uuid                            = (known after apply) 
						      + cluster_name                                     = (known after apply) 
						      + cluster_uuid                                     = "00061bb2-8c9f-2210-41e4-48210b5c6d75" 
						      + description                                      = "TACG-NTX-Cloud Connector 2" 
						      + enable_cpu_passthrough                           = false 
						      + enable_script_exec                               = (known after apply) 
						      + guest_customization_cloud_init_custom_key_values = (known after apply) 
						      + guest_customization_cloud_init_meta_data         = (known after apply) 
						      + guest_customization_cloud_init_user_data         = (known after apply) 
						      + guest_customization_is_overridable               = (known after apply) 
						      + guest_customization_sysprep                      = { 
						          + "install_type" = "PREPARED" 
						          + "unattend_xml" = "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHVuYXR0ZW5kIHhtbG5zPSJ1cm46c2NoZW1hcy1taWNyb3NvZnQtY29tOnVuYXR0ZW5kIj4KICAgIDxzZXR0aW5ncyBwYXNzPSJvb2JlU3lzdGVtIj4KICAgICAgICA8Y29tcG9uZW50IG5hbWU9Ik1pY3Jvc29mdC1XaW5kb3dzLVNoZWxsLVNldHVwIiBwcm9jZXNzb3JBcmNoaXRlY3R1cmU9ImFtZDY0IiBwdWJsaWNLZXlUb2tlbj0iMzFiZjM4NTZhZDM2NGUzNSIgbGFuZ3VhZ2U9Im5ldXRyYWwiIHZlcnNpb25TY29wZT0ibm9uU3hTIiB4bWxuczp3Y209Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vV01JQ29uZmlnLzIwMDIvU3RhdGUiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiPgogICAgICAgICAgICA8T09CRT4KICAgICAgICAgICAgICAgIDxIaWRlRVVMQVBhZ2U+dHJ1ZTwvSGlkZUVVTEFQYWdlPgogICAgICAgICAgICAgICAgPFNraXBVc2VyT09CRT50cnVlPC9Ta2lwVXNlck9PQkU+CiAgICAgICAgICAgICAgICA8SGlkZU9FTVJlZ2lzdHJhdGlvblNjcmVlbj50cnVlPC9IaWRlT0VNUmVnaXN0cmF0aW9uU2NyZWVuPgogICAgICAgICAgICAgICAgPEhpZGVPbmxpbmVBY2NvdW50U2NyZWVucz50cnVlPC9IaWRlT25saW5lQWNjb3VudFNjcmVlbnM+CiAgICAgICAgICAgICAgICA8SGlkZVdpcmVsZXNzU2V0dXBJbk9PQkU+dHJ1ZTwvSGlkZVdpcmVsZXNzU2V0dXBJbk9PQkU+CiAgICAgICAgICAgICAgICA8TmV0d29ya0xvY2F0aW9uPldvcms8L05ldHdvcmtMb2NhdGlvbj4KICAgICAgICAgICAgICAgIDxQcm90ZWN0WW91clBDPjE8L1Byb3RlY3RZb3VyUEM+CiAgICAgICAgICAgICAgICA8SGlkZUxvY2FsQWNjb3VudFNjcmVlbj50cnVlPC9IaWRlTG9jYWxBY2NvdW50U2NyZWVuPgogICAgICAgICAgICA8L09PQkU+CiAgICAgICAgICAgIDxVc2VyQWNjb3VudHM+CiAgICAgICAgICAgICAgICA8QWRtaW5pc3RyYXRvclBhc3N3b3JkPgogICAgICAgICAgICAgICAgICAgIDxWYWx1ZT5JUUJFQUdrQWJRQnVBRUVBVUFCWEFDRUFRUUJpQURJQU1BQXlBRFFBSVFBeEFDRUFJUUJCQUdRQWJRQnBBRzRBYVFCekFIUUFjZ0JoQUhRQWJ3QnlBRkFBWVFCekFITUFkd0J2QUhJQVpBQT08L1ZhbHVlPgogICAgICAgICAgICAgICAgICAgIDxQbGFpblRleHQ+ZmFsc2U8L1BsYWluVGV4dD4KICAgICAgICAgICAgICAgIDwvQWRtaW5pc3RyYXRvclBhc3N3b3JkPgogICAgICAgICAgICA8L1VzZXJBY2NvdW50cz4KICAgICAgICAgICAgPEF1dG9Mb2dvbj4KICAgICAgICAgICAgICAgIDxQYXNzd29yZD4KICAgICAgICAgICAgICAgICAgICA8VmFsdWU+SVFCRUFHa0FiUUJ1QUVFQVVBQlhBQ0VBUVFCaUFESUFNQUF5QURRQUlRQXhBQ0VBSVFCUUFHRUFjd0J6QUhjQWJ3QnlBR1FBPC9WYWx1ZT4KICAgICAgICAgICAgICAgICAgICA8UGxhaW5UZXh0PmZhbHNlPC9QbGFpblRleHQ+CiAgICAgICAgICAgICAgICA8L1Bhc3N3b3JkPgogICAgICAgICAgICAgICAgPEVuYWJsZWQ+dHJ1ZTwvRW5hYmxlZD4KICAgICAgICAgICAgICAgIDxMb2dvbkNvdW50PjE8L0xvZ29uQ291bnQ+CiAgICAgICAgICAgICAgICA8VXNlcm5hbWU+QWRtaW5pc3RyYXRvcjwvVXNlcm5hbWU+CiAgICAgICAgICAgIDwvQXV0b0xvZ29uPgogICAgICAgICAgICA8Rmlyc3RMb2dvbkNvbW1hbmRzPgogICAgICAgICAgICAgICAgPFN5bmNocm9ub3VzQ29tbWFuZCB3Y206YWN0aW9uPSJhZGQiPgogICAgICAgICAgICAgICAgICAgIDxDb21tYW5kTGluZT5wb3dlcnNoZWxsLmV4ZSAtTm9Qcm9maWxlIC1FeGVjdXRpb25Qb2xpY3kgQnlwYXNzIC1Db21tYW5kICJJbnZva2UtRXhwcmVzc2lvbiAoSW52b2tlLVdlYlJlcXVlc3QgLVVzZUJhc2ljUGFyc2luZyAtVXJpICdodHRwczovL3dtd2Jsb2IuYmxvYi5jb3JlLndpbmRvd3MubmV0L3RmZGF0YS9FbmFibGVXaW5STUZpcnN0UnVuLnBzMScpIjwvQ29tbWFuZExpbmU+CiAgICAgICAgICAgICAgICAgICAgPERlc2NyaXB0aW9uPnBvd2Vyc2hlbGxfcnVub25jZTwvRGVzY3JpcHRpb24+CiAgICAgICAgICAgICAgICAgICAgPE9yZGVyPjE8L09yZGVyPgogICAgICAgICAgICAgICAgPC9TeW5jaHJvbm91c0NvbW1hbmQ+CiAgICAgICAgICAgIDwvRmlyc3RMb2dvbkNvbW1hbmRzPgogICAgICAgIDwvY29tcG9uZW50PgogICAgICAgIDxjb21wb25lbnQgbmFtZT0iTWljcm9zb2Z0LVdpbmRvd3MtSW50ZXJuYXRpb25hbC1Db3JlIiBwcm9jZXNzb3JBcmNoaXRlY3R1cmU9ImFtZDY0IiBwdWJsaWNLZXlUb2tlbj0iMzFiZjM4NTZhZDM2NGUzNSIgbGFuZ3VhZ2U9Im5ldXRyYWwiIHZlcnNpb25TY29wZT0ibm9uU3hTIiB4bWxuczp3Y209Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vV01JQ29uZmlnLzIwMDIvU3RhdGUiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiPgogICAgICAgICAgICA8SW5wdXRMb2NhbGU+ZGUtQVQ8L0lucHV0TG9jYWxlPgogICAgICAgICAgICA8U3lzdGVtTG9jYWxlPmRlLUFUPC9TeXN0ZW1Mb2NhbGU+CiAgICAgICAgICAgIDxVSUxhbmd1YWdlPmVuLVVTPC9VSUxhbmd1YWdlPgogICAgICAgICAgICA8VXNlckxvY2FsZT5kZS1BVDwvVXNlckxvY2FsZT4KICAgICAgICAgICAgPFVJTGFuZ3VhZ2VGYWxsYmFjaz5kZS1ERTwvVUlMYW5ndWFnZUZhbGxiYWNrPgogICAgICAgIDwvY29tcG9uZW50PgogICAgPC9zZXR0aW5ncz4KICAgIDxzZXR0aW5ncyBwYXNzPSJzcGVjaWFsaXplIj4KICAgICAgICA8Y29tcG9uZW50IG5hbWU9Ik1pY3Jvc29mdC1XaW5kb3dzLVNoZWxsLVNldHVwIiBwcm9jZXNzb3JBcmNoaXRlY3R1cmU9ImFtZDY0IiBwdWJsaWNLZXlUb2tlbj0iMzFiZjM4NTZhZDM2NGUzNSIgbGFuZ3VhZ2U9Im5ldXRyYWwiIHZlcnNpb25TY29wZT0ibm9uU3hTIiB4bWxuczp3Y209Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vV01JQ29uZmlnLzIwMDIvU3RhdGUiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiPgogICAgICAgICAgICA8UmVnaXN0ZXJlZE9yZ2FuaXphdGlvbj5UQUNHIE51dGFuaXg8L1JlZ2lzdGVyZWRPcmdhbml6YXRpb24+CiAgICAgICAgICAgIDxSZWdpc3RlcmVkT3duZXI+VEFDRyBOdXRhbml4PC9SZWdpc3RlcmVkT3duZXI+CiAgICAgICAgICAgIDxUaW1lWm9uZT5HTVQgU3RhbmRhcmQgVGltZTwvVGltZVpvbmU+CiAgICAgICAgICAgIDxQcm9kdWN0S2V5Pk5SVjRRLVQ3WTczLUhNSjZWLTI4SlA4LTI5MlhZPC9Qcm9kdWN0S2V5PgogICAgICAgICAgICA8Q29tcHV0ZXJOYW1lPlRBQ0ctTlRYLUNDMjwvQ29tcHV0ZXJOYW1lPgogICAgICAgIDwvY29tcG9uZW50PgogICAgICAgIDxjb21wb25lbnQgbmFtZT0iTWljcm9zb2Z0LVdpbmRvd3MtVW5hdHRlbmRlZEpvaW4iIHByb2Nlc3NvckFyY2hpdGVjdHVyZT0iYW1kNjQiIHB1YmxpY0tleVRva2VuPSIzMWJmMzg1NmFkMzY0ZTM1IiBsYW5ndWFnZT0ibmV1dHJhbCIgdmVyc2lvblNjb3BlPSJub25TeFMiIHhtbG5zOndjbT0iaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS9XTUlDb25maWcvMjAwMi9TdGF0ZSIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSI+CiAgICAgICAgICAgIDxJZGVudGlmaWNhdGlvbj4KICAgICAgICAgICAgICAgIDxDcmVkZW50aWFscz4KICAgICAgICAgICAgICAgICAgICA8RG9tYWluPlRBQ0c8L0RvbWFpbj4KICAgICAgICAgICAgICAgICAgICA8VXNlcm5hbWU+YWRtaW48L1VzZXJuYW1lPgogICAgICAgICAgICAgICAgICAgIDxQYXNzd29yZD4hRGltbkFQVyFBYjIwMjQhMSEhPC9QYXNzd29yZD4KICAgICAgICAgICAgICAgIDwvQ3JlZGVudGlhbHM+CiAgICAgICAgICAgICAgICA8Sm9pbkRvbWFpbj50aGUtYXVzdHJpYW4tY2l0cml4LWd1eS5hdDwvSm9pbkRvbWFpbj4KICAgICAgICAgICAgICAgIDxNYWNoaW5lT2JqZWN0T1U+T1U9Q29tcHV0ZXJzLGRjPXRoZSxkYz1hdXN0cmlhbixkYz1jaXRyaXgsZGM9Z3V5LGRjPWF0PC9NYWNoaW5lT2JqZWN0T1U+CiAgICAgICAgICAgICAgICA8VW5zZWN1cmVKb2luIC8+CiAgICAgICAgICAgICAgICA8VGltZW91dFBlcmlvZEluTWludXRlcyAvPgogICAgICAgICAgICAgICAgPERlYnVnSm9pbk9ubHlPblRoaXNFcnJvciAvPgogICAgICAgICAgICAgICAgPERlYnVnSm9pbiAvPgogICAgICAgICAgICA8L0lkZW50aWZpY2F0aW9uPgogICAgICAgIDwvY29tcG9uZW50PgogICAgPC9zZXR0aW5ncz4KPC91bmF0dGVuZD4K" 
						        } 
						      + guest_customization_sysprep_custom_key_values    = (known after apply) 
						      + guest_os_id                                      = (known after apply) 
						      + hardware_clock_timezone                          = (known after apply) 
						      + host_reference                                   = (known after apply) 
						      + hypervisor_type                                  = (known after apply) 
						      + id                                               = (known after apply) 
						      + is_vcpu_hard_pinned                              = false 
						      + machine_type                                     = (known after apply) 
						      + memory_size_mib                                  = 8192 
						      + metadata                                         = (known after apply) 
						      + name                                             = "TACG-NTX-CC2" 
						      + ngt_credentials                                  = (known after apply) 
						      + ngt_enabled_capability_list                      = (known after apply) 
						      + nic_list_status                                  = (known after apply) 
						      + num_sockets                                      = 1 
						      + num_vcpus_per_socket                             = 2 
						      + num_vnuma_nodes                                  = (known after apply) 
						      + nutanix_guest_tools                              = (known after apply) 
						      + owner_reference                                  = (known after apply) 
						      + parent_reference                                 = (known after apply) 
						      + power_state                                      = (known after apply) 
						      + power_state_mechanism                            = (known after apply) 
						      + project_reference                                = (known after apply) 
						      + should_fail_on_script_failure                    = (known after apply) 
						      + state                                            = (known after apply) 
						      + use_hot_add                                      = true 
						      + vga_console_enabled                              = (known after apply) 
						 
						      + categories (known after apply) 
						 
						      + disk_list { 
						          + data_source_reference  = { 
						              + "kind" = "image" 
						              + "uuid" = "57d43449-fc0b-4afc-bf49-7c05cc8f92cf" 
						            } 
						          + disk_size_bytes        = 0 
						          + disk_size_mib          = 0 
						          + uuid                   = (known after apply) 
						          + volume_group_reference = (known after apply) 
						        } 
						 
						      + gpu_list (known after apply) 
						 
						      + nic_list { 
						          + is_connected                     = "true" 
						          + mac_address                      = (known after apply) 
						          + model                            = (known after apply) 
						          + network_function_chain_reference = (known after apply) 
						          + network_function_nic_type        = (known after apply) 
						          + nic_type                         = (known after apply) 
						          + num_queues                       = (known after apply) 
						          + subnet_name                      = (known after apply) 
						          + subnet_uuid                      = "5584d26a-f126-493e-84c5-9254c3d4378e" 
						          + uuid                             = (known after apply) 
						 
						          + ip_endpoint_list { 
						              + ip   = "10.10.174.2" 
						              + type = "ASSIGNED" 
						            } 
						        } 
						    } 
						 
						Plan: 3 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\_CCRLOnNutanix-Create&gt; terraform apply 
						data.template_file.AutoUnattendXMLAVM: Reading... 
						data.template_file.AutoUnattendXMLCC1: Reading... 
						data.template_file.AutoUnattendXMLCC2: Reading... 
						data.template_file.AutoUnattendXMLCC1: Read complete after 0s [id=25874adf0f57c5ea73492890e2ef426d9bce8704b6a7528d1a22e82677572f79] 
						data.template_file.AutoUnattendXMLAVM: Read complete after 0s [id=6e382d6b4dd4d570081a0529fee241efb9fddebee9362fb8caccb78b76486682] 
						data.template_file.AutoUnattendXMLCC2: Read complete after 0s [id=58938910dbd0eea5ab148aa2ed05d885febedd315231c2aeb39e5409a134d430] 
						data.nutanix_clusters.NTX-Clusters: Reading... 
						data.nutanix_subnets.NTX-Subnets: Reading... 
						data.nutanix_subnets.NTX-Subnets: Read complete after 0s [id=terraform-20240709104639964200000001] 
						data.nutanix_clusters.NTX-Clusters: Read complete after 0s [id=terraform-20240709104640065400000002] 
						data.nutanix_cluster.NTX-Cluster: Reading... 
						data.nutanix_cluster.NTX-Cluster: Read complete after 0s [id=00061bb2-8c9f-2210-41e4-48210b5c6d75] 
						 
						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: 
						 
						  # nutanix_virtual_machine.AVM-VM will be created 
						  + resource "nutanix_virtual_machine" "AVM-VM" { 
						      + api_version                                      = (known after apply) 
						      + availability_zone_reference                      = (known after apply) 
						      + boot_device_disk_address                         = (known after apply) 
						      + boot_device_mac_address                          = (known after apply) 
						      + boot_device_order_list                           = [ 
						          + "DISK", 
						        ] 
						      + boot_type                                        = "LEGACY" 
						      + cloud_init_cdrom_uuid                            = (known after apply) 
						      + cluster_name                                     = (known after apply) 
						      + cluster_uuid                                     = "00061bb2-8c9f-2210-41e4-48210b5c6d75" 
						      + description                                      = "TACG-NTX Admin VM" 
						      + enable_cpu_passthrough                           = false 
						      + enable_script_exec                               = (known after apply) 
						      + guest_customization_cloud_init_custom_key_values = (known after apply) 
						      + guest_customization_cloud_init_meta_data         = (known after apply) 
						      + guest_customization_cloud_init_user_data         = (known after apply) 
						      + guest_customization_is_overridable               = (known after apply) 
						      + guest_customization_sysprep                      = { 
						          + "install_type" = "PREPARED" 
						          + "unattend_xml" = "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHVuYXR0ZW5kIHhtbG5zPSJ1cm46c2NoZW1hcy1taWNyb3NvZnQtY29tOnVuYXR0ZW5kIj4KICAgIDxzZXR0aW5ncyBwYXNzPSJvb2JlU3lzdGVtIj4KICAgICAgICA8Y29tcG9uZW50IG5hbWU9Ik1pY3Jvc29mdC1XaW5kb3dzLVNoZWxsLVNldHVwIiBwcm9jZXNzb3JBcmNoaXRlY3R1cmU9ImFtZDY0IiBwdWJsaWNLZXlUb2tlbj0iMzFiZjM4NTZhZDM2NGUzNSIgbGFuZ3VhZ2U9Im5ldXRyYWwiIHZlcnNpb25TY29wZT0ibm9uU3hTIiB4bWxuczp3Y209Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vV01JQ29uZmlnLzIwMDIvU3RhdGUiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiPgogICAgICAgICAgICA8T09CRT4KICAgICAgICAgICAgICAgIDxIaWRlRVVMQVBhZ2U+dHJ1ZTwvSGlkZUVVTEFQYWdlPgogICAgICAgICAgICAgICAgPFNraXBVc2VyT09CRT50cnVlPC9Ta2lwVXNlck9PQkU+CiAgICAgICAgICAgICAgICA8SGlkZU9FTVJlZ2lzdHJhdGlvblNjcmVlbj50cnVlPC9IaWRlT0VNUmVnaXN0cmF0aW9uU2NyZWVuPgogICAgICAgICAgICAgICAgPEhpZGVPbmxpbmVBY2NvdW50U2NyZWVucz50cnVlPC9IaWRlT25saW5lQWNjb3VudFNjcmVlbnM+CiAgICAgICAgICAgICAgICA8SGlkZVdpcmVsZXNzU2V0dXBJbk9PQkU+dHJ1ZTwvSGlkZVdpcmVsZXNzU2V0dXBJbk9PQkU+CiAgICAgICAgICAgICAgICA8TmV0d29ya0xvY2F0aW9uPldvcms8L05ldHdvcmtMb2NhdGlvbj4KICAgICAgICAgICAgICAgIDxQcm90ZWN0WW91clBDPjE8L1Byb3RlY3RZb3VyUEM+CiAgICAgICAgICAgICAgICA8SGlkZUxvY2FsQWNjb3VudFNjcmVlbj50cnVlPC9IaWRlTG9jYWxBY2NvdW50U2NyZWVuPgogICAgICAgICAgICA8L09PQkU+CiAgICAgICAgICAgIDxVc2VyQWNjb3VudHM+CiAgICAgICAgICAgICAgICA8QWRtaW5pc3RyYXRvclBhc3N3b3JkPgogICAgICAgICAgICAgICAgICAgIDxWYWx1ZT5JUUJFQUdrQWJRQnVBRUVBVUFCWEFDRUFRUUJpQURJQU1BQXlBRFFBSVFBeEFDRUFJUUJCQUdRQWJRQnBBRzRBYVFCekFIUUFjZ0JoQUhRQWJ3QnlBRkFBWVFCekFITUFkd0J2QUhJQVpBQT08L1ZhbHVlPgogICAgICAgICAgICAgICAgICAgIDxQbGFpblRleHQ+ZmFsc2U8L1BsYWluVGV4dD4KICAgICAgICAgICAgICAgIDwvQWRtaW5pc3RyYXRvclBhc3N3b3JkPgogICAgICAgICAgICA8L1VzZXJBY2NvdW50cz4KICAgICAgICAgICAgPEF1dG9Mb2dvbj4KICAgICAgICAgICAgICAgIDxQYXNzd29yZD4KICAgICAgICAgICAgICAgICAgICA8VmFsdWU+SVFCRUFHa0FiUUJ1QUVFQVVBQlhBQ0VBUVFCaUFESUFNQUF5QURRQUlRQXhBQ0VBSVFCUUFHRUFjd0J6QUhjQWJ3QnlBR1FBPC9WYWx1ZT4KICAgICAgICAgICAgICAgICAgICA8UGxhaW5UZXh0PmZhbHNlPC9QbGFpblRleHQ+CiAgICAgICAgICAgICAgICA8L1Bhc3N3b3JkPgogICAgICAgICAgICAgICAgPEVuYWJsZWQ+dHJ1ZTwvRW5hYmxlZD4KICAgICAgICAgICAgICAgIDxMb2dvbkNvdW50PjE8L0xvZ29uQ291bnQ+CiAgICAgICAgICAgICAgICA8VXNlcm5hbWU+QWRtaW5pc3RyYXRvcjwvVXNlcm5hbWU+CiAgICAgICAgICAgIDwvQXV0b0xvZ29uPgogICAgICAgICAgICA8Rmlyc3RMb2dvbkNvbW1hbmRzPgogICAgICAgICAgICAgICAgPFN5bmNocm9ub3VzQ29tbWFuZCB3Y206YWN0aW9uPSJhZGQiPgogICAgICAgICAgICAgICAgICAgIDxDb21tYW5kTGluZT5wb3dlcnNoZWxsLmV4ZSAtTm9Qcm9maWxlIC1FeGVjdXRpb25Qb2xpY3kgQnlwYXNzIC1Db21tYW5kICJJbnZva2UtRXhwcmVzc2lvbiAoSW52b2tlLVdlYlJlcXVlc3QgLVVzZUJhc2ljUGFyc2luZyAtVXJpICdodHRwczovL3dtd2Jsb2IuYmxvYi5jb3JlLndpbmRvd3MubmV0L3RmZGF0YS9FbmFibGVXaW5STUZpcnN0UnVuLnBzMScpIjwvQ29tbWFuZExpbmU+CiAgICAgICAgICAgICAgICAgICAgPERlc2NyaXB0aW9uPnBvd2Vyc2hlbGxfcnVub25jZTwvRGVzY3JpcHRpb24+CiAgICAgICAgICAgICAgICAgICAgPE9yZGVyPjE8L09yZGVyPgogICAgICAgICAgICAgICAgPC9TeW5jaHJvbm91c0NvbW1hbmQ+CiAgICAgICAgICAgIDwvRmlyc3RMb2dvbkNvbW1hbmRzPgogICAgICAgIDwvY29tcG9uZW50PgogICAgICAgIDxjb21wb25lbnQgbmFtZT0iTWljcm9zb2Z0LVdpbmRvd3MtSW50ZXJuYXRpb25hbC1Db3JlIiBwcm9jZXNzb3JBcmNoaXRlY3R1cmU9ImFtZDY0IiBwdWJsaWNLZXlUb2tlbj0iMzFiZjM4NTZhZDM2NGUzNSIgbGFuZ3VhZ2U9Im5ldXRyYWwiIHZlcnNpb25TY29wZT0ibm9uU3hTIiB4bWxuczp3Y209Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vV01JQ29uZmlnLzIwMDIvU3RhdGUiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiPgogICAgICAgICAgICA8SW5wdXRMb2NhbGU+ZGUtQVQ8L0lucHV0TG9jYWxlPgogICAgICAgICAgICA8U3lzdGVtTG9jYWxlPmRlLUFUPC9TeXN0ZW1Mb2NhbGU+CiAgICAgICAgICAgIDxVSUxhbmd1YWdlPmVuLVVTPC9VSUxhbmd1YWdlPgogICAgICAgICAgICA8VXNlckxvY2FsZT5kZS1BVDwvVXNlckxvY2FsZT4KICAgICAgICAgICAgPFVJTGFuZ3VhZ2VGYWxsYmFjaz5kZS1ERTwvVUlMYW5ndWFnZUZhbGxiYWNrPgogICAgICAgIDwvY29tcG9uZW50PgogICAgPC9zZXR0aW5ncz4KICAgIDxzZXR0aW5ncyBwYXNzPSJzcGVjaWFsaXplIj4KICAgICAgICA8Y29tcG9uZW50IG5hbWU9Ik1pY3Jvc29mdC1XaW5kb3dzLVNoZWxsLVNldHVwIiBwcm9jZXNzb3JBcmNoaXRlY3R1cmU9ImFtZDY0IiBwdWJsaWNLZXlUb2tlbj0iMzFiZjM4NTZhZDM2NGUzNSIgbGFuZ3VhZ2U9Im5ldXRyYWwiIHZlcnNpb25TY29wZT0ibm9uU3hTIiB4bWxuczp3Y209Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vV01JQ29uZmlnLzIwMDIvU3RhdGUiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiPgogICAgICAgICAgICA8UmVnaXN0ZXJlZE9yZ2FuaXphdGlvbj5UQUNHIE51dGFuaXg8L1JlZ2lzdGVyZWRPcmdhbml6YXRpb24+CiAgICAgICAgICAgIDxSZWdpc3RlcmVkT3duZXI+VEFDRyBOdXRhbml4PC9SZWdpc3RlcmVkT3duZXI+CiAgICAgICAgICAgIDxUaW1lWm9uZT5HTVQgU3RhbmRhcmQgVGltZTwvVGltZVpvbmU+CiAgICAgICAgICAgIDxQcm9kdWN0S2V5Pk5SVjRRLVQ3WTczLUhNSjZWLTI4SlA4LTI5MlhZPC9Qcm9kdWN0S2V5PgogICAgICAgICAgICA8Q29tcHV0ZXJOYW1lPlRBQ0ctTlRYLUFWTTwvQ29tcHV0ZXJOYW1lPgogICAgICAgIDwvY29tcG9uZW50PgogICAgICAgIDxjb21wb25lbnQgbmFtZT0iTWljcm9zb2Z0LVdpbmRvd3MtVW5hdHRlbmRlZEpvaW4iIHByb2Nlc3NvckFyY2hpdGVjdHVyZT0iYW1kNjQiIHB1YmxpY0tleVRva2VuPSIzMWJmMzg1NmFkMzY0ZTM1IiBsYW5ndWFnZT0ibmV1dHJhbCIgdmVyc2lvblNjb3BlPSJub25TeFMiIHhtbG5zOndjbT0iaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS9XTUlDb25maWcvMjAwMi9TdGF0ZSIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSI+CiAgICAgICAgICAgIDxJZGVudGlmaWNhdGlvbj4KICAgICAgICAgICAgICAgIDxDcmVkZW50aWFscz4KICAgICAgICAgICAgICAgICAgICA8RG9tYWluPlRBQ0c8L0RvbWFpbj4KICAgICAgICAgICAgICAgICAgICA8VXNlcm5hbWU+YWRtaW48L1VzZXJuYW1lPgogICAgICAgICAgICAgICAgICAgIDxQYXNzd29yZD4hRGltbkFQVyFBYjIwMjQhMSEhPC9QYXNzd29yZD4KICAgICAgICAgICAgICAgIDwvQ3JlZGVudGlhbHM+CiAgICAgICAgICAgICAgICA8Sm9pbkRvbWFpbj50aGUtYXVzdHJpYW4tY2l0cml4LWd1eS5hdDwvSm9pbkRvbWFpbj4KICAgICAgICAgICAgICAgIDxNYWNoaW5lT2JqZWN0T1U+T1U9Q29tcHV0ZXJzLGRjPXRoZSxkYz1hdXN0cmlhbixkYz1jaXRyaXgsZGM9Z3V5LGRjPWF0PC9NYWNoaW5lT2JqZWN0T1U+CiAgICAgICAgICAgICAgICA8VW5zZWN1cmVKb2luIC8+CiAgICAgICAgICAgICAgICA8VGltZW91dFBlcmlvZEluTWludXRlcyAvPgogICAgICAgICAgICAgICAgPERlYnVnSm9pbk9ubHlPblRoaXNFcnJvciAvPgogICAgICAgICAgICAgICAgPERlYnVnSm9pbiAvPgogICAgICAgICAgICA8L0lkZW50aWZpY2F0aW9uPgogICAgICAgIDwvY29tcG9uZW50PgogICAgPC9zZXR0aW5ncz4KPC91bmF0dGVuZD4K" 
						        } 
						      + guest_customization_sysprep_custom_key_values    = (known after apply) 
						      + guest_os_id                                      = (known after apply) 
						      + hardware_clock_timezone                          = (known after apply) 
						      + host_reference                                   = (known after apply) 
						      + hypervisor_type                                  = (known after apply) 
						      + id                                               = (known after apply) 
						      + is_vcpu_hard_pinned                              = false 
						      + machine_type                                     = (known after apply) 
						      + memory_size_mib                                  = 8192 
						      + metadata                                         = (known after apply) 
						      + name                                             = "TACG-NTX-AVM" 
						      + ngt_credentials                                  = (known after apply) 
						      + ngt_enabled_capability_list                      = (known after apply) 
						      + nic_list_status                                  = (known after apply) 
						      + num_sockets                                      = 1 
						      + num_vcpus_per_socket                             = 2 
						      + num_vnuma_nodes                                  = (known after apply) 
						      + nutanix_guest_tools                              = (known after apply) 
						      + owner_reference                                  = (known after apply) 
						      + parent_reference                                 = (known after apply) 
						      + power_state                                      = (known after apply) 
						      + power_state_mechanism                            = (known after apply) 
						      + project_reference                                = (known after apply) 
						      + should_fail_on_script_failure                    = (known after apply) 
						      + state                                            = (known after apply) 
						      + use_hot_add                                      = true 
						      + vga_console_enabled                              = (known after apply) 
						 
						      + categories (known after apply) 
						 
						      + disk_list { 
						          + data_source_reference  = { 
						              + "kind" = "image" 
						              + "uuid" = "57d43449-fc0b-4afc-bf49-7c05cc8f92cf" 
						            } 
						          + disk_size_bytes        = 0 
						          + disk_size_mib          = 0 
						          + uuid                   = (known after apply) 
						          + volume_group_reference = (known after apply) 
						        } 
						 
						      + gpu_list (known after apply) 
						 
						      + nic_list { 
						          + is_connected                     = "true" 
						          + mac_address                      = (known after apply) 
						          + model                            = (known after apply) 
						          + network_function_chain_reference = (known after apply) 
						          + network_function_nic_type        = (known after apply) 
						          + nic_type                         = (known after apply) 
						          + num_queues                       = (known after apply) 
						          + subnet_name                      = (known after apply) 
						          + subnet_uuid                      = "5584d26a-f126-493e-84c5-9254c3d4378e" 
						          + uuid                             = (known after apply) 
						 
						          + ip_endpoint_list { 
						              + ip   = "10.10.174.3" 
						              + type = "ASSIGNED" 
						            } 
						        } 
						    } 
						 
						  # nutanix_virtual_machine.CC1-VM will be created 
						  + resource "nutanix_virtual_machine" "CC1-VM" { 
						      + api_version                                      = (known after apply) 
						      + availability_zone_reference                      = (known after apply) 
						      + boot_device_disk_address                         = (known after apply) 
						      + boot_device_mac_address                          = (known after apply) 
						      + boot_device_order_list                           = [ 
						          + "DISK", 
						        ] 
						      + boot_type                                        = "LEGACY" 
						      + cloud_init_cdrom_uuid                            = (known after apply) 
						      + cluster_name                                     = (known after apply) 
						      + cluster_uuid                                     = "00061bb2-8c9f-2210-41e4-48210b5c6d75" 
						      + description                                      = "TACG-NTX-Cloud Connector 1" 
						      + enable_cpu_passthrough                           = false 
						      + enable_script_exec                               = (known after apply) 
						      + guest_customization_cloud_init_custom_key_values = (known after apply) 
						      + guest_customization_cloud_init_meta_data         = (known after apply) 
						      + guest_customization_cloud_init_user_data         = (known after apply) 
						      + guest_customization_is_overridable               = (known after apply) 
						      + guest_customization_sysprep                      = { 
						          + "install_type" = "PREPARED" 
						          + "unattend_xml" = "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHVuYXR0ZW5kIHhtbG5zPSJ1cm46c2NoZW1hcy1taWNyb3NvZnQtY29tOnVuYXR0ZW5kIj4KICAgIDxzZXR0aW5ncyBwYXNzPSJvb2JlU3lzdGVtIj4KICAgICAgICA8Y29tcG9uZW50IG5hbWU9Ik1pY3Jvc29mdC1XaW5kb3dzLVNoZWxsLVNldHVwIiBwcm9jZXNzb3JBcmNoaXRlY3R1cmU9ImFtZDY0IiBwdWJsaWNLZXlUb2tlbj0iMzFiZjM4NTZhZDM2NGUzNSIgbGFuZ3VhZ2U9Im5ldXRyYWwiIHZlcnNpb25TY29wZT0ibm9uU3hTIiB4bWxuczp3Y209Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vV01JQ29uZmlnLzIwMDIvU3RhdGUiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiPgogICAgICAgICAgICA8T09CRT4KICAgICAgICAgICAgICAgIDxIaWRlRVVMQVBhZ2U+dHJ1ZTwvSGlkZUVVTEFQYWdlPgogICAgICAgICAgICAgICAgPFNraXBVc2VyT09CRT50cnVlPC9Ta2lwVXNlck9PQkU+CiAgICAgICAgICAgICAgICA8SGlkZU9FTVJlZ2lzdHJhdGlvblNjcmVlbj50cnVlPC9IaWRlT0VNUmVnaXN0cmF0aW9uU2NyZWVuPgogICAgICAgICAgICAgICAgPEhpZGVPbmxpbmVBY2NvdW50U2NyZWVucz50cnVlPC9IaWRlT25saW5lQWNjb3VudFNjcmVlbnM+CiAgICAgICAgICAgICAgICA8SGlkZVdpcmVsZXNzU2V0dXBJbk9PQkU+dHJ1ZTwvSGlkZVdpcmVsZXNzU2V0dXBJbk9PQkU+CiAgICAgICAgICAgICAgICA8TmV0d29ya0xvY2F0aW9uPldvcms8L05ldHdvcmtMb2NhdGlvbj4KICAgICAgICAgICAgICAgIDxQcm90ZWN0WW91clBDPjE8L1Byb3RlY3RZb3VyUEM+CiAgICAgICAgICAgICAgICA8SGlkZUxvY2FsQWNjb3VudFNjcmVlbj50cnVlPC9IaWRlTG9jYWxBY2NvdW50U2NyZWVuPgogICAgICAgICAgICA8L09PQkU+CiAgICAgICAgICAgIDxVc2VyQWNjb3VudHM+CiAgICAgICAgICAgICAgICA8QWRtaW5pc3RyYXRvclBhc3N3b3JkPgogICAgICAgICAgICAgICAgICAgIDxWYWx1ZT5JUUJFQUdrQWJRQnVBRUVBVUFCWEFDRUFRUUJpQURJQU1BQXlBRFFBSVFBeEFDRUFJUUJCQUdRQWJRQnBBRzRBYVFCekFIUUFjZ0JoQUhRQWJ3QnlBRkFBWVFCekFITUFkd0J2QUhJQVpBQT08L1ZhbHVlPgogICAgICAgICAgICAgICAgICAgIDxQbGFpblRleHQ+ZmFsc2U8L1BsYWluVGV4dD4KICAgICAgICAgICAgICAgIDwvQWRtaW5pc3RyYXRvclBhc3N3b3JkPgogICAgICAgICAgICA8L1VzZXJBY2NvdW50cz4KICAgICAgICAgICAgPEF1dG9Mb2dvbj4KICAgICAgICAgICAgICAgIDxQYXNzd29yZD4KICAgICAgICAgICAgICAgICAgICA8VmFsdWU+SVFCRUFHa0FiUUJ1QUVFQVVBQlhBQ0VBUVFCaUFESUFNQUF5QURRQUlRQXhBQ0VBSVFCUUFHRUFjd0J6QUhjQWJ3QnlBR1FBPC9WYWx1ZT4KICAgICAgICAgICAgICAgICAgICA8UGxhaW5UZXh0PmZhbHNlPC9QbGFpblRleHQ+CiAgICAgICAgICAgICAgICA8L1Bhc3N3b3JkPgogICAgICAgICAgICAgICAgPEVuYWJsZWQ+dHJ1ZTwvRW5hYmxlZD4KICAgICAgICAgICAgICAgIDxMb2dvbkNvdW50PjE8L0xvZ29uQ291bnQ+CiAgICAgICAgICAgICAgICA8VXNlcm5hbWU+QWRtaW5pc3RyYXRvcjwvVXNlcm5hbWU+CiAgICAgICAgICAgIDwvQXV0b0xvZ29uPgogICAgICAgICAgICA8Rmlyc3RMb2dvbkNvbW1hbmRzPgogICAgICAgICAgICAgICAgPFN5bmNocm9ub3VzQ29tbWFuZCB3Y206YWN0aW9uPSJhZGQiPgogICAgICAgICAgICAgICAgICAgIDxDb21tYW5kTGluZT5wb3dlcnNoZWxsLmV4ZSAtTm9Qcm9maWxlIC1FeGVjdXRpb25Qb2xpY3kgQnlwYXNzIC1Db21tYW5kICJJbnZva2UtRXhwcmVzc2lvbiAoSW52b2tlLVdlYlJlcXVlc3QgLVVzZUJhc2ljUGFyc2luZyAtVXJpICdodHRwczovL3dtd2Jsb2IuYmxvYi5jb3JlLndpbmRvd3MubmV0L3RmZGF0YS9FbmFibGVXaW5STUZpcnN0UnVuLnBzMScpIjwvQ29tbWFuZExpbmU+CiAgICAgICAgICAgICAgICAgICAgPERlc2NyaXB0aW9uPnBvd2Vyc2hlbGxfcnVub25jZTwvRGVzY3JpcHRpb24+CiAgICAgICAgICAgICAgICAgICAgPE9yZGVyPjE8L09yZGVyPgogICAgICAgICAgICAgICAgPC9TeW5jaHJvbm91c0NvbW1hbmQ+CiAgICAgICAgICAgIDwvRmlyc3RMb2dvbkNvbW1hbmRzPgogICAgICAgIDwvY29tcG9uZW50PgogICAgICAgIDxjb21wb25lbnQgbmFtZT0iTWljcm9zb2Z0LVdpbmRvd3MtSW50ZXJuYXRpb25hbC1Db3JlIiBwcm9jZXNzb3JBcmNoaXRlY3R1cmU9ImFtZDY0IiBwdWJsaWNLZXlUb2tlbj0iMzFiZjM4NTZhZDM2NGUzNSIgbGFuZ3VhZ2U9Im5ldXRyYWwiIHZlcnNpb25TY29wZT0ibm9uU3hTIiB4bWxuczp3Y209Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vV01JQ29uZmlnLzIwMDIvU3RhdGUiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiPgogICAgICAgICAgICA8SW5wdXRMb2NhbGU+ZGUtQVQ8L0lucHV0TG9jYWxlPgogICAgICAgICAgICA8U3lzdGVtTG9jYWxlPmRlLUFUPC9TeXN0ZW1Mb2NhbGU+CiAgICAgICAgICAgIDxVSUxhbmd1YWdlPmVuLVVTPC9VSUxhbmd1YWdlPgogICAgICAgICAgICA8VXNlckxvY2FsZT5kZS1BVDwvVXNlckxvY2FsZT4KICAgICAgICAgICAgPFVJTGFuZ3VhZ2VGYWxsYmFjaz5kZS1ERTwvVUlMYW5ndWFnZUZhbGxiYWNrPgogICAgICAgIDwvY29tcG9uZW50PgogICAgPC9zZXR0aW5ncz4KICAgIDxzZXR0aW5ncyBwYXNzPSJzcGVjaWFsaXplIj4KICAgICAgICA8Y29tcG9uZW50IG5hbWU9Ik1pY3Jvc29mdC1XaW5kb3dzLVNoZWxsLVNldHVwIiBwcm9jZXNzb3JBcmNoaXRlY3R1cmU9ImFtZDY0IiBwdWJsaWNLZXlUb2tlbj0iMzFiZjM4NTZhZDM2NGUzNSIgbGFuZ3VhZ2U9Im5ldXRyYWwiIHZlcnNpb25TY29wZT0ibm9uU3hTIiB4bWxuczp3Y209Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vV01JQ29uZmlnLzIwMDIvU3RhdGUiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiPgogICAgICAgICAgICA8UmVnaXN0ZXJlZE9yZ2FuaXphdGlvbj5UQUNHIE51dGFuaXg8L1JlZ2lzdGVyZWRPcmdhbml6YXRpb24+CiAgICAgICAgICAgIDxSZWdpc3RlcmVkT3duZXI+VEFDRyBOdXRhbml4PC9SZWdpc3RlcmVkT3duZXI+CiAgICAgICAgICAgIDxUaW1lWm9uZT5HTVQgU3RhbmRhcmQgVGltZTwvVGltZVpvbmU+CiAgICAgICAgICAgIDxQcm9kdWN0S2V5Pk5SVjRRLVQ3WTczLUhNSjZWLTI4SlA4LTI5MlhZPC9Qcm9kdWN0S2V5PgogICAgICAgICAgICA8Q29tcHV0ZXJOYW1lPlRBQ0ctTlRYLUNDMTwvQ29tcHV0ZXJOYW1lPgogICAgICAgIDwvY29tcG9uZW50PgogICAgICAgIDxjb21wb25lbnQgbmFtZT0iTWljcm9zb2Z0LVdpbmRvd3MtVW5hdHRlbmRlZEpvaW4iIHByb2Nlc3NvckFyY2hpdGVjdHVyZT0iYW1kNjQiIHB1YmxpY0tleVRva2VuPSIzMWJmMzg1NmFkMzY0ZTM1IiBsYW5ndWFnZT0ibmV1dHJhbCIgdmVyc2lvblNjb3BlPSJub25TeFMiIHhtbG5zOndjbT0iaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS9XTUlDb25maWcvMjAwMi9TdGF0ZSIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSI+CiAgICAgICAgICAgIDxJZGVudGlmaWNhdGlvbj4KICAgICAgICAgICAgICAgIDxDcmVkZW50aWFscz4KICAgICAgICAgICAgICAgICAgICA8RG9tYWluPlRBQ0c8L0RvbWFpbj4KICAgICAgICAgICAgICAgICAgICA8VXNlcm5hbWU+YWRtaW48L1VzZXJuYW1lPgogICAgICAgICAgICAgICAgICAgIDxQYXNzd29yZD4hRGltbkFQVyFBYjIwMjQhMSEhPC9QYXNzd29yZD4KICAgICAgICAgICAgICAgIDwvQ3JlZGVudGlhbHM+CiAgICAgICAgICAgICAgICA8Sm9pbkRvbWFpbj50aGUtYXVzdHJpYW4tY2l0cml4LWd1eS5hdDwvSm9pbkRvbWFpbj4KICAgICAgICAgICAgICAgIDxNYWNoaW5lT2JqZWN0T1U+T1U9Q29tcHV0ZXJzLGRjPXRoZSxkYz1hdXN0cmlhbixkYz1jaXRyaXgsZGM9Z3V5LGRjPWF0PC9NYWNoaW5lT2JqZWN0T1U+CiAgICAgICAgICAgICAgICA8VW5zZWN1cmVKb2luIC8+CiAgICAgICAgICAgICAgICA8VGltZW91dFBlcmlvZEluTWludXRlcyAvPgogICAgICAgICAgICAgICAgPERlYnVnSm9pbk9ubHlPblRoaXNFcnJvciAvPgogICAgICAgICAgICAgICAgPERlYnVnSm9pbiAvPgogICAgICAgICAgICA8L0lkZW50aWZpY2F0aW9uPgogICAgICAgIDwvY29tcG9uZW50PgogICAgPC9zZXR0aW5ncz4KPC91bmF0dGVuZD4K" 
						        } 
						      + guest_customization_sysprep_custom_key_values    = (known after apply) 
						      + guest_os_id                                      = (known after apply) 
						      + hardware_clock_timezone                          = (known after apply) 
						      + host_reference                                   = (known after apply) 
						      + hypervisor_type                                  = (known after apply) 
						      + id                                               = (known after apply) 
						      + is_vcpu_hard_pinned                              = false 
						      + machine_type                                     = (known after apply) 
						      + memory_size_mib                                  = 192 
						      + metadata                                         = (known after apply) 
						      + name                                             = "TACG-NTX-CC1" 
						      + ngt_credentials                                  = (known after apply) 
						      + ngt_enabled_capability_list                      = (known after apply) 
						      + nic_list_status                                  = (known after apply) 
						      + num_sockets                                      = 1 
						      + num_vcpus_per_socket                             = 2 
						      + num_vnuma_nodes                                  = (known after apply) 
						      + nutanix_guest_tools                              = (known after apply) 
						      + owner_reference                                  = (known after apply) 
						      + parent_reference                                 = (known after apply) 
						      + power_state                                      = (known after apply) 
						      + power_state_mechanism                            = (known after apply) 
						      + project_reference                                = (known after apply) 
						      + should_fail_on_script_failure                    = (known after apply) 
						      + state                                            = (known after apply) 
						      + use_hot_add                                      = true 
						      + vga_console_enabled                              = (known after apply) 
						 
						      + categories (known after apply) 
						 
						      + disk_list { 
						          + data_source_reference  = { 
						              + "kind" = "image" 
						              + "uuid" = "57d43449-fc0b-4afc-bf49-7c05cc8f92cf" 
						            } 
						          + disk_size_bytes        = 0 
						          + disk_size_mib          = 0 
						          + uuid                   = (known after apply) 
						          + volume_group_reference = (known after apply) 
						        } 
						 
						      + gpu_list (known after apply) 
						 
						      + nic_list { 
						          + is_connected                     = "true" 
						          + mac_address                      = (known after apply) 
						          + model                            = (known after apply) 
						          + network_function_chain_reference = (known after apply) 
						          + network_function_nic_type        = (known after apply) 
						          + nic_type                         = (known after apply) 
						          + num_queues                       = (known after apply) 
						          + subnet_name                      = (known after apply) 
						          + subnet_uuid                      = "5584d26a-f126-493e-84c5-9254c3d4378e" 
						          + uuid                             = (known after apply) 
						 
						          + ip_endpoint_list { 
						              + ip   = "10.10.174.1" 
						              + type = "ASSIGNED" 
						            } 
						        } 
						    } 
						 
						  # nutanix_virtual_machine.CC2-VM will be created 
						  + resource "nutanix_virtual_machine" "CC2-VM" { 
						      + api_version                                      = (known after apply) 
						      + availability_zone_reference                      = (known after apply) 
						      + boot_device_disk_address                         = (known after apply) 
						      + boot_device_mac_address                          = (known after apply) 
						      + boot_device_order_list                           = [ 
						          + "DISK", 
						        ] 
						      + boot_type                                        = "LEGACY" 
						      + cloud_init_cdrom_uuid                            = (known after apply) 
						      + cluster_name                                     = (known after apply) 
						      + cluster_uuid                                     = "00061bb2-8c9f-2210-41e4-48210b5c6d75" 
						      + description                                      = "TACG-NTX-Cloud Connector 2" 
						      + enable_cpu_passthrough                           = false 
						      + enable_script_exec                               = (known after apply) 
						      + guest_customization_cloud_init_custom_key_values = (known after apply) 
						      + guest_customization_cloud_init_meta_data         = (known after apply) 
						      + guest_customization_cloud_init_user_data         = (known after apply) 
						      + guest_customization_is_overridable               = (known after apply) 
						      + guest_customization_sysprep                      = { 
						          + "install_type" = "PREPARED" 
						          + "unattend_xml" = "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHVuYXR0ZW5kIHhtbG5zPSJ1cm46c2NoZW1hcy1taWNyb3NvZnQtY29tOnVuYXR0ZW5kIj4KICAgIDxzZXR0aW5ncyBwYXNzPSJvb2JlU3lzdGVtIj4KICAgICAgICA8Y29tcG9uZW50IG5hbWU9Ik1pY3Jvc29mdC1XaW5kb3dzLVNoZWxsLVNldHVwIiBwcm9jZXNzb3JBcmNoaXRlY3R1cmU9ImFtZDY0IiBwdWJsaWNLZXlUb2tlbj0iMzFiZjM4NTZhZDM2NGUzNSIgbGFuZ3VhZ2U9Im5ldXRyYWwiIHZlcnNpb25TY29wZT0ibm9uU3hTIiB4bWxuczp3Y209Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vV01JQ29uZmlnLzIwMDIvU3RhdGUiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiPgogICAgICAgICAgICA8T09CRT4KICAgICAgICAgICAgICAgIDxIaWRlRVVMQVBhZ2U+dHJ1ZTwvSGlkZUVVTEFQYWdlPgogICAgICAgICAgICAgICAgPFNraXBVc2VyT09CRT50cnVlPC9Ta2lwVXNlck9PQkU+CiAgICAgICAgICAgICAgICA8SGlkZU9FTVJlZ2lzdHJhdGlvblNjcmVlbj50cnVlPC9IaWRlT0VNUmVnaXN0cmF0aW9uU2NyZWVuPgogICAgICAgICAgICAgICAgPEhpZGVPbmxpbmVBY2NvdW50U2NyZWVucz50cnVlPC9IaWRlT25saW5lQWNjb3VudFNjcmVlbnM+CiAgICAgICAgICAgICAgICA8SGlkZVdpcmVsZXNzU2V0dXBJbk9PQkU+dHJ1ZTwvSGlkZVdpcmVsZXNzU2V0dXBJbk9PQkU+CiAgICAgICAgICAgICAgICA8TmV0d29ya0xvY2F0aW9uPldvcms8L05ldHdvcmtMb2NhdGlvbj4KICAgICAgICAgICAgICAgIDxQcm90ZWN0WW91clBDPjE8L1Byb3RlY3RZb3VyUEM+CiAgICAgICAgICAgICAgICA8SGlkZUxvY2FsQWNjb3VudFNjcmVlbj50cnVlPC9IaWRlTG9jYWxBY2NvdW50U2NyZWVuPgogICAgICAgICAgICA8L09PQkU+CiAgICAgICAgICAgIDxVc2VyQWNjb3VudHM+CiAgICAgICAgICAgICAgICA8QWRtaW5pc3RyYXRvclBhc3N3b3JkPgogICAgICAgICAgICAgICAgICAgIDxWYWx1ZT5JUUJFQUdrQWJRQnVBRUVBVUFCWEFDRUFRUUJpQURJQU1BQXlBRFFBSVFBeEFDRUFJUUJCQUdRQWJRQnBBRzRBYVFCekFIUUFjZ0JoQUhRQWJ3QnlBRkFBWVFCekFITUFkd0J2QUhJQVpBQT08L1ZhbHVlPgogICAgICAgICAgICAgICAgICAgIDxQbGFpblRleHQ+ZmFsc2U8L1BsYWluVGV4dD4KICAgICAgICAgICAgICAgIDwvQWRtaW5pc3RyYXRvclBhc3N3b3JkPgogICAgICAgICAgICA8L1VzZXJBY2NvdW50cz4KICAgICAgICAgICAgPEF1dG9Mb2dvbj4KICAgICAgICAgICAgICAgIDxQYXNzd29yZD4KICAgICAgICAgICAgICAgICAgICA8VmFsdWU+SVFCRUFHa0FiUUJ1QUVFQVVBQlhBQ0VBUVFCaUFESUFNQUF5QURRQUlRQXhBQ0VBSVFCUUFHRUFjd0J6QUhjQWJ3QnlBR1FBPC9WYWx1ZT4KICAgICAgICAgICAgICAgICAgICA8UGxhaW5UZXh0PmZhbHNlPC9QbGFpblRleHQ+CiAgICAgICAgICAgICAgICA8L1Bhc3N3b3JkPgogICAgICAgICAgICAgICAgPEVuYWJsZWQ+dHJ1ZTwvRW5hYmxlZD4KICAgICAgICAgICAgICAgIDxMb2dvbkNvdW50PjE8L0xvZ29uQ291bnQ+CiAgICAgICAgICAgICAgICA8VXNlcm5hbWU+QWRtaW5pc3RyYXRvcjwvVXNlcm5hbWU+CiAgICAgICAgICAgIDwvQXV0b0xvZ29uPgogICAgICAgICAgICA8Rmlyc3RMb2dvbkNvbW1hbmRzPgogICAgICAgICAgICAgICAgPFN5bmNocm9ub3VzQ29tbWFuZCB3Y206YWN0aW9uPSJhZGQiPgogICAgICAgICAgICAgICAgICAgIDxDb21tYW5kTGluZT5wb3dlcnNoZWxsLmV4ZSAtTm9Qcm9maWxlIC1FeGVjdXRpb25Qb2xpY3kgQnlwYXNzIC1Db21tYW5kICJJbnZva2UtRXhwcmVzc2lvbiAoSW52b2tlLVdlYlJlcXVlc3QgLVVzZUJhc2ljUGFyc2luZyAtVXJpICdodHRwczovL3dtd2Jsb2IuYmxvYi5jb3JlLndpbmRvd3MubmV0L3RmZGF0YS9FbmFibGVXaW5STUZpcnN0UnVuLnBzMScpIjwvQ29tbWFuZExpbmU+CiAgICAgICAgICAgICAgICAgICAgPERlc2NyaXB0aW9uPnBvd2Vyc2hlbGxfcnVub25jZTwvRGVzY3JpcHRpb24+CiAgICAgICAgICAgICAgICAgICAgPE9yZGVyPjE8L09yZGVyPgogICAgICAgICAgICAgICAgPC9TeW5jaHJvbm91c0NvbW1hbmQ+CiAgICAgICAgICAgIDwvRmlyc3RMb2dvbkNvbW1hbmRzPgogICAgICAgIDwvY29tcG9uZW50PgogICAgICAgIDxjb21wb25lbnQgbmFtZT0iTWljcm9zb2Z0LVdpbmRvd3MtSW50ZXJuYXRpb25hbC1Db3JlIiBwcm9jZXNzb3JBcmNoaXRlY3R1cmU9ImFtZDY0IiBwdWJsaWNLZXlUb2tlbj0iMzFiZjM4NTZhZDM2NGUzNSIgbGFuZ3VhZ2U9Im5ldXRyYWwiIHZlcnNpb25TY29wZT0ibm9uU3hTIiB4bWxuczp3Y209Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vV01JQ29uZmlnLzIwMDIvU3RhdGUiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiPgogICAgICAgICAgICA8SW5wdXRMb2NhbGU+ZGUtQVQ8L0lucHV0TG9jYWxlPgogICAgICAgICAgICA8U3lzdGVtTG9jYWxlPmRlLUFUPC9TeXN0ZW1Mb2NhbGU+CiAgICAgICAgICAgIDxVSUxhbmd1YWdlPmVuLVVTPC9VSUxhbmd1YWdlPgogICAgICAgICAgICA8VXNlckxvY2FsZT5kZS1BVDwvVXNlckxvY2FsZT4KICAgICAgICAgICAgPFVJTGFuZ3VhZ2VGYWxsYmFjaz5kZS1ERTwvVUlMYW5ndWFnZUZhbGxiYWNrPgogICAgICAgIDwvY29tcG9uZW50PgogICAgPC9zZXR0aW5ncz4KICAgIDxzZXR0aW5ncyBwYXNzPSJzcGVjaWFsaXplIj4KICAgICAgICA8Y29tcG9uZW50IG5hbWU9Ik1pY3Jvc29mdC1XaW5kb3dzLVNoZWxsLVNldHVwIiBwcm9jZXNzb3JBcmNoaXRlY3R1cmU9ImFtZDY0IiBwdWJsaWNLZXlUb2tlbj0iMzFiZjM4NTZhZDM2NGUzNSIgbGFuZ3VhZ2U9Im5ldXRyYWwiIHZlcnNpb25TY29wZT0ibm9uU3hTIiB4bWxuczp3Y209Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vV01JQ29uZmlnLzIwMDIvU3RhdGUiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiPgogICAgICAgICAgICA8UmVnaXN0ZXJlZE9yZ2FuaXphdGlvbj5UQUNHIE51dGFuaXg8L1JlZ2lzdGVyZWRPcmdhbml6YXRpb24+CiAgICAgICAgICAgIDxSZWdpc3RlcmVkT3duZXI+VEFDRyBOdXRhbml4PC9SZWdpc3RlcmVkT3duZXI+CiAgICAgICAgICAgIDxUaW1lWm9uZT5HTVQgU3RhbmRhcmQgVGltZTwvVGltZVpvbmU+CiAgICAgICAgICAgIDxQcm9kdWN0S2V5Pk5SVjRRLVQ3WTczLUhNSjZWLTI4SlA4LTI5MlhZPC9Qcm9kdWN0S2V5PgogICAgICAgICAgICA8Q29tcHV0ZXJOYW1lPlRBQ0ctTlRYLUNDMjwvQ29tcHV0ZXJOYW1lPgogICAgICAgIDwvY29tcG9uZW50PgogICAgICAgIDxjb21wb25lbnQgbmFtZT0iTWljcm9zb2Z0LVdpbmRvd3MtVW5hdHRlbmRlZEpvaW4iIHByb2Nlc3NvckFyY2hpdGVjdHVyZT0iYW1kNjQiIHB1YmxpY0tleVRva2VuPSIzMWJmMzg1NmFkMzY0ZTM1IiBsYW5ndWFnZT0ibmV1dHJhbCIgdmVyc2lvblNjb3BlPSJub25TeFMiIHhtbG5zOndjbT0iaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS9XTUlDb25maWcvMjAwMi9TdGF0ZSIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSI+CiAgICAgICAgICAgIDxJZGVudGlmaWNhdGlvbj4KICAgICAgICAgICAgICAgIDxDcmVkZW50aWFscz4KICAgICAgICAgICAgICAgICAgICA8RG9tYWluPlRBQ0c8L0RvbWFpbj4KICAgICAgICAgICAgICAgICAgICA8VXNlcm5hbWU+YWRtaW48L1VzZXJuYW1lPgogICAgICAgICAgICAgICAgICAgIDxQYXNzd29yZD4hRGltbkFQVyFBYjIwMjQhMSEhPC9QYXNzd29yZD4KICAgICAgICAgICAgICAgIDwvQ3JlZGVudGlhbHM+CiAgICAgICAgICAgICAgICA8Sm9pbkRvbWFpbj50aGUtYXVzdHJpYW4tY2l0cml4LWd1eS5hdDwvSm9pbkRvbWFpbj4KICAgICAgICAgICAgICAgIDxNYWNoaW5lT2JqZWN0T1U+T1U9Q29tcHV0ZXJzLGRjPXRoZSxkYz1hdXN0cmlhbixkYz1jaXRyaXgsZGM9Z3V5LGRjPWF0PC9NYWNoaW5lT2JqZWN0T1U+CiAgICAgICAgICAgICAgICA8VW5zZWN1cmVKb2luIC8+CiAgICAgICAgICAgICAgICA8VGltZW91dFBlcmlvZEluTWludXRlcyAvPgogICAgICAgICAgICAgICAgPERlYnVnSm9pbk9ubHlPblRoaXNFcnJvciAvPgogICAgICAgICAgICAgICAgPERlYnVnSm9pbiAvPgogICAgICAgICAgICA8L0lkZW50aWZpY2F0aW9uPgogICAgICAgIDwvY29tcG9uZW50PgogICAgPC9zZXR0aW5ncz4KPC91bmF0dGVuZD4K" 
						        } 
						      + guest_customization_sysprep_custom_key_values    = (known after apply) 
						      + guest_os_id                                      = (known after apply) 
						      + hardware_clock_timezone                          = (known after apply) 
						      + host_reference                                   = (known after apply) 
						      + hypervisor_type                                  = (known after apply) 
						      + id                                               = (known after apply) 
						      + is_vcpu_hard_pinned                              = false 
						      + machine_type                                     = (known after apply) 
						      + memory_size_mib                                  = 8192 
						      + metadata                                         = (known after apply) 
						      + name                                             = "TACG-NTX-CC2" 
						      + ngt_credentials                                  = (known after apply) 
						      + ngt_enabled_capability_list                      = (known after apply) 
						      + nic_list_status                                  = (known after apply) 
						      + num_sockets                                      = 1 
						      + num_vcpus_per_socket                             = 2 
						      + num_vnuma_nodes                                  = (known after apply) 
						      + nutanix_guest_tools                              = (known after apply) 
						      + owner_reference                                  = (known after apply) 
						      + parent_reference                                 = (known after apply) 
						      + power_state                                      = (known after apply) 
						      + power_state_mechanism                            = (known after apply) 
						      + project_reference                                = (known after apply) 
						      + should_fail_on_script_failure                    = (known after apply) 
						      + state                                            = (known after apply) 
						      + use_hot_add                                      = true 
						      + vga_console_enabled                              = (known after apply) 
						 
						      + categories (known after apply) 
						 
						      + disk_list { 
						          + data_source_reference  = { 
						              + "kind" = "image" 
						              + "uuid" = "57d43449-fc0b-4afc-bf49-7c05cc8f92cf" 
						            } 
						          + disk_size_bytes        = 0 
						          + disk_size_mib          = 0 
						          + uuid                   = (known after apply) 
						          + volume_group_reference = (known after apply) 
						        } 
						 
						      + gpu_list (known after apply) 
						 
						      + nic_list { 
						          + is_connected                     = "true" 
						          + mac_address                      = (known after apply) 
						          + model                            = (known after apply) 
						          + network_function_chain_reference = (known after apply) 
						          + network_function_nic_type        = (known after apply) 
						          + nic_type                         = (known after apply) 
						          + num_queues                       = (known after apply) 
						          + subnet_name                      = (known after apply) 
						          + subnet_uuid                      = "5584d26a-f126-493e-84c5-9254c3d4378e" 
						          + uuid                             = (known after apply) 
						 
						          + ip_endpoint_list { 
						              + ip   = "10.10.174.2" 
						              + type = "ASSIGNED" 
						            } 
						        } 
						    } 
						 
						Plan: 3 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 
						 
						nutanix_virtual_machine.AVM-VM: Creating... 
						nutanix_virtual_machine.CC1-VM: Creating... 
						nutanix_virtual_machine.CC2-VM: Creating... 
						nutanix_virtual_machine.CC1-VM: Still creating... [10s elapsed] 
						nutanix_virtual_machine.AVM-VM: Still creating... [10s elapsed] 
						nutanix_virtual_machine.CC2-VM: Still creating... [10s elapsed] 
						nutanix_virtual_machine.AVM-VM: Creation complete after 11s [id=958ed855-7e1b-4c3a-910e-c6be16184b10] 
						nutanix_virtual_machine.CC1-VM: Creation complete after 11s [id=98a1c161-27a6-46c6-bd57-c46effc585ec] 
						nutanix_virtual_machine.CC2-VM: Creation complete after 11s [id=52864356-8179-451b-8561-7c1d0a2b9d4b] 
						 
						Apply complete! Resources: 3 added, 0 changed, 0 destroyed. 
						PS D:\_ppmm\__tf\_CCRLOnNutanix\_CCRLOnNutanix-Create&gt;
					 
				
			
		
	



	The Terraform flow was completed successfully, and the next module can begin. 
	 
 


	Module 2: Install and Configure all Resources needed for the Resource Location on Nutanix AHV



	
		Important: 
		 
		This module can only be run on the previously created Administrative VM, as Modules 2, 3, and 4 rely heavily on WinRM. 
		Please install Terraform on the Administrative VM before trying to run the scripts. 
		 
	 



	This module is split into the following configuration parts:
 


	
		Configuring the three previously created Virtual Machines on Nutanix AHV:

		
			
				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
			
		
	



	 
 


	
		Note: 
	 

	
		This module is complex as many automated actions and different entities are created and run on the VMs. 
		It contains a mixture of PowerShell scripts, REST-API calls, and Terraform scripts to achieve a working deployment. 
		 
	 



	
		Important: 
		 
		Please ensure you have configured the variables according to your needs using the corresponding .auto.tfvars.json and terraform.tfvars file. 
		 
	 



	Terraform runs various scripts before creating the CCs' 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:
 


	Examples of necessary scripts: 
	At first, Terraform writes the configuration file without the Resource Location ID:
 


	
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}	
	
		
			
				
					
						
							### Create CWC-Installer configuration file based on variables and save it into the 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"
						 
					
				
			
		
	



	 
	Afterwards, the Resource Location is created:
 


	
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}	
	
		
			
				
					
						
							## 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  = &lt;&lt;-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"
						 

						
							}
						 
					
				
			
		
	



	 
 


	
		Important:
	 

	
		The Terraform script will pause for some time after creating the Resource Location. 
		This is because of time constraints in the back-end. 
	 

	
		Creating the Zone related to the Resource Location takes some time. We have seen delays of up to 8 minutes before the Zone was created. 
		If the script proceeds too fast, the Zone ID readout will fail, and the Terraform script will not be able to continue successfully. 
		 
	 



	 
	After installing further prerequisites, Terraform runs more PowerShell scripts to get all the needed IDs and updates the configuration file for the CWC Installer. 
	The CWC Installer then installs the Cloud Connector software and registers the CCs to Citrix Cloud.
 


	
		Important:
	 

	
		Before starting the Terraform workflow, please ensure 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 td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						 
						PS C:\_tacg\_CCRLOnNutanix\CCRLOnNutanix - Installation&gt; 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 latest version of citrix/citrixadc... 
						- Finding nutanix/nutanix versions matching "1.9.5"... 
						- Finding mastercard/restapi versions matching "1.18.2"... 
						- Finding citrix/citrix versions matching "0.6.1"... 
						- Finding latest version of hashicorp/local... 
						- Installing hashicorp/null v3.2.2... 
						- Installed hashicorp/null v3.2.2 (signed by HashiCorp) 
						- Installing citrix/citrixadc v1.39.0... 
						- Installed citrix/citrixadc v1.39.0 (signed by a HashiCorp partner, key ID 25D62DD8407EA386) 
						- Installing nutanix/nutanix v1.9.5... 
						- Installed nutanix/nutanix v1.9.5 (signed by a HashiCorp partner, key ID BEA5F795571AD06E) 
						- Installing mastercard/restapi v1.18.2... 
						- Installed mastercard/restapi v1.18.2 (self-signed, key ID DCB8C431D71C30AB) 
						- Installing citrix/citrix v0.6.1... 
						- Installed citrix/citrix v0.6.1 (self-signed, key ID BD4BD0E690CB7D88) 
						- Installing hashicorp/local v2.5.1... 
						- Installed hashicorp/local v2.5.1 (signed by HashiCorp) 
						- Installing hashicorp/time v0.11.2... 
						- Installed hashicorp/time v0.11.2 (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\_CCRLOnNutanix\CCRLOnNutanix - Installation&gt; terraform validate 
						Success! The configuration is valid. 
						 
						PS C:\_tacg\_CCRLOnNutanix\CCRLOnNutanix - Installation&gt; terraform plan 
						 
						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 = "3600s" 
						      + id              = (known after apply) 
						    } 
						 
						  # time_sleep.wait_1800_seconds_CC2 will be created 
						  + resource "time_sleep" "wait_1800_seconds_CC2" { 
						      + create_duration = "3600s" 
						      + 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: 28 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\_CCRLOnNutanix\CCRLOnNutanix - Installation&gt; terraform apply 
						 
						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_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: 28 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.RestartCC: Creating... 
						local_file.InstallPoSHSDKOnAVM: Creating... 
						local_file.Log: Creating... 
						local_file.CWC-Configuration: Creating... 
						local_file.GetBearerToken: Creation complete after 0s [id=0c179d18ae6c2518d31d13e395b22c0a7e1273b5] 
						local_file.RestartCC: Creation complete after 0s [id=606daeae95b6e85b5e1c2a68bae8841fe8d9cbc4] 
						local_file.InstallPoSHSDKOnAVM: Creation complete after 0s [id=45b8a434e6ee1d259932d6a358c3a46a9d1bf536] 
						local_file.Log: Creation complete after 0s [id=d725ce92ca8335439a5d83acf47f3ca2c957a515] 
						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] 
						null_resource.ExecuteInstallPoSHSDKOnAVM: Still creating... [30s elapsed] 
						null_resource.ExecuteInstallPoSHSDKOnAVM: Still creating... [40s elapsed] 
						null_resource.ExecuteInstallPoSHSDKOnAVM: Still creating... [50s elapsed] 
						null_resource.ExecuteInstallPoSHSDKOnAVM: Still creating... [1m0s elapsed] 
						null_resource.ExecuteInstallPoSHSDKOnAVM: Creation complete after 1m7s [id=6292074328874391378] 
						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 2s [id=6795fceb-7f2b-a5c8-d570-b4e4581d2921] 
						local_file.CreateRLScript: Creating... 
						local_file.CreateRLScript: Creation complete after 0s [id=29d69757469a674a62dcb2c430b05e55131fb471] 
						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=a94f4f03-6ee8-a9df-2662-b9ec16266acc] 
						time_sleep.wait_60_seconds: Creating... 
						time_sleep.wait_900_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_60_seconds: Still creating... [30s elapsed] 
						time_sleep.wait_900_seconds: Still creating... [30s elapsed] 
						time_sleep.wait_60_seconds: Still creating... [40s elapsed] 
						time_sleep.wait_900_seconds: Still creating... [40s elapsed] 
						time_sleep.wait_900_seconds: Still creating... [50s elapsed] 
						time_sleep.wait_60_seconds: Still creating... [50s elapsed] 
						time_sleep.wait_60_seconds: Creation complete after 1m0s [id=2024-07-09T19:43:32Z] 
						local_file.GetSiteIDScript: Creating... 
						time_sleep.wait_900_seconds: Still creating... [1m0s elapsed] 
						local_file.GetSiteIDScript: Creation complete after 0s [id=b6be87d7eeed66b79fa8b73087b022ec61a02963] 
						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=5ac43d31-d466-41ff-c979-8d547f8d0629] 
						time_sleep.wait_900_seconds: Still creating... [1m10s elapsed] 
						time_sleep.wait_900_seconds: Still creating... [1m20s elapsed] 
						time_sleep.wait_900_seconds: Still creating... [1m30s elapsed] 
						... 
						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-07-09T19:57:32Z] 
						local_file.GetZoneIDScript: Creating... 
						local_file.GetZoneIDScript: Creation complete after 0s [id=1a4407bf1ea5f010d6be85c5f1b52cb1ed0b042b] 
						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=09def960-4178-9939-43a0-35807d9d6d0b] 
						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 0s [id=6015a043-fd5f-d90d-931c-bcc5b1749d31] 
						local_file.CreateValidCWCOnAVM: Creating... 
						local_file.CreateValidCWCOnAVM: Creation complete after 0s [id=5c9a6ed3780018684cf85d1c1eabc32eef74eac2] 
						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=c31bba42-7c7f-63f9-715e-06439ebc7f58] 
						null_resource.UploadRequiredComponentsToCC1: Creating... 
						local_file.InstallCWCOnCC: Creating... 
						null_resource.UploadRequiredComponentsToCC2: Creating... 
						null_resource.UploadRequiredComponentsToCC2: Provisioning with 'file'... 
						local_file.InstallCWCOnCC: Creation complete after 0s [id=68e279f3dbd788796c1b99dc1d3b8d5fb02b68fd] 
						null_resource.UploadRequiredComponentsToCC1: Provisioning with 'file'... 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadRequiredComponentsToCC1: Provisioning with 'file'... 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadRequiredComponentsToCC2: Provisioning with 'file'... 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadRequiredComponentsToCC2: Provisioning with 'file'... 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadRequiredComponentsToCC1: Provisioning with 'file'... 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadRequiredComponentsToCC2: Provisioning with 'file'... 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadRequiredComponentsToCC1: Provisioning with 'file'... 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadRequiredComponentsToCC2: Provisioning with 'file'... 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadRequiredComponentsToCC1: Provisioning with 'file'... 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadRequiredComponentsToCC2: Provisioning with 'file'... 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadRequiredComponentsToCC1: Provisioning with 'file'... 
						null_resource.UploadRequiredComponentsToCC2: Still creating... [10s elapsed] 
						null_resource.UploadRequiredComponentsToCC1: Still creating... [10s elapsed] 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadRequiredComponentsToCC2: Creation complete after 12s [id=3499680909836385760] 
						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.10.174.2 
						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! 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadRequiredComponentsToCC1: Creation complete after 12s [id=5955026058901817371] 
						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.10.174.1 
						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! 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
						null_resource.CallRequiredScriptsOnCC2 (remote-exec): C:\Users\Administrator&gt;powershell -File c:/temp/xdinst/DATA/InstallCWCOnCC.ps1 
						 
						null_resource.CallRequiredScriptsOnCC1 (remote-exec): C:\Users\Administrator&gt;powershell -File c:/temp/xdinst/DATA/InstallCWCOnCC.ps1 
						null_resource.CallRequiredScriptsOnCC2: Still creating... [10s elapsed] 
						null_resource.CallRequiredScriptsOnCC1: Still creating... [10s elapsed] 
						null_resource.CallRequiredScriptsOnCC2: Still creating... [20s elapsed] 
						null_resource.CallRequiredScriptsOnCC1: Still creating... [20s elapsed] 
						null_resource.CallRequiredScriptsOnCC2: Still creating... [30s elapsed] 
						null_resource.CallRequiredScriptsOnCC1: Still creating... [30s elapsed] 
						null_resource.CallRequiredScriptsOnCC2: Still creating... [40s elapsed] 
						null_resource.CallRequiredScriptsOnCC1: Still creating... [40s elapsed] 
						null_resource.CallRequiredScriptsOnCC2: Still creating... [50s elapsed] 
						null_resource.CallRequiredScriptsOnCC1: Still creating... [50s elapsed] 
						null_resource.CallRequiredScriptsOnCC2: Still creating... [1m0s elapsed] 
						null_resource.CallRequiredScriptsOnCC1: Still creating... [1m0s elapsed] 
						null_resource.CallRequiredScriptsOnCC2: Still creating... [1m10s elapsed] 
						null_resource.CallRequiredScriptsOnCC1: Still creating... [1m10s elapsed] 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.CallRequiredScriptsOnCC2: Creation complete after 1m17s [id=313788685341419830] 
						time_sleep.wait_1800_seconds_CC2: Creating... 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.CallRequiredScriptsOnCC1: Creation complete after 1m19s [id=7639137808060603745] 
						time_sleep.wait_1800_seconds_CC1: Creating... 
						time_sleep.wait_1800_seconds_CC2: Still creating... [10s elapsed] 
						time_sleep.wait_1800_seconds_CC1: Still creating... [10s elapsed] 
						time_sleep.wait_1800_seconds_CC2: Still creating... [20s elapsed] 
						time_sleep.wait_1800_seconds_CC1: Still creating... [20s elapsed] 
						time_sleep.wait_1800_seconds_CC2: Still creating... [30s elapsed]
					 

					
						...
					 

					
						time_sleep.wait_1800_seconds_CC2: Still creating... [59m31s elapsed] 
						time_sleep.wait_1800_seconds_CC1: Still creating... [59m32s elapsed] 
						time_sleep.wait_1800_seconds_CC2: Still creating... [59m41s elapsed] 
						time_sleep.wait_1800_seconds_CC1: Still creating... [59m42s elapsed] 
						time_sleep.wait_1800_seconds_CC2: Still creating... [59m51s elapsed] 
						time_sleep.wait_1800_seconds_CC1: Still creating... [59m52s elapsed] 
						time_sleep.wait_1800_seconds_CC2: Creation complete after 1h0m0s [id=2024-07-09T20:59:02Z] 
						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.10.174.2 
						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.CallRebootScriptOnCC2 (remote-exec): Connected! 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;time_sleep.wait_1800_seconds_CC1: Creation complete after 1h0m0s [id=2024-07-09T20:59:05Z] 
						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.10.174.1 
						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 
						null_resource.CallRebootScriptOnCC1 (remote-exec): Connected! 
						 
						null_resource.CallRebootScriptOnCC2 (remote-exec): C:\Users\Administrator&gt;powershell -File c:/temp/xdinst/DATA/RestartCC.ps1 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
						null_resource.CallRebootScriptOnCC1 (remote-exec): C:\Users\Administrator&gt;powershell -File c:/temp/xdinst/DATA/RestartCC.ps1 
						null_resource.CallRebootScriptOnCC2: Creation complete after 6s [id=3196161617334800677] 
						null_resource.CallRebootScriptOnCC1: Creation complete after 6s [id=5086230096573140711] 
						 
						Apply complete! Resources: 28 added, 0 changed, 0 destroyed. 
						PS C:\_tacg\_CCRLOnNutanix\CCRLOnNutanix - Installation&gt;
					 
				
			
		
	



	 
	Before running Terraform, we cannot see the Resource Location: 
	
 


	 
	No CC Software is installed on the Cloud Connector VMs before running Module 2: 
	
 


	 
	After installing the Cloud Connector software, the Cloud Connectivity Test runs successfully: 
	
 


	 
	The Resource Location is now available: 
	
 


	 
	The Cloud Connectors successfully communicate with Citrix Cloud Infrastructure: 
	
 


	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
	



	The environment is now ready to deploy the Nutanix AHV-Plug-In for Citrix on the Cloud Connector VMs.
 


	 
 


	Module 3: Deploy the Nutanix AHV-Plug-In for Citrix on the Cloud Connector VMs



	The Nutanix AHV Plug-In for Citrix allows Cloud Connectors to manage workloads running on AHV. 
	We need to install the Nutanix AHV Plug-In for Citrix on all Cloud Connectors in all Resource Locations in the instance where AHV is deployed.
 


	 
 


	As we want an uninterrupted flow, we must install the Plug-In silently. 
	Therefore, Terraform downloads a PowerShell Script from an Azure-based BLOB-Storage to the Cloud Connectors and executes the script using WinRM. 
 


	The command line is quite lengthy – please be sure to use the correct syntax:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					cmd /c 'msiexec /i "c:\temp\xdinst\DATA\NutanixAHV_Citrix_plugin.msi" /qn /l*v "c:\temp\xdinst\DATA\NutanixAHVPlugin.log" ALLUSERS="1" IAGREE="Yes" PLUGININSTALLPATH="C:\Program Files\Common Files\Citrix\HCLPlugins\CitrixMachineCreation\v1.0.0.0\" INSTALLFOLDER="C:\Program Files\Common Files\Citrix\HCLPlugins\CitrixMachineCreation\v1.0.0.0\NutanixAHV\" PVSINSTALLFOLDER="C:\Program Files\Common Files\Citrix\HCLPlugins\CitrixMachineCreation\v1.0.0.0\NutanixAHV\" REGISTERPLUGINSTOREPATH="C:\Program Files\Common Files\Citrix\HCLPlugins\CitrixMachineCreation\v1.0.0.0\" ADDLOCAL="F7_9_CWA_INSTALLFOLDER" REMOVE="PVS_F7_14_INSTALLFOLDER,F7_9_INSTALLFOLDER" INSTALLERTYPE="cwa"'
				
			
		
	



	 
 


	
		Important:
	 

	
		Before starting the Terraform workflow, please ensure you have configured the variables according to your needs. 
		 
	 



	The configuration can be started by following the standard Terraform workflow:
 


	
		terraform init,
	 

	
		terraform plan 
		 
	 



	and if no errors occur
 


	
		terraform apply
	 



	 
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\_TACG\_CCRLOnNutanix\CCRLOnNutanix - NTNXPluginInstallation&gt; terraform init 
						Initializing the backend... 
						Initializing provider plugins... 
						- Finding latest version of citrix/citrixadc... 
						- Finding latest version of hashicorp/local... 
						- Finding latest version of hashicorp/null... 
						- Finding nutanix/nutanix versions matching "1.9.5"... 
						- Finding mastercard/restapi versions matching "1.18.2"... 
						- Finding citrix/citrix versions matching "0.6.1"... 
						- Installing hashicorp/null v3.2.2... 
						- Installed hashicorp/null v3.2.2 (signed by HashiCorp) 
						- Installing nutanix/nutanix v1.9.5... 
						- Installed nutanix/nutanix v1.9.5 (signed by a HashiCorp partner, key ID BEA5F795571AD06E) 
						- Installing mastercard/restapi v1.18.2... 
						- Installed mastercard/restapi v1.18.2 (self-signed, key ID DCB8C431D71C30AB) 
						- Installing citrix/citrix v0.6.1... 
						- Installed citrix/citrix v0.6.1 (self-signed, key ID BD4BD0E690CB7D88) 
						- Installing citrix/citrixadc v1.39.0... 
						- Installed citrix/citrixadc v1.39.0 (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\_CCRLOnNutanix\CCRLOnNutanix - NTNXPluginInstallation&gt; terraform plan 
						 
						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.DownloadNTNXPluginInstallationScriptToAVM will be created 
						  + resource "local_file" "DownloadNTNXPluginInstallationScriptToAVM" { 
						      + 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/InstallNutanixAHVPlugInOnCC.ps1" 
						      + id                   = (known after apply) 
						    } 
						 
						  # local_file.DownloadNTNXPluginToAVM will be created 
						  + resource "local_file" "DownloadNTNXPluginToAVM" { 
						      + 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/DownloadNTNXPluginToAVM.ps1" 
						      + 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.ExecuteDownloadNTNXPluginInstallationScriptToAVM will be created 
						  + resource "null_resource" "ExecuteDownloadNTNXPluginInstallationScriptToAVM" { 
						      + id = (known after apply) 
						    } 
						 
						  # null_resource.ExecuteDownloadNutanixAHVPlugInOnAVM will be created 
						  + resource "null_resource" "ExecuteDownloadNutanixAHVPlugInOnAVM" { 
						      + id = (known after apply) 
						    } 
						 
						  # null_resource.UploadNutanixAHVToCC1 will be created 
						  + resource "null_resource" "UploadNutanixAHVToCC1" { 
						      + id = (known after apply) 
						    } 
						 
						  # null_resource.UploadNutanixAHVToCC2 will be created 
						  + resource "null_resource" "UploadNutanixAHVToCC2" { 
						      + 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\_CCRLOnNutanix\CCRLOnNutanix - NTNXPluginInstallation&gt; terraform apply 
						 
						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.DownloadNTNXPluginInstallationScriptToAVM will be created 
						  + resource "local_file" "DownloadNTNXPluginInstallationScriptToAVM" { 
						      + 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/InstallNutanixAHVPlugInOnCC.ps1" 
						      + id                   = (known after apply) 
						    } 
						 
						  # local_file.DownloadNTNXPluginToAVM will be created 
						  + resource "local_file" "DownloadNTNXPluginToAVM" { 
						      + 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/DownloadNTNXPluginToAVM.ps1" 
						      + 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.ExecuteDownloadNTNXPluginInstallationScriptToAVM will be created 
						  + resource "null_resource" "ExecuteDownloadNTNXPluginInstallationScriptToAVM" { 
						      + id = (known after apply) 
						    } 
						 
						  # null_resource.ExecuteDownloadNutanixAHVPlugInOnAVM will be created 
						  + resource "null_resource" "ExecuteDownloadNutanixAHVPlugInOnAVM" { 
						      + id = (known after apply) 
						    } 
						 
						  # null_resource.UploadNutanixAHVToCC1 will be created 
						  + resource "null_resource" "UploadNutanixAHVToCC1" { 
						      + id = (known after apply) 
						    } 
						 
						  # null_resource.UploadNutanixAHVToCC2 will be created 
						  + resource "null_resource" "UploadNutanixAHVToCC2" { 
						      + 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 
						 
						local_file.DownloadNTNXPluginInstallationScriptToAVM: Creating... 
						local_file.DownloadNTNXPluginToAVM: Creating... 
						local_file.DownloadNTNXPluginInstallationScriptToAVM: Creation complete after 0s [id=58441448bd04fa4a85628abf8a218891bcd24463] 
						local_file.DownloadNTNXPluginToAVM: Creation complete after 0s [id=d66e63f653326e57fc72b32aef152762f0b8f1a1] 
						null_resource.ExecuteDownloadNTNXPluginInstallationScriptToAVM: Creating... 
						null_resource.ExecuteDownloadNutanixAHVPlugInOnAVM: Creating... 
						null_resource.ExecuteDownloadNutanixAHVPlugInOnAVM: Provisioning with 'local-exec'... 
						null_resource.ExecuteDownloadNTNXPluginInstallationScriptToAVM: Provisioning with 'local-exec'... 
						null_resource.ExecuteDownloadNutanixAHVPlugInOnAVM (local-exec): Executing: ["PowerShell" "-Command" " c:/temp/xdinst/DATA/DownloadNTNXPluginToAVM.ps1"] 
						null_resource.ExecuteDownloadNTNXPluginInstallationScriptToAVM (local-exec): Executing: ["PowerShell" "-Command" " c:/temp/xdinst/DATA/InstallNutanixAHVPlugInOnCC.ps1"] 
						null_resource.ExecuteDownloadNutanixAHVPlugInOnAVM: Still creating... [10s elapsed] 
						null_resource.ExecuteDownloadNTNXPluginInstallationScriptToAVM: Still creating... [10s elapsed] 
						null_resource.ExecuteDownloadNTNXPluginInstallationScriptToAVM: Still creating... [20s elapsed] 
						null_resource.ExecuteDownloadNutanixAHVPlugInOnAVM: Still creating... [20s elapsed] 
						null_resource.ExecuteDownloadNTNXPluginInstallationScriptToAVM: Still creating... [30s elapsed] 
						null_resource.ExecuteDownloadNutanixAHVPlugInOnAVM: Still creating... [30s elapsed] 
						null_resource.ExecuteDownloadNutanixAHVPlugInOnAVM: Still creating... [40s elapsed] 
						null_resource.ExecuteDownloadNTNXPluginInstallationScriptToAVM: Still creating... [40s elapsed] 
						null_resource.ExecuteDownloadNutanixAHVPlugInOnAVM: Still creating... [50s elapsed] 
						null_resource.ExecuteDownloadNTNXPluginInstallationScriptToAVM: Still creating... [50s elapsed] 
						null_resource.ExecuteDownloadNutanixAHVPlugInOnAVM: Still creating... [1m0s elapsed] 
						null_resource.ExecuteDownloadNTNXPluginInstallationScriptToAVM: Still creating... [1m0s elapsed] 
						null_resource.ExecuteDownloadNutanixAHVPlugInOnAVM: Creation complete after 1m0s [id=3030678197037961348] 
						null_resource.UploadNutanixAHVToCC1: Creating... 
						null_resource.UploadNutanixAHVToCC2: Creating... 
						null_resource.UploadNutanixAHVToCC1: Provisioning with 'file'... 
						null_resource.UploadNutanixAHVToCC2: Provisioning with 'file'... 
						null_resource.ExecuteDownloadNTNXPluginInstallationScriptToAVM: Creation complete after 1m0s [id=1833985232879280472] 
						null_resource.UploadNutanixAHVToCC2: Still creating... [10s elapsed] 
						null_resource.UploadNutanixAHVToCC1: Still creating... [10s elapsed] 
						null_resource.UploadNutanixAHVToCC1: Still creating... [20s elapsed] 
						null_resource.UploadNutanixAHVToCC2: Still creating... [20s elapsed] 
						null_resource.UploadNutanixAHVToCC2: Still creating... [30s elapsed] 
						null_resource.UploadNutanixAHVToCC1: Still creating... [30s elapsed] 
						null_resource.UploadNutanixAHVToCC1: Still creating... [40s elapsed] 
						null_resource.UploadNutanixAHVToCC2: Still creating... [40s elapsed] 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadNutanixAHVToCC2: Provisioning with 'file'... 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadNutanixAHVToCC1: Provisioning with 'file'... 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadNutanixAHVToCC2: Creation complete after 47s [id=8256669184849626355] 
						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.10.174.2 
						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! 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadNutanixAHVToCC1: Creation complete after 47s [id=5135813728942165357] 
						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.10.174.1 
						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! 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
						null_resource.CallRequiredScriptsOnCC2 (remote-exec): C:\Users\Administrator&gt;powershell -File c:/temp/xdinst/DATA/InstallNutanixAHVPlugInOnCC.ps1 
						 
						null_resource.CallRequiredScriptsOnCC1 (remote-exec): C:\Users\Administrator&gt;powershell -File c:/temp/xdinst/DATA/InstallNutanixAHVPlugInOnCC.ps1 
						null_resource.CallRequiredScriptsOnCC2: Still creating... [10s elapsed] 
						null_resource.CallRequiredScriptsOnCC1: Still creating... [10s elapsed] 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.CallRequiredScriptsOnCC2: Creation complete after 14s [id=8129597400070755177] 
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.CallRequiredScriptsOnCC1: Creation complete after 14s [id=3838749021188728013] 
						 
						Apply complete! Resources: 8 added, 0 changed, 0 destroyed. 
						PS C:\_TACG\_CCRLOnNutanix\CCRLOnNutanix - NTNXPluginInstallation&gt;
					 
				
			
		
	



	 
	Let´s check if the configuration is also available for the corresponding zone:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\Users\administrator.TACG&gt; Get-HypHypervisorPlugin -ZoneUid cb0c649a-4b6f-4a35-b911-6e3cc60ceda3 | FT 
						 
						Available CitrixVerified ConnectionType DisplayName                                      PluginFactoryName           UnavailableReason UsesCloudInfrastructure 
						--------- -------------- -------------- -----------                                      -----------------           ----------------- ----------------------- 
						     True                        Custom Microsoft® Azure™                                AzureRmFactory                                                  False 
						     True                        Custom Google Cloud Platform                            GcpPluginFactory                                                False 
						     True                        Custom HPE Moonshot                                     HPMoonshotFactory                                               False 
						     True                        Custom Windows 365 Cloud PC                             W365CloudPCFactory                                              False 
						     True                           AWS Amazon EC2                                       AWSMachineManagerFactory                                         True 
						     True                         SCVMM Microsoft® System Center Virtual Machine Manager MicrosoftPSFactory                                              False 
						     True                       VCenter VMware vSphere®                                  VmwareFactory                                                   False 
						     True                     XenServer XenServer™                                       XenFactory                                                      False 
						     True                        Custom Nutanix AHV                                      AcropolisFactory                                                False 
						     True                        Custom Remote-PC Wake-On-LAN                            VdaWOLMachineManagerFactory                                     False 
						 
					 
				
			
		
	



	 
	Before running Module 3, no Plug-in is installed on the Cloud Connector VMs: 
	
 


	 
	After running Module 3, the Plug-In is installed: 
	
 


	 
 


	
		Caution:
	 

	
		Please make sure that the installation script has set up the Plug-in in the correct mode – it must be 
	 

	
		You can check it by using regedit to look into the corresponding Registry key:
	 

	
		Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Nutanix Inc.\Citrix MCS Plugin
	 

	
		"InstallerType"="cwa" 
		 
	 



	 
	
 


	 
 


	That completes the configuration of the needed Nutanix AHV Plug-In.
 


	The environment is now ready to deploy the Citrix Cloud entities.
 


	 
 


	Module 4: Deploy a Hosting Connection and a Hosting Connection Pool on Nutanix AHV and Citrix Cloud



	This module is split into the following configuration parts:
 


	
		Creating a Hosting Connection and a Hosting Connection Pool
	
	
		Creating a Machine Catalog
	
	
		Creating a Delivery Group with AutoScale configured and enabled
	



	The Terraform configuration contains some idle time slots to ensure that background operations can be completed before the following configuration steps occur. 
	We have seen different elapsed configuration times related to varying loads on Nutanix AHV.
 


	Before Terraform can create the Hosting Connection and the Hosting Connection Pool, It must retrieve the Site ID and Zone ID of the newly created Resource Location. 
	 
 


	
		Important: 
	 

	
		The Citrix Remote PowerShell SDK must be installed on the Administrative Machine, as Terraform relies on the SDK. 
		 
	 



	After retrieving the IDs, Terraform configures a Hosting Connection and a Hosting Connection Pool associated with the Hypervisor Connection.  
	Before running Terraform, no Terraform-related entities are available. 
	 
 


	
		Important:
	 

	
		Before starting the Terraform workflow, please ensure you have configured the variables according to your needs. 
		 
	 



	The configuration can be started by following the standard Terraform workflow:
 


	
		terraform init,
	 

	
		terraform plan 
		 
	 



	and if no errors occur
 


	
		terraform apply
	 



	 
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\_TACG\_CCRLOnNutanix\CCRLOnNutanix - CCStuff&gt; terraform init 
						Initializing the backend... 
						Initializing provider plugins... 
						- Finding latest version of hashicorp/local... 
						- Finding nutanix/nutanix versions matching "1.9.5"... 
						- Finding mastercard/restapi versions matching "1.18.2"... 
						- Finding citrix/citrix versions matching "1.0.0"... 
						- Finding latest version of hashicorp/time... 
						- Installing hashicorp/local v2.5.1... 
						- Installed hashicorp/local v2.5.1 (signed by HashiCorp) 
						- Installing nutanix/nutanix v1.9.5... 
						- Installed nutanix/nutanix v1.9.5 (signed by a HashiCorp partner, key ID BEA5F795571AD06E) 
						- Installing mastercard/restapi v1.18.2... 
						- Installed mastercard/restapi v1.18.2 (self-signed, key ID DCB8C431D71C30AB) 
						- Installing citrix/citrix v1.0.0... 
						- Installed citrix/citrix v1.0.0 (self-signed, key ID BD4BD0E690CB7D88) 
						- Installing hashicorp/time v0.12.0... 
						- Installed hashicorp/time v0.12.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\_CCRLOnNutanix\CCRLOnNutanix - CCStuff&gt; terraform plan 
						data.local_file.LoadZoneID: Reading... 
						data.local_file.LoadZoneID: Read complete after 0s [id=f62a4c48bb4082040a1f452767973c2bf610737c] 
						 
						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_machine_catalog.CreateNTXMCSCatalog will be created 
						  + resource "citrix_machine_catalog" "CreateNTXMCSCatalog" { 
						      + allocation_type          = "Random" 
						      + description              = "Terraform-based Machine Catalog" 
						      + id                       = (known after apply) 
						      + minimum_functional_level = "L7_20" 
						      + name                     = "MC-TACG-TF-ONP-NUTANIX" 
						      + provisioning_scheme      = { 
						          + hypervisor                     = (known after apply) 
						          + hypervisor_resource_pool       = (known after apply) 
						          + identity_type                  = "ActiveDirectory" 
						          + machine_account_creation_rules = { 
						              + naming_scheme      = "TACG-TF-NTX-#" 
						              + naming_scheme_type = "Numeric" 
						            } 
						          + machine_domain_identity        = { 
						              + domain                   = "the-austrian-citrix-guy.at" 
						              + domain_ou                = "CN=Computers,DC=the-austrian-citrix-guy,DC=at" 
						              + service_account          = (sensitive value) 
						              + service_account_password = (sensitive value) 
						            } 
						          + number_of_total_machines       = 1 
						          + nutanix_machine_config         = { 
						              + container           = "default-container-36052048168163" 
						              + cores_per_cpu_count = 2 
						              + cpu_count           = 2 
						              + master_image        = "mi" 
						              + master_image_note   = "" 
						              + memory_mb           = 4096 
						            } 
						        } 
						      + provisioning_type        = "MCS" 
						      + scopes                   = [] 
						      + session_support          = "MultiSession" 
						      + zone                     = "cb0c649a-4b6f-4a35-b911-6e3cc60ceda3" 
						    } 
						 
						  # citrix_nutanix_hypervisor.CreateHypervisorConnection will be created 
						  + resource "citrix_nutanix_hypervisor" "CreateHypervisorConnection" { 
						      + addresses                                = [ 
						          + "nutanix.the-austrian-citrix-guy.at", 
						        ] 
						      + id                                       = (known after apply) 
						      + max_absolute_active_actions              = 100 
						      + max_absolute_new_actions_per_minute      = 10 
						      + max_power_actions_percentage_of_machines = 20 
						      + name                                     = "TACG-TF-ONP-Nutanix-HypConn" 
						      + password                                 = (sensitive value) 
						      + password_format                          = "PlainText" 
						      + scopes                                   = [] 
						      + username                                 = (sensitive value) 
						      + zone                                     = (sensitive value) 
						    } 
						 
						  # citrix_nutanix_hypervisor_resource_pool.CreateHypervisorPool will be created 
						  + resource "citrix_nutanix_hypervisor_resource_pool" "CreateHypervisorPool" { 
						      + hypervisor = (known after apply) 
						      + id         = (known after apply) 
						      + name       = "TACG-TF-ONP-Nutanix-HypConnPool" 
						      + networks   = [ 
						          + "NW-NTX-TACG", 
						        ] 
						    } 
						 
						  # time_sleep.wait_30_seconds will be created 
						  + resource "time_sleep" "wait_30_seconds" { 
						      + create_duration = "30s" 
						      + id              = (known after apply) 
						    } 
						 
						  # time_sleep.wait_30_seconds1 will be created 
						  + resource "time_sleep" "wait_30_seconds1" { 
						      + create_duration = "30s" 
						      + id              = (known after apply) 
						    } 
						 
						Plan: 5 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\_CCRLOnNutanix\CCRLOnNutanix - CCStuff&gt; terraform apply 
						data.local_file.LoadZoneID: Reading... 
						data.local_file.LoadZoneID: Read complete after 0s [id=f62a4c48bb4082040a1f452767973c2bf610737c] 
						 
						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_machine_catalog.CreateNTXMCSCatalog will be created 
						  + resource "citrix_machine_catalog" "CreateNTXMCSCatalog" { 
						      + allocation_type          = "Random" 
						      + description              = "Terraform-based Machine Catalog" 
						      + id                       = (known after apply) 
						      + minimum_functional_level = "L7_20" 
						      + name                     = "MC-TACG-TF-ONP-NUTANIX" 
						      + provisioning_scheme      = { 
						          + hypervisor                     = (known after apply) 
						          + hypervisor_resource_pool       = (known after apply) 
						          + identity_type                  = "ActiveDirectory" 
						          + machine_account_creation_rules = { 
						              + naming_scheme      = "TACG-TF-NTX-#" 
						              + naming_scheme_type = "Numeric" 
						            } 
						          + machine_domain_identity        = { 
						              + domain                   = "the-austrian-citrix-guy.at" 
						              + domain_ou                = "CN=Computers,DC=the-austrian-citrix-guy,DC=at" 
						              + service_account          = (sensitive value) 
						              + service_account_password = (sensitive value) 
						            } 
						          + number_of_total_machines       = 1 
						          + nutanix_machine_config         = { 
						              + container           = "default-container-36052048168163" 
						              + cores_per_cpu_count = 2 
						              + cpu_count           = 2 
						              + master_image        = "mi" 
						              + master_image_note   = "" 
						              + memory_mb           = 4096 
						            } 
						        } 
						      + provisioning_type        = "MCS" 
						      + scopes                   = [] 
						      + session_support          = "MultiSession" 
						      + zone                     = (sensitive value) 
						    } 
						 
						  # citrix_nutanix_hypervisor.CreateHypervisorConnection will be created 
						  + resource "citrix_nutanix_hypervisor" "CreateHypervisorConnection" { 
						      + addresses                                = [ 
						          + "nutanix.the-austrian-citrix-guy.at", 
						        ] 
						      + id                                       = (known after apply) 
						      + max_absolute_active_actions              = 100 
						      + max_absolute_new_actions_per_minute      = 10 
						      + max_power_actions_percentage_of_machines = 20 
						      + name                                     = "TACG-TF-ONP-Nutanix-HypConn" 
						      + password                                 = (sensitive value) 
						      + password_format                          = "PlainText" 
						      + scopes                                   = [] 
						      + username                                 = (sensitive value) 
						      + zone                                     = (sensitive value) 
						    } 
						 
						  # citrix_nutanix_hypervisor_resource_pool.CreateHypervisorPool will be created 
						  + resource "citrix_nutanix_hypervisor_resource_pool" "CreateHypervisorPool" { 
						      + hypervisor = (known after apply) 
						      + id         = (known after apply) 
						      + name       = "TACG-TF-ONP-Nutanix-HypConnPool" 
						      + networks   = [ 
						          + "NW-NTX-TACG", 
						        ] 
						    } 
						 
						  # time_sleep.wait_30_seconds will be created 
						  + resource "time_sleep" "wait_30_seconds" { 
						      + create_duration = "30s" 
						      + id              = (known after apply) 
						    } 
						 
						  # time_sleep.wait_30_seconds1 will be created 
						  + resource "time_sleep" "wait_30_seconds1" { 
						      + create_duration = "30s" 
						      + id              = (known after apply) 
						    }
					 

					
						 
						  # citrix_delivery_group.CreateDG will be created 
						  + resource "citrix_delivery_group" "CreateDG" { 
						      + associated_machine_catalogs = [ 
						          + { 
						              + machine_catalog = "a82644d9-d995-43e5-9225-8c12fd11aba8" 
						              + 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          = [ 
						                      + "Friday", 
						                      + "Monday", 
						                      + "Thursday", 
						                      + "Tuesday", 
						                      + "Wednesday", 
						                    ] 
						                  + display_name          = "TACG-TF-NTX-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 
						                }, 
						            ] 
						        } 
						      + description                 = "" 
						      + desktops                    = [ 
						          + { 
						              + description             = "Terraform-based Delivery Group running on Nutanix" 
						              + enable_session_roaming  = true 
						              + enabled                 = true 
						              + published_name          = "DG-TACG-TF-NTX" 
						              + restricted_access_users = { 
						                  + allow_list = [ 
						                      + "TACG\\vdaallowed", 
						                    ] 
						                } 
						            }, 
						        ] 
						      + id                          = (known after apply) 
						      + minimum_functional_level    = "L7_20" 
						      + name                        = "DG-TACG-TF-NTX" 
						      + reboot_schedules            = [ 
						          + { 
						              + days_in_week            = [ 
						                  + "Sunday", 
						                ] 
						              + frequency               = "Weekly" 
						              + frequency_factor        = 1 
						              + ignore_maintenance_mode = true 
						              + name                    = "TACG-NTX-Reboot Schedule" 
						              + natural_reboot_schedule = false 
						              + reboot_duration_minutes = 0 
						              + reboot_schedule_enabled = true 
						              + start_date              = "2024-01-01" 
						              + start_time              = "02:00" 
						                # (1 unchanged attribute hidden) 
						            }, 
						        ] 
						      + restricted_access_users     = { 
						          + allow_list = [ 
						              + "TACG\\vdaallowed", 
						            ] 
						        } 
						      + scopes                      = [] 
						      + total_machines              = (known after apply) 
						    } 
						 
						  # time_sleep.wait_30_seconds_2 will be created 
						  + resource "time_sleep" "wait_30_seconds_2" { 
						      + create_duration = "30s" 
						      + id              = (known after apply) 
						    } 
						 
						Plan: 7 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_nutanix_hypervisor.CreateHypervisorConnection: Creating... 
						citrix_nutanix_hypervisor.CreateHypervisorConnection: Still creating... [10s elapsed] 
						citrix_nutanix_hypervisor.CreateHypervisorConnection: Still creating... [20s elapsed] 
						citrix_nutanix_hypervisor.CreateHypervisorConnection: Still creating... [30s elapsed] 
						citrix_nutanix_hypervisor.CreateHypervisorConnection: Still creating... [40s elapsed] 
						citrix_nutanix_hypervisor.CreateHypervisorConnection: Still creating... [50s elapsed] 
						citrix_nutanix_hypervisor.CreateHypervisorConnection: Still creating... [1m0s elapsed] 
						citrix_nutanix_hypervisor.CreateHypervisorConnection: Still creating... [1m10s elapsed] 
						citrix_nutanix_hypervisor.CreateHypervisorConnection: Creation complete after 1m11s [id=c406ed17-d1b7-4051-8586-a75f37805b7b] 
						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-08-14T11:58:59Z] 
						citrix_nutanix_hypervisor_resource_pool.CreateHypervisorPool: Creating... 
						citrix_nutanix_hypervisor_resource_pool.CreateHypervisorPool: Still creating... [10s elapsed] 
						citrix_nutanix_hypervisor_resource_pool.CreateHypervisorPool: Creation complete after 12s [id=d9ac0507-6531-49c2-a0c2-f61b00b8c4c6] 
						time_sleep.wait_30_seconds1: Creating... 
						time_sleep.wait_30_seconds1: Still creating... [10s elapsed] 
						time_sleep.wait_30_seconds1: Still creating... [20s elapsed] 
						time_sleep.wait_30_seconds1: Still creating... [30s elapsed] 
						time_sleep.wait_30_seconds1: Creation complete after 30s [id=2024-08-14T11:59:41Z] 
						citrix_machine_catalog.CreateNTXMCSCatalog: Creating... 
						citrix_machine_catalog.CreateNTXMCSCatalog: Still creating... [10s elapsed] 
						citrix_machine_catalog.CreateNTXMCSCatalog: Still creating... [20s elapsed] 
						citrix_machine_catalog.CreateNTXMCSCatalog: Still creating... [30s elapsed] 
						citrix_machine_catalog.CreateNTXMCSCatalog: Still creating... [40s elapsed] 
						citrix_machine_catalog.CreateNTXMCSCatalog: Still creating... [50s elapsed] 
						citrix_machine_catalog.CreateNTXMCSCatalog: Still creating... [1m0s elapsed] 
						citrix_machine_catalog.CreateNTXMCSCatalog: Still creating... [1m10s elapsed] 
						citrix_machine_catalog.CreateNTXMCSCatalog: Still creating... [1m20s elapsed] 
						citrix_machine_catalog.CreateNTXMCSCatalog: Still creating... [1m30s elapsed] 
						citrix_machine_catalog.CreateNTXMCSCatalog: Still creating... [1m40s elapsed] 
						citrix_machine_catalog.CreateNTXMCSCatalog: Still creating... [1m50s elapsed] 
						citrix_machine_catalog.CreateNTXMCSCatalog: Still creating... [2m0s elapsed] 
						citrix_machine_catalog.CreateNTXMCSCatalog: Creation complete after 2m6s [id=a82644d9-d995-43e5-9225-8c12fd11aba8] 
						time_sleep.wait_30_seconds_2: Creating... 
						time_sleep.wait_30_seconds_2: Still creating... [10s elapsed] 
						time_sleep.wait_30_seconds_2: Still creating... [20s elapsed] 
						time_sleep.wait_30_seconds_2: Creation complete after 30s [id=2024-08-14T12:09:23Z] 
						citrix_delivery_group.CreateDG: Creating... 
						citrix_delivery_group.CreateDG: Still creating... [10s elapsed] 
						citrix_delivery_group.CreateDG: Creation complete after 11s [id=8fcffef2-51e1-4485-881c-375e5b088b35] 
						 
						Apply complete! Resources: 7 added, 0 changed, 0 destroyed. 
						PS C:\_TACG\_CCRLOnNutanix\CCRLOnNutanix - CCStuff&gt;
					 
				
			
		
	



	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	



	 
	Terraform successfully created the Hosting Connection and a Hosting Connection Pool associated with the Hosting Connection: 
	
 


	 
 


	
 


	 
	Terraform successfully created the Machine Catalog: 
	
 


	 
	Terraform successfully created the Delivery Group: 
	
 


	 
	Terraform successfully created the AutoScale settings: 
	
 


	 
	The Delivery Group is ready to start the Worker VMs: 
	
 


	That concludes our guide "Using Terraform to deploy a Citrix DaaS Resource Location on Nutanix AHV".
 


	 
 


	Appendix



	
		Disclaimer
	 

	
		EXCEPT WHERE EXPRESSLY PROVIDED OTHERWISE BY CLOUD SOFTWARE GROUP, THE CONTENT IN THIS DOCUMENT IS PROVIDED “AS IS” AND CLOUD SOFTWARE GROUP HEREBY DISCLAIMS ALL EXPRESS OR IMPLIED REPRESENTATIONS, WARRANTIES, GUARANTIES, AND CONDITIONS, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES OR CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD TO BE LEGALLY INVALID. CLOUD SOFTWARE GROUP MAKES NO REPRESENTATIONS, WARRANTIES, GUARANTIES, OR CONDITIONS AS TO THE QUALITY, SUITABILITY, TRUTH, ACCURACY, OR COMPLETENESS OF ANY OF THE CONTENT CONTAINED ON THE WEBSITE.
	 

	
		The above disclaimer applies to any damages, liability, or injuries caused by any failure of performance, error, omission, interruption, deletion, defect, delay in operation or transmission, computer virus, communication line failure, theft or destruction of or unauthorized access to, alteration of, or use, whether for breach of contract, tort, negligence or any other cause of action. 
		 
	 



	Examples of the Terraform scripts
 


	Module 1: CCRLOnNutanix-Creation



	These are the Terraform configuration files for Module 1 (excerpts):
 


	_CCRLOnNutanix-Provider.tf



	 
 


	
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}	
	
		
			
				
					
						
							
								# Terraform deployment of Citrix Virtual Apps and Desktops on Nutanix Prism
							

							
								## Definition of all required Terraform providers
							

							
								
									
										 
									

									
										terraform {
									

									
										    required_version = "&gt;= 1.9.0"
									
									 

									
										  required_providers {
									

									
										    nutanix = {
									

									
										      source  = "nutanix/nutanix"
									

									
										      version = "= 1.9.5"
									

									
										    }
									
									 

									
										    restapi = {
									

									
										      source = "Mastercard/restapi"
									

									
										      version = "1.18.2"
									

									
										    }
									
									 

									
										    citrix = {
									

									
										      source  = "citrix/citrix"
									

									
										      version = "=1.0.0"
									

									
										    }
									

									
										  }
									

									
										}
									
								
							

							
								
									
										provider "citrix" {
									

									
										  cvad_config = {
									

									
										  customer_id               = var.CC_CustomerID
									

									
										  client_id                 = var.CC_APIKey-ClientID
									

									
										  client_secret             = var.CC_APIKey-ClientSecret
									

									
										  }
									

									
										}
									
									 

									
										provider "nutanix" {
									

									
										  username                  = var.Nutanix_Provider-Username
									

									
										  password                  = var.Nutanix_Provider-Password
									

									
										  endpoint                  = var.Nutanix_Provider-Endpoint
									

									
										  port                      = var.Nutanix_Provider-Port
									

									
										  insecure                  = var.Nutanix_Provider-Insecure
									

									
										  wait_timeout              = var.Nutanix_Provider-Timeout
									
									 

									
										}
									
								
							
						
					
				
			
		
	



	 
	_CCRLOnNutanix-Create.tf



	
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}	
	
		
			
				
					
						
							
								# Terraform deployment of Citrix Virtual Apps and Desktops on Nutanix AHV
							

							
								## Definition of all required local variables
							
							 

							
								### Get Nutanix Prism Cluster-ID
							

							
								### We assume that only one cluster is configured
							

							
								data "nutanix_clusters" "NTX-Clusters" {
							

							
								}
							
							 

							
								data "nutanix_cluster" "NTX-Cluster" {
							

							
								   cluster_id = data.nutanix_clusters.NTX-Clusters.entities[0].metadata.uuid
							

							
								}
							
							 

							
								### Get Nutanix Prism Subnet-ID
							

							
								### We assume that only one subnet is configured
							

							
								data "nutanix_subnets" "NTX-Subnets" {
							

							
								}
							
							 

							
								### Set the local variables based on the Data sources
							

							
								locals {
							

							
								  NTX-Cluster-ID = data.nutanix_clusters.NTX-Clusters.entities[0].metadata.uuid
							

							
								  NTX-Subnet-ID = data.nutanix_subnets.NTX-Subnets.entities[1].metadata.uuid
							

							
								  NTX-Cluster-Name = data.nutanix_cluster.NTX-Cluster.name
							

							
								}
							
							 

							
								### Upload ISO-Images
							

							
								/* resource "nutanix_image" "CVAD2402LTSR" {
							

							
								  name        = "CVAD 2402 LTSR-ISO"
							

							
								  description = "CVAD 2402 LTSR-ISO"
							

							
								  source_path  = "${path.module}/DATA/Citrix_Virtual_Apps_and_Desktops_7_2402_LTSR.iso"
							

							
								}  */
							

							
								 
							

							
								### Load and configure AutoUnattend.xml file for customizing the Windows Server-based Image
							

							
								data "template_file" "AutoUnattendXMLCC1" {
							

							
								  template = file("${path.module}/DATA/AutoUnattend.xml")
							

							
								  vars = {
							

							
								    Unattended-VM_NAME              = "${var.CC1-VM-Name}"
							

							
								    Unattended-DomainName           = "${var.Unattended-DomainName}"
							

							
								    Unattended-DomainName-NB        = "${var.Unattended-DomainName-NB}"
							

							
								    Unattended-DomainOU             = "${var.Unattended-DomainOU}"
							

							
								    Unattended-OrgName              = "${var.Unattended-OrgName}"
							

							
								    Unattended-OwnerName            = "${var.Unattended-OwnerName}"
							

							
								    Unattended-DomainAdmin-PW       = textencodebase64(join("", ["${var.Unattended-DomainAdmin-PW}", "AdministratorPassword"]), "UTF-16LE")
							

							
								    Unattended-Admin-PW             = textencodebase64(join("", ["${var.Unattended-Admin-PW}", "AdministratorPassword"]), "UTF-16LE")
							

							
								    Unattended-Autologon-PW         = textencodebase64(join("", ["${var.Unattended-Admin-PW}", "Password"]), "UTF-16LE")
							

							
								    Unattended-DomainUser           = "${var.Unattended-DomainUser}"
							

							
								    Unattended-DomainUser-PW        = "${var.Unattended-DomainUser-PW}"
							

							
								    Unattended-Timezone             = "${var.Unattended-Timezone}"
							

							
								    Unattended-FRScript             = "${var.Unattended-FRScript}"
							

							
								    Unattended-ProductKey           = "${var.Unattended-ProductKey}"
							

							
								  }
							

							
								} 
							
							 

							
								data "template_file" "AutoUnattendXMLCC2" {
							

							
								  template = file("${path.module}/DATA/AutoUnattend.xml")
							

							
								  vars = {
							

							
								    Unattended-VM_NAME              = "${var.CC2-VM-Name}"
							

							
								    Unattended-DomainName           = "${var.Unattended-DomainName}"
							

							
								    Unattended-DomainName-NB        = "${var.Unattended-DomainName-NB}"
							

							
								    Unattended-DomainOU             = "${var.Unattended-DomainOU}"
							

							
								    Unattended-OrgName              = "${var.Unattended-OrgName}"
							

							
								    Unattended-OwnerName            = "${var.Unattended-OwnerName}"
							

							
								    Unattended-DomainAdmin-PW       = textencodebase64(join("", ["${var.Unattended-DomainAdmin-PW}", "AdministratorPassword"]), "UTF-16LE")
							

							
								    Unattended-Admin-PW             = textencodebase64(join("", ["${var.Unattended-Admin-PW}", "AdministratorPassword"]), "UTF-16LE")
							

							
								    Unattended-Autologon-PW         = textencodebase64(join("", ["${var.Unattended-Admin-PW}", "Password"]), "UTF-16LE")
							

							
								    Unattended-DomainUser           = "${var.Unattended-DomainUser}"
							

							
								    Unattended-DomainUser-PW        = "${var.Unattended-DomainUser-PW}"
							

							
								    Unattended-Timezone             = "${var.Unattended-Timezone}"
							

							
								    Unattended-FRScript             = "${var.Unattended-FRScript}"
							

							
								    Unattended-ProductKey           = "${var.Unattended-ProductKey}"
							

							
								  }
							

							
								} 
							
							 

							
								data "template_file" "AutoUnattendXMLAVM" {
							

							
								  template = file("${path.module}/DATA/AutoUnattend.xml")
							

							
								  vars = {
							

							
								    Unattended-VM_NAME              = "${var.AVM-VM-Name}"
							

							
								    Unattended-DomainName           = "${var.Unattended-DomainName}"
							

							
								    Unattended-DomainName-NB        = "${var.Unattended-DomainName-NB}"
							

							
								    Unattended-DomainOU             = "${var.Unattended-DomainOU}"
							

							
								    Unattended-OrgName              = "${var.Unattended-OrgName}"
							

							
								    Unattended-OwnerName            = "${var.Unattended-OwnerName}"
							

							
								    Unattended-DomainAdmin-PW       = textencodebase64(join("", ["${var.Unattended-DomainAdmin-PW}", "AdministratorPassword"]), "UTF-16LE")
							

							
								    Unattended-Admin-PW             = textencodebase64(join("", ["${var.Unattended-Admin-PW}", "AdministratorPassword"]), "UTF-16LE")
							

							
								    Unattended-Autologon-PW         = textencodebase64(join("", ["${var.Unattended-Admin-PW}", "Password"]), "UTF-16LE")
							

							
								    Unattended-DomainUser           = "${var.Unattended-DomainUser}"
							

							
								    Unattended-DomainUser-PW        = "${var.Unattended-DomainUser-PW}"
							

							
								    Unattended-Timezone             = "${var.Unattended-Timezone}"
							

							
								    Unattended-FRScript             = "${var.Unattended-FRScript}"
							

							
								    Unattended-ProductKey           = "${var.Unattended-ProductKey}"
							

							
								  }
							

							
								} 
							
							 

							
								### Create CC1-VM
							

							
								# Virtual machine resource
							

							
								resource "nutanix_virtual_machine" "CC1-VM" {
							

							
								  # General Information
							

							
								  name                 = "${var.CC1-VM-Name}"
							

							
								  description          = "${var.CC1-VM-Description}"
							

							
								  num_vcpus_per_socket = "${var.CC1-VM-CPU}"
							

							
								  num_sockets          = "${var.CC1-VM-Sockets}"
							

							
								  memory_size_mib      = "${var.CC1-VM-RAM}"
							

							
								  boot_type            = "LEGACY"
							

							
								  boot_device_order_list = ["DISK",]  
							

							
								  guest_customization_sysprep = {
							

							
								    install_type = "PREPARED"
							

							
								    unattend_xml = base64encode(data.template_file.AutoUnattendXMLCC1.rendered)
							

							
								  } 
							
							 

							
								  # VM Cluster
							

							
								  cluster_uuid = local.NTX-Cluster-ID
							
							 

							
								  # Networks
							

							
								  nic_list {
							

							
								    subnet_uuid = local.NTX-Subnet-ID
							

							
								    ip_endpoint_list {
							

							
								      ip =  "10.10.174.1"
							

							
								      type = "ASSIGNED"
							

							
								    } 
							

							
								  }
							
							 

							
								  # Disk/CD-Rom configuration
							

							
								  disk_list {
							

							
								    data_source_reference = {
							

							
								      kind = "image"
							

							
								      uuid = "${var.NTX-Image-UUId}"
							

							
								    }
							

							
								  }
							

							
								}
							
							 

							
								### Create CC2-VM
							

							
								# Virtual machine resource
							

							
								resource "nutanix_virtual_machine" "CC2-VM" {
							

							
								  # General Information
							

							
								  name                 = "${var.CC2-VM-Name}"
							

							
								  description          = "${var.CC2-VM-Description}"
							

							
								  num_vcpus_per_socket = "${var.CC2-VM-CPU}"
							

							
								  num_sockets          = "${var.CC2-VM-Sockets}"
							

							
								  memory_size_mib      = "${var.CC2-VM-RAM}"
							

							
								  boot_type            = "LEGACY"
							

							
								  boot_device_order_list = ["DISK",]  
							

							
								  guest_customization_sysprep = {
							

							
								    install_type = "PREPARED"
							

							
								    unattend_xml = base64encode(data.template_file.AutoUnattendXMLCC2.rendered)
							

							
								  } 
							
							 

							
								  # VM Cluster
							

							
								  cluster_uuid = local.NTX-Cluster-ID
							
							 

							
								  # Networks
							

							
								  nic_list {
							

							
								    subnet_uuid = local.NTX-Subnet-ID
							

							
								    ip_endpoint_list {
							

							
								      ip =  "10.10.174.2"
							

							
								      type = "ASSIGNED"
							

							
								    } 
							

							
								  }
							
							 

							
								  # Disk/CD-Rom configuration
							

							
								  disk_list {
							

							
								    data_source_reference = {
							

							
								      kind = "image"
							

							
								      uuid = "${var.NTX-Image-UUId}"
							

							
								    }
							

							
								  }
							

							
								}
							
							 

							
								### Create AVM-VM
							

							
								# Virtual machine resource
							

							
								resource "nutanix_virtual_machine" "AVM-VM" {
							

							
								  # General Information
							

							
								  name                 = "${var.AVM-VM-Name}"
							

							
								  description          = "${var.AVM-VM-Description}"
							

							
								  num_vcpus_per_socket = "${var.AVM-VM-CPU}"
							

							
								  num_sockets          = "${var.AVM-VM-Sockets}"
							

							
								  memory_size_mib      = "${var.AVM-VM-RAM}"
							

							
								  boot_type            = "LEGACY"
							

							
								  boot_device_order_list = ["DISK",]  
							

							
								  guest_customization_sysprep = {
							

							
								    install_type = "PREPARED"
							

							
								    unattend_xml = base64encode(data.template_file.AutoUnattendXMLAVM.rendered)
							

							
								  } 
							
							 

							
								  # VM Cluster
							

							
								  cluster_uuid = local.NTX-Cluster-ID
							
							 

							
								  # Networks 
							

							
								  nic_list {
							

							
								    subnet_uuid = local.NTX-Subnet-ID
							

							
								    ip_endpoint_list {
							

							
								      ip =  "10.10.174.3"
							

							
								      type = "ASSIGNED"
							

							
								    } 
							

							
								  }
							
							 

							
								  # Disk/CD-Rom configuration 
							

							
								  disk_list {
							

							
								    data_source_reference = {
							

							
								      kind = "image"
							

							
								      uuid = "${var.NTX-Image-UUId}"
							

							
								    }
							

							
								  }
							

							
								}
							
							 

							
								######################################################################################
							

							
								###  All VMs for creating a Resource Location on Nutanix should have been created  ###
							

							
								###################################################################################### 
							
						
					
				
			
		

		
			 
		 

		
			Module 2: CCRLOnNutanix-Install
		

		
			These are the Terraform configuration files for Module 2 (excerpts):
		 

		
			_CCRLOnNutanix-Install-CreatePreReqs.tf
		

		
			
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}			
			
				
					
						
							
								
									
										# Terraform deployment of a Citrix Cloud Resource Location on Nutanix
									

									
										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"
									

									
										} 
									
									 

									
										### Create PowerShell file for installing the Citrix Remote PoSH SDK on AVM 
									

									
										resource "local_file" "InstallPoSHSDKOnAVM" {
									

									
										content  = &lt;&lt;-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 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  = &lt;&lt;-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  = &lt;&lt;-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  = &lt;&lt;-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  = &lt;&lt;-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  = &lt;&lt;-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_corr.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 valid CWC.JSON file
									

									
										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  = &lt;&lt;-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_corr.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.InstallCWCOnCC ]
									

									
										  content  = &lt;&lt;-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 = [ terraform_data.ExecuteCreateValidCWCOnAVM ]
									

									
										 connection {
									

									
										    type            = var.Provisioner_Type
									

									
										    user            = var.Provisioner_Admin-Username
									

									
										    password        = var.Provisioner_Admin-Password
									

									
										    host            = var.Provisioner_CC1-IP
									

									
										    timeout         = var.Provisioner_Timeout
									
									 

									
										  }
									
									 

									
										###### Upload Cloud Connector configuration file to CC1
									

									
										  provisioner "file" {
									

									
										    source      = "${var.CC_Install_LogPath}/DATA/cwc_corr.json"
									

									
										    destination = "${var.CC_Install_LogPath}/DATA/cwc_corr.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            = var.Provisioner_CC1-IP
									

									
										    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 = [ terraform_data.ExecuteCreateValidCWCOnAVM ]
									

									
										 connection {
									

									
										    type            = var.Provisioner_Type
									

									
										    user            = var.Provisioner_Admin-Username
									

									
										    password        = var.Provisioner_Admin-Password
									

									
										    host            = var.Provisioner_CC2-IP
									

									
										    timeout         = var.Provisioner_Timeout
									
									 

									
										  }
									
									 

									
										###### Upload Cloud Connector configuration file to CC2
									

									
										  provisioner "file" {
									

									
										    source      = "${var.CC_Install_LogPath}/DATA/cwc_corr.json"
									

									
										    destination = "${var.CC_Install_LogPath}/DATA/cwc_corr.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            = var.Provisioner_CC2-IP
									

									
										    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            = var.Provisioner_CC1-IP
									

									
										    timeout         = var.Provisioner_Timeout
									
									 

									
										  }
									

									
										 
									

									
										  provisioner "remote-exec" {
									

									
										    inline = [
									

									
										      "powershell -File ${var.CC_Install_LogPath}/DATA/RestartCC.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            = var.Provisioner_CC2-IP
									

									
										    timeout         = var.Provisioner_Timeout
									
									 

									
										  }
									

									
										 
									

									
										  provisioner "remote-exec" {
									

									
										    inline = [
									

									
										      "powershell -File ${var.CC_Install_LogPath}/DATA/RestartCC.ps1"
									

									
										    ]
									

									
										  } 
									

									
										}  
									
								
							
						
					
				
			
		

		
			 
			Module 3: CCRLOnNutanix-Install-NTNXPluginInstallation
		

		
			These are the Terraform configuration files for Module 3 (excerpts):
		 

		
			_CCRLOnNutanix-Install-NTNXPluginInstallation.tf
		

		
			
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}			
			
				
					
						
							
								
									
										# Terraform deployment of a Citrix Cloud Resource Location on Nutanix
									

									
										locals {
									
									 

									
										}
									

									
										## Create all Pre-requisites
									

									
										### Create PowerShell file for downloading the Nutanix AHV-Plugin for Citrix on AVM 
									

									
										resource "local_file" "DownloadNTNXPluginToAVM" {
									

									
										content  = &lt;&lt;-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 "`nDownload of utanix AHV Plugin for Citrix started."
									

									
										# Download Nutanix AHV Plugin for Citrix
									

									
										Invoke-WebRequest '${var.CC_Install_AHVPluginURI}' -OutFile '${var.CC_Install_LogPath}/DATA/NutanixAHV_Citrix_Plugin.msi'
									

									
										Add-Content ${var.CC_Install_LogPath}/log.txt "`Nutanix AHV Plugin for Citrix downloaded."
									

									
										# 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/DownloadNTNXPluginToAVM.ps1"
									

									
										}
									
									 

									
										### Create PowerShell file for downloading the Nutanix AHV-Plugin installation script on AVM 
									

									
										resource "local_file" "DownloadNTNXPluginInstallationScriptToAVM" {
									

									
										content  = &lt;&lt;-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 "`nDownload of Nutanix AHV Plugin for Citrix started."
									

									
										# Download Nutanix AHV Plugin for Citrix
									

									
										Invoke-WebRequest '${var.CC_Install_AHVPluginInstallationScriptURI}' -OutFile '${var.CC_Install_LogPath}/DATA/InstallNutanixAHVPlugInOnCC.ps1'
									

									
										Add-Content ${var.CC_Install_LogPath}/log.txt "`Nutanix AHV Plugin installation script downloaded."
									

									
										# 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/InstallNutanixAHVPlugInOnCC.ps1"
									

									
										}
									
									 

									
										#### Execute Nutanix AHV-Plugin download script on AVM
									

									
										resource "null_resource" "ExecuteDownloadNutanixAHVPlugInOnAVM" {
									

									
										  depends_on = [ local_file.DownloadNTNXPluginToAVM ]
									

									
										  provisioner "local-exec" {
									

									
										    command = " ${var.CC_Install_LogPath}/DATA/DownloadNTNXPluginToAVM.ps1"
									

									
										    interpreter = ["PowerShell", "-Command"]
									

									
										  }
									

									
										}
									
									 

									
										#### Execute Nutanix AHV-Plugin installation script download script on AVM
									

									
										resource "null_resource" "ExecuteDownloadNTNXPluginInstallationScriptToAVM" {
									

									
										  depends_on = [ local_file.DownloadNTNXPluginInstallationScriptToAVM ]
									

									
										  provisioner "local-exec" {
									

									
										    command = " ${var.CC_Install_LogPath}/DATA/InstallNutanixAHVPlugInOnCC.ps1"
									

									
										    interpreter = ["PowerShell", "-Command"]
									

									
										  }
									

									
										}
									
									 

									
										### Install Nutanix AHV Plugin on CC1 
									

									
										#### Upload required components to CC1
									

									
										###### Set the Provisioner-Connection
									

									
										resource "null_resource" "UploadNutanixAHVToCC1" {
									

									
										 depends_on = [ null_resource.ExecuteDownloadNutanixAHVPlugInOnAVM ]
									

									
										 connection {
									

									
										    type            = var.Provisioner_Type
									

									
										    user            = var.Provisioner_Admin-Username
									

									
										    password        = var.Provisioner_Admin-Password
									

									
										    host            = var.Provisioner_CC1-IP
									

									
										  }
									
									 

									
										###### Upload Nutanix AHV-PlugIn file to CC1
									

									
										  provisioner "file" {
									

									
										    source      = "${var.CC_Install_LogPath}/DATA/NutanixAHV_Citrix_plugin.msi"
									

									
										    destination = "${var.CC_Install_LogPath}/DATA/NutanixAHV_Citrix_plugin.msi"
									

									
										    
									

									
										  } 
									
									 

									
										###### Upload Nutanix AHV-PlugIn installation script to CC1
									

									
										  provisioner "file" {
									

									
										    source      = "${var.CC_Install_LogPath}/DATA/InstallNutanixAHVPlugInOnCC.ps1"
									

									
										    destination = "${var.CC_Install_LogPath}/DATA/InstallNutanixAHVPlugInOnCC.ps1"
									

									
										    
									

									
										  } 
									

									
										}
									
									 

									
										### Install Nutanix AHV Plugin on CC2 
									

									
										#### Upload required components to CC2
									

									
										###### Set the Provisioner-Connection
									

									
										resource "null_resource" "UploadNutanixAHVToCC2" {
									

									
										 depends_on = [ null_resource.ExecuteDownloadNutanixAHVPlugInOnAVM ]
									

									
										 connection {
									

									
										    type            = var.Provisioner_Type
									

									
										    user            = var.Provisioner_Admin-Username
									

									
										    password        = var.Provisioner_Admin-Password
									

									
										    host            = var.Provisioner_CC2-IP
									

									
										  }
									
									 

									
										###### Upload Nutanix AHV-PlugIn file to CC2
									

									
										  provisioner "file" {
									

									
										    source      = "${var.CC_Install_LogPath}/DATA/NutanixAHV_Citrix_plugin.msi"
									

									
										    destination = "${var.CC_Install_LogPath}/DATA/NutanixAHV_Citrix_plugin.msi"
									

									
										    
									

									
										  } 
									
									 

									
										###### Upload Nutanix AHV-PlugIn installation script to CC2
									

									
										  provisioner "file" {
									

									
										    source      = "${var.CC_Install_LogPath}/DATA/InstallNutanixAHVPlugInOnCC.ps1"
									

									
										    destination = "${var.CC_Install_LogPath}/DATA/InstallNutanixAHVPlugInOnCC.ps1"
									

									
										    
									

									
										  } 
									

									
										}
									
									 

									
										#### Execute the PlugIn installation script on CC1
									

									
										resource "null_resource" "CallRequiredScriptsOnCC1" {
									

									
										 depends_on = [ null_resource.UploadNutanixAHVToCC1 ]
									

									
										 connection {
									

									
										    type            = var.Provisioner_Type
									

									
										    user            = var.Provisioner_Admin-Username
									

									
										    password        = var.Provisioner_Admin-Password
									

									
										    host            = var.Provisioner_CC1-IP
									

									
										}
									

									
										 
									

									
										  provisioner "remote-exec" {
									

									
										    inline = [
									

									
										      "powershell -File ${var.CC_Install_LogPath}/DATA/InstallNutanixAHVPlugInOnCC.ps1"
									

									
										    ]
									

									
										  } 
									

									
										} 
									
									 

									
										#### Execute the PlugIn installation script on CC2
									

									
										resource "null_resource" "CallRequiredScriptsOnCC2" {
									

									
										 depends_on = [ null_resource.UploadNutanixAHVToCC2 ]
									

									
										 connection {
									

									
										    type            = var.Provisioner_Type
									

									
										    user            = var.Provisioner_Admin-Username
									

									
										    password        = var.Provisioner_Admin-Password
									

									
										    host            = var.Provisioner_CC2-IP
									

									
										}
									

									
										 
									

									
										  provisioner "remote-exec" {
									

									
										    inline = [
									

									
										      "powershell -File ${var.CC_Install_LogPath}/DATA/InstallNutanixAHVPlugInOnCC.ps1"
									

									
										    ]
									

									
										  } 
									

									
										}
									
								
							
						
					
				
			
		

		
			 
			_CCRLOnNutanix-Install-NTNXPluginInstallation.ps1
		

		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
			
				
					
						
							
								cmd /c 'msiexec /i "c:\temp\xdinst\DATA\NutanixAHV_Citrix_plugin.msi" /qn /l*v "c:\temp\xdinst\DATA\NutanixAHVPlugin.log" ALLUSERS="1" IAGREE="Yes" PLUGININSTALLPATH="C:\Program Files\Common Files\Citrix\HCLPlugins\CitrixMachineCreation\v1.0.0.0\" INSTALLFOLDER="C:\Program Files\Common Files\Citrix\HCLPlugins\CitrixMachineCreation\v1.0.0.0\NutanixAHV\" PVSINSTALLFOLDER="C:\Program Files\Common Files\Citrix\HCLPlugins\CitrixMachineCreation\v1.0.0.0\NutanixAHV\" REGISTERPLUGINSTOREPATH="C:\Program Files\Common Files\Citrix\HCLPlugins\CitrixMachineCreation\v1.0.0.0\" ADDLOCAL="F7_9_CWA_INSTALLFOLDER" REMOVE="PVS_F7_14_INSTALLFOLDER,F7_9_INSTALLFOLDER" INSTALLERTYPE="cwa"' 
							 
						
					
				
			
		

		
			 
		 

		
			Module 4: CCRLOnNutanix-CCStuff
		

		
			These are the Terraform configuration files for Module 4 (excerpts):
		 

		
			_CCRLOnNutanix-CCStuff-CreateCCEntities.tf
		

		
			 
		 

		
			
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}			
			
				
					
						
							
								
									
										# 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"
									

									
										}
									
									 

									
										#### Creating the Hypervisor Connection
									

									
										resource "citrix_nutanix_hypervisor" "CreateHypervisorConnection" {
									

									
										  depends_on = [ data.local_file.LoadZoneID ]
									

									
										    name                        = var.CC_Nutanix-HypConn-Name
									

									
										    zone                        = data.local_file.LoadZoneID.content
									

									
										    username                    = var.CC_Nutanix-HypConn-UN
									

									
										    password                    = var.CC_Nutanix-HypConn-PW
									

									
										    password_format             = var.CC_Nutanix-HypConn-PWFormat
									

									
										    addresses                   = var.CC_Nutanix-HypConn-IP
									

									
										} 
									
									 

									
										#### Sleep 30s to let Background processes settle
									

									
										resource "time_sleep" "wait_30_seconds" {
									

									
										  depends_on = [ citrix_nutanix_hypervisor.CreateHypervisorConnection ]
									

									
										  create_duration = "30s"
									

									
										}
									
									 

									
										#### Creating the Hypervisor Resource Pool
									

									
										resource "citrix_nutanix_hypervisor_resource_pool" "CreateHypervisorPool" {
									

									
										  depends_on = [ time_sleep.wait_30_seconds]
									

									
										    name                        = var.CC_Nutanix-HypConnPool-Name
									

									
										    hypervisor                  = citrix_nutanix_hypervisor.CreateHypervisorConnection.id
									

									
										    networks                    = var.CC_Nutanix-HypConnPool-Networks       
									

									
										}  
									
									 

									
										#### Sleep 30s to let Background processes settle
									

									
										resource "time_sleep" "wait_30_seconds1" {
									

									
										  depends_on = [ citrix_nutanix_hypervisor_resource_pool.CreateHypervisorPool ]
									

									
										  create_duration = "30s"
									

									
										}
									
									 

									
										### Creating a Machine Catalog
									

									
										#### Create the Machine Catalog
									

									
										resource "citrix_machine_catalog" "CreateNTXMCSCatalog" {
									

									
										  depends_on            = [ time_sleep.wait_30_seconds1 ]
									

									
										    name                        = var.CC_Nutanix-MC-Name
									

									
										    description                 = var.CC_Nutanix-MC-Description
									

									
										    allocation_type             = var.CC_Nutanix-MC-AllocationType
									

									
										    session_support             = var.CC_Nutanix-MC-SessionType
									

									
										    provisioning_type           = "MCS"
									

									
										    zone                        = data.local_file.LoadZoneID.content
									

									
										    provisioning_scheme         =   {
									

									
										        hypervisor               = citrix_nutanix_hypervisor.CreateHypervisorConnection.id
									

									
										        hypervisor_resource_pool = citrix_nutanix_hypervisor_resource_pool.CreateHypervisorPool.id
									

									
										        identity_type            = var.CC_Nutanix-MC-IDPType
									

									
										        machine_domain_identity  = {
									

									
										            domain                   = var.CC_Nutanix-MC-Domain
									

									
										            domain_ou                = var.CC_Nutanix-MC-DomainOU
									

									
										            service_account          = var.CC_Nutanix-MC-DomainAdmin-Username-UPN
									

									
										            service_account_password = var.CC_Nutanix-MC-DomainAdmin-Password
									

									
										        }
									

									
										        nutanix_machine_config = {
									

									
										            master_image        = var.CC_Nutanix-MC-MasterImage
									

									
										            container           = var.CC_Nutanix-MC-Container
									

									
										            cpu_count           = var.CC_Nutanix-MC-CPUCount
									

									
										            cores_per_cpu_count = var.CC_Nutanix-MC-CoresPerCPU
									

									
										            memory_mb           = var.CC_Nutanix-MC-Memory
									

									
										        }
									

									
										        number_of_total_machines =  var.CC_Nutanix-MC-Machine_Count
									
									 

									
										        machine_account_creation_rules = {
									

									
										            naming_scheme      = var.CC_Nutanix-MC-Naming_Scheme_Name
									

									
										            naming_scheme_type = var.CC_Nutanix-MC-Naming_Scheme_Type
									

									
										        }
									

									
										    }
									

									
										}
									
									 

									
										#### Sleep 60s to let CC Background processes settle
									

									
										resource "time_sleep" "wait_30_seconds_2" {
									

									
										  depends_on = [ citrix_machine_catalog.CreateNTXMCSCatalog ]
									

									
										  create_duration = "30s"
									

									
										}   
									
									 

									
										#### Create the Delivery Group based on the Machine Catalog
									

									
										resource "citrix_delivery_group" "CreateDG" {
									

									
										  depends_on = [ time_sleep.wait_30_seconds_2]
									

									
										    name                                    = var.CC_Nutanix-DG-Name
									

									
										    associated_machine_catalogs             = [
									

									
										        {
									

									
										            machine_catalog                 = citrix_machine_catalog.CreateNTXMCSCatalog.id
									

									
										            machine_count                   = var.CC_Nutanix-MC-Machine_Count
									

									
										        }
									

									
										    ]
									

									
										    desktops                                = [
									

									
										        {
									

									
										            published_name                  = var.CC_Nutanix-DG-PublishedDesktopName
									

									
										            description                     = var.CC_Nutanix-DG-Description
									

									
										            restricted_access_users         = {
									

									
										                                               allow_list = [ "TACG\\vdaallowed" ]
									

									
										                                              }
									

									
										            enabled                         = true
									

									
										            enable_session_roaming          = var.CC_Nutanix-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_Nutanix-DG-AS-Name
									

									
										                display_name                = var.CC_Nutanix-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\\vdaallowed" ]
									

									
										                                              }
									

									
										    reboot_schedules                        = [
									

									
										                                               {
									

									
										                                                 name = "TACG-NTX-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
									

									
										                                               }
									

									
										  ]
									

									
										}]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_08/image.png.d3ed460b4afeb7c7ae26f9f2ca8304b9.png" length="7735" type="image/png"/><pubDate>Fri, 16 Aug 2024 07:48:18 +0000</pubDate></item><item><title>Workspace Environment Manager Tool Hub</title><link>https://community.stage.citrix.com/tech-zone/learn/tech-briefs/wem-tool-hub/</link><description><![CDATA[Overview



	The Citrix Workspace Environment Management (WEM) Tool Hub is a versatile suite designed to streamline the configuration and management of Citrix environments for administrators. This comprehensive toolkit enhances administrative efficiency by offering a range of user-friendly tools, such as the Application Assistant, File Type Association Assistant, and Start Menu Configuration. These tools are engineered to simplify the setup and optimization processes, ensuring a seamless experience in managing user profiles, application configurations, and logon performance. With features like the Windows Logon Analysis for detailed performance insights and the Profile Migration Tool for smooth profile transitions, the WEM Tool Hub empowers administrators to deliver optimal user experiences with reduced complexity and improved operational efficiency.
 


	 
 


	WEM Tool Hub



	
 


	 
 


	Application Assistant



	The Application Assistant is a powerful feature designed to streamline adding and managing applications within the Citrix environment. It simplifies the configuration of applications and Citrix Workspace resources, enabling administrators to prepare and import application data into the management console efficiently. By providing an intuitive interface to browse, select, and copy necessary application information, the Application Assistant reduces the complexity and time required for application setup, ensuring a more seamless and error-free deployment process. This tool significantly enhances administrative productivity and helps maintain a consistent and optimized user experience across the Citrix workspace.
 


	Key Features
 


	
		Easy Configuration: Browse to existing resources, then copy &amp; paste them into the web console.
	
	
		Central App Control: Control application processing on logon &amp; reconnection.
	
	
		Resource Flexibility: Enumerate applications for multiple stores.
	
	
		Assignment Control: Easily manage user or group assignment and app visibility options.
	



	Select Application &amp; Copy Data
 


	
 


	 
 


	Paste Resource Info into WEM Console
 


	
 


	 
 


	Assign to users or group
 


	
 


	Configured Applications
 


	
 


	 
 


	Benefits
 


	
		Timesaving: By automating many steps in application configuration, the Application Assistant reduces the time and effort required to deploy and manage applications.
	
	
		Improved Accuracy: The intuitive interface and copy-paste functionality help eliminate errors that can occur with manual configuration, ensuring that application settings are consistently applied across the environment.
	
	
		Enhanced User Experience: Properly configured applications lead to a smoother and more reliable user experience, reducing downtime and increasing productivity.
	
	
		Simplified Management: The Application Assistant centralizes application configuration tasks, making it easier for administrators to manage and update applications as needed.
	



	File Info Viewer



	The File Info Viewer is an essential utility for administrators, designed to facilitate the quick retrieval of critical file data for configuring executable rules in the management console. By allowing admins to select a file or folder and instantly access detailed information such as path, publisher, and hash values, the File Info Viewer streamlines the process of setting up and managing application security and access control rules. This tool enhances the accuracy and efficiency of rule configuration, ensuring that administrators can effectively secure and optimize their Citrix environments with minimal effort.
 


	Key Features
 


	
		Detailed File Information: The File Info Viewer displays comprehensive details about files, including size, type, location, and associated metadata, helping administrators make informed decisions about file management.
	
	
		Metadata Access: Administrators can view detailed metadata for each file, such as creation and modification dates, which aids in tracking changes and managing file versions.
	
	
		Quick Actions: The File Info Viewer enables quick actions such as copying file paths or exporting file details, making it easy to share information or use in other management tasks.
	



	File Info Viewer
 


	
 


	Benefits
 


	
		Improved File Management: The File Info Viewer helps administrators manage files more effectively by providing detailed information.
	
	
		Enhanced Troubleshooting: Access to comprehensive file details and metadata allows administrators to quickly identify and resolve file-related issues, improving overall system performance.
	
	
		Accurate Information: Detailed and accurate file information helps ensure file management decisions are based on reliable data, reducing the risk of errors.
	



	File Type Association Assistant



	The File Type Association Assistant is a tool that simplifies the configuration of file type associations (FTAs) within the Citrix environment. This assistant lets administrators easily define and manage associations between file extensions and their corresponding applications, ensuring users can open and edit files using appropriate software. The tool streamlines the setup process and minimizes errors by providing a user-friendly interface to browse, select, and customize FTAs.
 


	Key Features
 


	
		Easy Configuration: Administrators can browse and select from registered applications, making it simple to find and associate the correct programs with specific file types.
	
	
		Configuration data can then be copied from the FTA Assistant and pasted into the Citrix WEM console, streamlining the setup process and ensuring accuracy.
	
	
		Custom Actions: The tool supports customizing actions such as Open, Edit, and Print for associated file types, providing flexibility in handling files.
	



	Lookup file extension, select ProgID &amp; Copy
 


	
 


	Paste data into WEM Console
 


	
 


	Add target assignment
 


	
 


	 
 


	Check FTA is assigned &amp; enabled
 


	
 


	Benefits
 


	
		Consistency: By centrally managing file type associations, the FTA Assistant ensures that users have a consistent experience when opening, editing, or printing files across the Citrix environment.
	
	
		Accuracy: The copy-and-paste functionality minimizes errors and ensures configuration data is correctly applied in the management console.
	



	Profile Migration Tool



	The Profile Migration Tool is designed to streamline migrating user profiles from FSLogix profile containers, Citrix file-based solutions, and local machine profiles to Citrix container-based profile solutions (CPM). This tool seamlessly transfers user settings, preferences, and data, minimizing disruptions and maintaining a consistent user experience.
 


	Key Features
 


	
		Automated Migration: The Profile Migration Tool automates the migration process, reducing manual effort and the risk of errors.
	
	
		Compatibility: It supports various user profile types and ensures compatibility with various versions of Windows and Citrix environments (FSLogix profile containers, Citrix file-based solution, and local machine profiles). The Target operating system can be selected before migration.
	
	
		Data Integrity: The tool ensures that all user data, settings, and preferences are accurately transferred, preserving the user’s personalized environment.
	



	 
 


	Select profile type to migrate &amp; target
 


	
 


	Select users or groups &amp; OS version
 


	
 


	Results page
 


	
 


	Benefits
 


	
		Seamless Transitions: By automating the profile migration process, the tool ensures that users experience minimal disruptions and can continue working without interruption.
	
	
		Efficiency: The tool significantly reduces the time and effort required for profile migrations, allowing administrators to focus on other critical tasks.
	
	
		Consistency: The Profile Migration Tool ensures user settings and preferences are consistently applied, maintaining a familiar and productive environment.
	
	
		Compatibility: Integrated with the Web console for profile configuration, logging &amp; health check.
	



	Rule Generator for App Access Control



	The Rule Generator for App Access Control is designed to streamline the creation and management of rules that control user access to applications, files, folders, and registries. This tool is particularly useful for administrators who want to enforce security policies and manage application visibility to users or machines. These rules can then be imported into Profile Management Settings &gt; App Access Control to control user access to these resources. 
 


	Key Features
 


	
		Simplified Rule Creation: The Rule Generator allows administrators to create rules through a graphical user interface, reducing the need for complex scripting. This user-friendly approach minimizes errors and improves efficiency.
	
	
		Target Objects: Administrators can specify objects such as files, folders, and registry keys associated with applications they want to control. The tool can scan the system to list installed applications and their related objects, simplifying the selection process.
	
	
		Assignments: Rules can be assigned to users, computers (Including Non-domain joined), and processes. This flexibility ensures that the right controls are applied to the appropriate entities within the organization. Assignments can be managed using Azure Active Directory (AAD) object selectors or specified directly within the tool.
	
	
		Import and Export: The tool can generate raw data for the created rules, which can be saved and later imported into the WEM administration console (Profile Management Settings &gt; App access control). Also facilitates easy backup and restoration of rules.
	



	Practical Applications
 


	
		Security Management: Enforce strict control over which applications are accessible to different user groups, ensuring that sensitive or licensed software is only available to authorized personnel.
	
	
		Application Visibility: Hide unnecessary or potentially distracting applications from users who do not need them, streamlining their workspace and improving productivity.
	
	
		Profile Management: Integrate with Citrix Profile Management to apply rules consistently across user profiles, maintaining a secure and efficient environment.
	



	Create Rule
 


	
 


	Assign Rule
 


	
 


	Raw Data to Copy
 


	
 


	Paste into WEM Console
 


	
 


	 
 


	Start Menu Configurator for Windows 11



	The Start Menu Configurator for Windows 11 is an innovative tool designed to help administrators customize and manage the Start menu layouts for their Windows 11 environments. This tool provides a user-friendly interface for arranging and deploying Start menu configurations, ensuring a streamlined and efficient user experience.
 


	Key Features
 


	
		User-Friendly Interface: The Start Menu Configurator offers a simple and intuitive GUI, allowing administrators to easily select and arrange applications within the Start menu. This eliminates the need for manual editing of JSON files or complex scripting.
	
	
		Customization of Pinned Layout: Administrators can choose which applications to pin to the Start menu and arrange them in the desired order. This feature helps create a consistent and organized Start menu layout across the organization.
	
	
		Bulk Application Addition: The tool supports adding multiple applications simultaneously, saving time and effort. Administrators can select from a list of available applications and include them in the Start menu configuration.
	
	
		Easy Deployment: After customizing the start menu layout, the tool generates a JSON configuration file that can be easily copied and pasted into the WEM administration console, ensuring the quick and efficient deployment of the Start menu settings.
	



	 
 


	Select the applications to be added.
 


	
 


	 
 


	Arrange the icons &amp; generate configuration
 


	
 


	 
 


	Paste configuration into WEM Console
 


	
 


	Practical Applications
 


	
		Standardized Start Menu Layouts: Ensure all users have a consistent Start menu layout, which will make finding and accessing the applications they need easier.
	
	
		Improved User Experience: By customizing the Start menu, administrators can remove unnecessary applications and highlight important tools, enhancing the overall user experience.
	



	UPM Configuration Check Tool



	The UPM Configuration Check Tool is a valuable feature designed to assist administrators in verifying and optimizing User Profile Management (UPM) configurations. This tool provides comprehensive diagnostics to ensure that UPM settings are correctly applied and functioning as intended, enhancing user environments' stability and performance.
 


	Key Features
 


	
		Comprehensive Diagnostics: The UPM Configuration Check Tool performs detailed diagnostics on UPM settings, checking for common configuration errors and issues. This helps administrators identify and resolve problems that could affect user profiles.
	
	
		Configuration Validation: The tool validates the UPM configuration against best practices and Citrix recommendations. It ensures all settings are optimally configured to provide the best user experience.
	
	
		Detailed Reporting: Administrators receive detailed reports outlining the results of the configuration checks. These reports include information on detected issues, their potential impact, and recommended corrective actions.
	



	
 


	User Store Creation Tool
 


	The User Store Creation Tool is a great feature that is designed to streamline the process of setting up user stores. User stores are central network locations for storing Citrix user profiles, and this tool simplifies their creation, ensuring that appropriate permissions and configurations are applied. 
 


	Key Features
 


	
		Simplified Setup Process: The User Store Creation Tool offers a straightforward interface for setting up user stores. It guides administrators through creating and configuring these stores without requiring extensive manual setup.
	
	
		Automated Configuration: The tool configures folder paths and share names, ensuring user stores are set up correctly with minimal effort. It also sets appropriate permissions for users and groups, reducing the risk of configuration errors.
	
	
		Customizable Options: Administrators can specify custom folder paths and share names for specific users and groups. This customization ensures the user store setup aligns with organizational policies and requirements.
	
	
		Error Handling and Logging: The tool provides detailed error messages and logs, helping administrators troubleshoot and resolve issues during the user store creation. This feature enhances reliability and reduces downtime.
	



	 
 


	User Store Creation Tool
 


	
 


	 
 


	Benefits
 


	
		Efficiency: The User Store Creation Tool significantly reduces the time and effort required to set up user stores, enabling faster deployments and easier management.
	
	
		Consistency: By automating the setup process, the tool ensures that all user stores are configured consistently, reducing the risk of errors and improving overall system stability.
	



	Windows Logon Analysis



	The Windows Logon Analysis tool is a powerful feature designed to help administrators analyze and optimize the Windows logon process. This tool provides detailed insights into various stages of the logon process, identifies potential bottlenecks, and offers actionable recommendations to enhance logon performance.
 


	Key Features
 


	
		Detailed Logon Metrics: The tool breaks down the logon process into distinct phases, such as pre-logon, authentication, profile loading, group policy processing, and shell start-up. Each phase is analyzed to provide granular metrics and insights.
	
	
		Optimization Tips: The tool provides practical tips for optimizing each phase of the logon process. These recommendations help administrators implement targeted improvements to reduce logon times and enhance overall performance.
	



	 
 


	Windows Logon Analysis
 


	
 


	 
 


	Windows Logon Analysis - User Logon Phase Breakdown
 


	
 


	 
 


	Practical Applications
 


	
		Performance Optimization: Use the tool to identify and address bottlenecks in the logon process. Implementing the provided optimization tips can significantly reduce logon times and improve user satisfaction.
	
	
		Troubleshooting: Leverage the detailed logon metrics and reports to troubleshoot logon-related issues. The tool helps pinpoint specific stages where delays occur, enabling targeted troubleshooting.
	



	Benefits
 


	
		Enhanced User Experience: The Windows Logon Analysis tool enhances the overall user experience by reducing logon times and improving performance, increasing productivity and satisfaction.
	
	
		Data-Driven Decisions: The tool's comprehensive insights and historical data analysis support data-driven infrastructure planning and optimization decision-making.
	



	Summary



	The Citrix Workspace Environment Management (WEM) Tool Hub is an indispensable tool suite designed to enhance IT administrator's efficiency and effectiveness. The WEM Tool Hub ensures optimal management of user environments by simplifying complex configuration tasks, providing detailed performance insights, and offering robust troubleshooting capabilities. Whether customizing the Start menu for Windows 11, managing user profile stores, analyzing Windows logon performance, or controlling app access, the WEM Tool Hub empowers administrators to deliver a seamless, secure, and high-performance user experience. With its user-friendly interface and comprehensive feature set, the WEM Tool Hub is a critical asset for any organization seeking to maximize productivity and user satisfaction in their Citrix deployments.]]></description><pubDate>Wed, 07 Aug 2024 15:50:17 +0000</pubDate></item><item><title>Citrix Enterprise Browser</title><link>https://community.stage.citrix.com/tech-zone/learn/tech-briefs/citrix-enterprise-browser/</link><description>Overview



	This Tech Brief explores Enterprise Browsers, detailing their purpose in an enterprise environment and providing a detailed overview of the Citrix Enterprise Browser and its core features.
 


	What is an Enterprise Browser?



	Browsers are the number one way for malware, spyware, and ransomware to infect enterprise organizations. Enterprise Browsers were introduced to the market to meet organizations' security requirements and isolate browser sessions to combat this ongoing problem. At their core, enterprise browsers are specialized browsers that provide enhanced security and control over browser functionality, access to corporate web and SaaS applications, and sensitive data. Enterprise Browsers typically have several features to enhance users' security, manageability, and productivity in enterprise environments. These features can include:
 


	
		Enhanced security features like sandboxing, malware protection, phishing protection, Data Loss Prevention (DLP), and data encryption to protect sensitive data and mitigate cyber threats.
	
	
		Centralized policy and user access control management allow administrators to configure browser settings for all enterprise devices.
	
	
		Compliance and reporting features like activity logging help enterprises comply with regulations and internal policies.
	
	
		System integration into other enterprise systems, such as Single Sign-on (SSO) and custom browser extensions or plugins.
	



	Use Cases for Enterprise Browsers



	There are several use cases for Enterprise Browser use within an enterprise, including:
 


	
		Internal web applications - Enterprises provide secure and managed access to internal web applications.
	
	
		Compliance - Enterprises that must comply with industry-specific regulations and internal policies.
	
	
		Bring your own device - Enterprises that allow personal devices to access enterprise resources.
	
	
		Contract and Temp Work - Enterprises that provide corporate access to contract or temp worker unmanaged devices.
	



	What is Citrix Enterprise Browser?



	Citrix Enterprise Browser is a Chromium-based browser that runs on the endpoint and creates a security sandbox for the web session. Focusing on security as a transparent layer on top of an enhanced user experience, running locally gives end users the best performance for rendering webpages of SaaS and private web applications, as the browser looks and acts like a user's traditional browser. The Citrix Enterprise Browser provides all the functionalities of a native browsing solution to the end user&#x2019;s work environment. The secure sandbox protects the end user and the enterprise against malware, performance degradation, data loss, and unintended end-user behavior. With Citrix Enterprise Browser, organizations can centrally apply adaptive access policies that limit keyloggers, screen captures, clipboard operations, and more. In addition, the Citrix Enterprise Browser leverages the zero-trust capabilities of Citrix Secure Private Access. 
 


	Launching any internally hosted web application or a SaaS app with security policies configured will trigger the Citrix Enterprise Browser to enforce security policies and enable a seamless user experience.
 


	Citrix Enterprise Browser protects against cyber threats, ensures secure access to critical applications, and helps comply with industry regulations and internal security policies.
 


	Citrix Enterprise Browser Architecture



	
 


	End users log into the Citrix Workspace or Citrix StoreFront on managed or unmanaged devices using single sign-on or Federated authentication. Citrix Enterprise Browser inherits the user's authenticated session, providing seamless access to authorized web and SaaS applications. A new browser session is launched within a secure, isolated environment, and Secure Access Policies and Configurations are applied, including last-mile DLP policies. Citrix Enterprise Browser routes web traffic through the Secure Private Access web URL filtering, and data is encrypted to transit to protect against interception. Users then interact with the web applications through the Citrix Enterprise Browser interface.
 


	Citrix Enterprise Browser Features



	There are several features within the Citrix Enterprise Browser to enforce zero-trust access and ensure a safe browsing experience for your end users. 
 


	Web/SaaS app access



	Internal web and external SaaS apps can be directed to open within the Citrix Enterprise Browser. For internal web apps that would otherwise require a VPN to access outside the Citrix Workspace framework, Citrix Enterprise Browser provides enhanced security for end users to launch the web apps. This is based on the default deny ZTNA principal, and a separate full tunnel is not created. For external SaaS apps, Secure Private Access policies can be applied to ensure that SaaS apps are open securely using the Citrix Enterprise Browser.
 


	Data Loss Prevention (DLP)



	Consumer browsers do not control copy/paste, downloads, printing, and screen capture, which can be a risk for critical apps, especially on BYO devices. Together with Citrix Secure Private Access, the Citrix Enterprise Browser provides ZTNA access to protect users against threats through configured security policies and controls for the following:
 


	
		Watermark application
	
	
		Clipboard restriction or isolation
	
	
		Printing restriction
	
	
		File up- and download restriction
	
	
		App protection policies, including:
		
			
				Anti-keylogging to prevent hijacking of sensitive data
			
			
				Anti-screen capture against malicious software, as well as accidental screen sharing
			
		
	
	
		Personal data masking
	



	Additional information on these controls can be found here and here.
 


	Browser Management



	With Citrix Enterprise Browser and Secure Private Access, administrators can centrally manage and secure the browsing experience on managed and unmanaged devices. Browser management capabilities include:
 


	
		Extension Management &#x2014; Create Secure Private Access policies with Citrix Enterprise Browser to force-install IT-required extensions and set up extensions to allow lists for end users to download optional extensions they may require.
	
	
		Web Filtering &#x2014; Combined with Secure Private Access policies, comprehensive web filtering policies can be applied.
	
	
		Push Bookmarks &#x2014; Administrators can push bookmarks to the end user's device to quickly access needed resources.
	
	
		Additional Controls &#x2014; Allow users to save passwords, clear data on exit (including which data to clear), allow incognito mode, and developer tools.
	



	User Experience



	The Citrix Enterprise Browser allows administrators to customize the browser settings and policies according to their security needs. It provides a familiar experience that acts and looks like any other browser for easy navigation and use. Citrix Enterprise Browser and Citrix Secure Private Access provide internal and external access to applications without a VPN. This enterprise-grade browser works on any corporate or personal device. Citrix Enterprise Browser fully supports microphones, webcams, progressive web apps, web conferencing tools, bookmarks, dark modes, tabs, and windows. Work Browser mode can open all work links and allow an alternate browser to open non-work links. 
 


	
 


	Remote Browser Isolation



	Remote browser Isolation is a web security measure that creates an "air gap" between a remote user's internet browser and corporate networks. It separates web browsing activity from endpoint devices by redirecting sessions to an isolated environment. This reduces the attack surface, ensuring browser-based cyber threats don't reach the company's digital resources. With Citrix Enterprise Browser and Remote Browser Isolation, policy controls can be enabled to secure browsing for any user, over any connection, on any device, and keep malware off your network. On Bring Your Own (BYO), unmanaged devices, or even managed devices, the Remote Browser Isolation service prevents the movement of sensitive and malicious content to ensure browser-based threats don't reach the user's device by creating an air gap between the browser and device. Sessions are also discarded once the user is done for additional protection from web threats or attackers.
 


	
 


	Summary



	Citrix Enterprise Browser is a powerful tool for organizations looking to secure their web and SaaS applications while providing a seamless, high-performance user experience. Its integration with Citrix Workspace, advanced security features, and ease of management make it an ideal choice for enterprises aiming to enhance their digital workspace infrastructure.</description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_08/image.png.75566e45fb4311dfac041e62489ea21f.png" length="60100" type="image/png"/><pubDate>Tue, 06 Aug 2024 19:05:45 +0000</pubDate></item><item><title>Migrating from F5 to NetScaler: Terms to Know</title><link>https://community.stage.citrix.com/tech-zone/build/tech-papers/f5-netscaler-migration-terms/</link><description>Overview



	Migrating from F5 to NetScaler involves several critical steps and considerations, necessitating a comprehensive understanding of both technologies and their respective architectures. F5, with its BIG-IP platform, primarily uses the Traffic Management Operating System (TMOS) and iRules for traffic manipulation, offering robust features like Global Traffic Manager (GTM) and Local Traffic Manager (LTM). NetScaler utilizes its proprietary Operating System and natively supports policies through NetScaler policies and AppExpert, providing similar functionalities like Global Server Load Balancing (GSLB) and Local Load Balancing.
 


	When migrating from F5 to NetScaler, it is critical to understand the differences in concepts and terms between the two technologies to ensure a smooth transition.  The table here outlines the key distinctions between F5 and NetScaler to help your migration effort.
 


	
		
			
				
					F5 Term/Concept
				 
			
			
				
					NetScaler Term/Concept
				 
			
		
	
	
		
			
				
					BIG-IP. ADC product offering.
				 
			
			
				
					NetScaler
				 
			
		
		
			
				
					Virtual Server. Logical entity that represents a network service.
				 
			
			
				
					Virtual Server
				 
			
		
		
			
				
					Virtual IP (VIP). An IP address assigned to a virtual server.
				 
			
			
				
					Virtual IP
				 
			
		
		
			
				
					Pool. A group of servers offering the same service.
				 
			
			
				
					Service Group
				 
			
		
		
			
				
					Pool Members. Individual servers within a pool
				 
			
			
				
					Services
				 
			
		
		
			
				
					SNAT (Secure Network Address Translation). Translates the source IP address of outgoing packets.
				 
			
			
				
					Source IP (SNIP)
				 
			
		
		
			
				
					SSL Profile. Configurations for SSL/TLS Settings.
				 
			
			
				
					SSL Profile
				 
			
		
		
			
				
					HA Pair. High Availability devices configured for failover.
				 
			
			
				
					HA Pair
				 
			
		
		
			
				
					Local Traffic Policies
				 
			
			
				
					Content Switching
				 
			
		
		
			
				
					Advanced WAF/ASM
				 
			
			
				
					Application Firewall
				 
			
		
		
			
				
					Local Traffic Policies and/or iRules
				 
			
			
				
					Responder Policy
				 
			
		
		
			
				
					Local Traffic Policies and/or iRules
				 
			
			
				
					Rewrite Policy
				 
			
		
		
			
				
					On-Box Analytics (AVR), sFlow, IPFIX
				 
			
			
				
					AppFlow
				 
			
		
		
			
				
					BIG-IQ Centralized Management
				 
			
			
				
					NetScaler Console
				 
			
		
		
			
				
					DNS (rename from GTM - Global Traffic Manager)
				 
			
			
				
					GSLB (Global Server Load Balancing)
				 
			
		
		
			
				
					APM (Access Policy Manager)
				 
			
			
				
					AAA for Traffic Management
				 
			
		
		
			
				
					IP Intelligence
				 
			
			
				
					IP Reputation
				 
			
		
		
			
				
					SSL Orchestrator
				 
			
			
				
					SSL Forward Proxy
				 
			
		
		
			
				
					Integrated Bot Defense
				 
			
			
				
					Bot Management
				 
			
		
		
			
				
					iControl. API for automation and integration. 
				 
			
			
				
					NITRO
				 
			
		
		
			
				
					iApps: Application templates. F5 Application Services Templates (FAST) are replacing iApp templates. See https://support.f5.com/csp/article/K13422  for details.
				 
			
			
				
					Stylebooks
				 
			
		
		
			
				
					iRules: Customized TCL-based scripting language for programmatic traffic control
				 
			
			
				
					NetScaler Policies
				 
			
		
		
			
				
					iSession: Maintains session persistence across multiple servers.
				 
			
			
				
					Session Reliability
				 
			
		
		
			
				
					WebSafe/DataSafe
				 
			
			
				
					Application layer encryption, form field encryption</description><pubDate>Thu, 01 Aug 2024 04:05:00 +0000</pubDate></item><item><title>Citrix VDI Handbook - Assess</title><link>https://community.stage.citrix.com/tech-zone/build/tech-papers/citrix-vdi-handbook-assess/</link><description><![CDATA[Assess
		

		
			Creating an app and desktop delivery solution begins with a proper assessment. Architects who fail to assess the current environment properly find that they require the assessment information later, forcing them to backtrack, which could stall and put the project at risk. By gathering all of the information from the outset, the architect will gain an appreciation for the current environment and be able to work from the beginning on properly aligning business and user requirements with the overall solution.
		 

		
			The Assess phase is a four-step, simple-to-follow process:
		 

		
			
		 

		
			 
		 

		
			Step 1: Define the Organization
		

		
			The first step in your virtualization project should be understanding and prioritizing the organization's strategic imperatives. This enables the project management team to define success criteria and allows the design team to create a tailored and optimized architecture.
		 

		
			During this step, it is important to identify specific business use cases for the Citrix environment. The associated business unit, user personas, workload, and application requirements should all be captured as a part of this process.
		 

		
			Requirements can be captured during meetings or by distributing questionnaires. Meetings are more time-consuming but allow for follow-up questions to be asked and help to simplify the prioritization process. This exercise must be completed jointly by both business managers and IT decision-makers since both groups will have significantly different viewpoints. Most organizations do not focus on technology. They focus on the needs of the user and the organization. These needs can be met with technical solutions, but the team must understand the “Why” of the project.
		 

		
			This table identifies a few other priorities that many organizations often state:
		 

		
			
				
					
						
							Requestor
						 
					
					
						
							Requirement
						 
					
				
				
					
						
							Business Managers
						 
					
					
						
							Better IT agility and responsiveness – Flexible solution capable of accommodating periods of change such as rapid growth or downsizing. For example, it enables the business to rapidly set up project offices or temporary points of sale without long delays, hardware acquisitions, or IT notification periods. 
						 
					
				
				
					
						
							Bring your own device – Empower employees to choose their own devices to improve productivity, collaboration, and mobility. 
						 
					
				
				
					
						
							Remote, Mobile, and Hybrid Work – The business needs to support home workers to attract and retain top talent and/or traveling employees.
						 
					
				
				
					
						
							IT Decision Makers
						 
					
					
						
							Cloud Migration—Take advantage of the benefits of moving to the cloud, including increased flexibility and scalability, to meet business demands.
						 
					
				
				
					
						
							Better desktop management—Simplify infrastructure management. IT is not as proactive as it would like and spends too much time “fighting fires.”
						 
					
				
				
					
						
							Increase security—Data theft, cyber-attacks, or the loss of devices containing sensitive data are big risks, and preventive measures are a top priority.
						 
					
				
				
					
						
							Extend desktop hardware lifecycle—Replacing workstations every three to five years to keep up with the operating system or application requirements has been very costly.
						 
					
				
				
					
						
							Reducing IT Management Scope: Improve IT efficiency by focusing only on business-critical aspects and offloading the remaining functions to third parties via cloud or service providers. 
						 
					
				
				
					
						
							Improving user experience - Increasing performance or enabling features that would otherwise not be possible with a geographically dispersed user population.
						 
					
				
			
		

		
			The prioritization process should be completed in collaboration with the project team, business managers, and IT managers so that all views are considered.
		 

		
			Step 2: Define the User Groups
		

		
			Although there are multiple approaches to defining user groups, it is often easiest to align user groups with departments, as most users within the same department or organizational unit consume the same set of applications.
		 

		
			User Segmentation
		

		
			Depending on the department's size, a subset of users with unique requirements might exist. Each defined user group should be evaluated against the following criteria to determine if the departmental user group needs to be further divided into more specialized user groups.
		 

		
			
				Primary datacenter – Each user will have a primary datacenter or cloud resource location assigned to host their virtual desktop, data, and application servers. Identify the datacenter the user should be assigned to rather than the one they currently use. Users will be grouped based on their primary datacenter so that a unique design can be created for each one.
			
			
				Personalization – Personalization requirements are used to help determine the appropriate VDI model for each user group. For example, if a user group requires complete personalization, a personal desktop will be recommended as the optimal solution. There are three classifications available:
			
		

		
			
				
					
						
							Personalization
						 
					
					
						
							Requirement
						 
					
				
				
					
						
							None
						 
					
					
						
							Users cannot modify user or application settings, such as in a kiosk.
						 
					
				
				
					
						
							Basic
						 
					
					
						
							Users can modify the user-level settings of desktops and applications. 
						 
					
				
				
					
						
							Complete
						 
					
					
						
							The User can make changes, including installing applications.
						 
					
				
			
		

		
			
				Security – Security requirements are used to help determine the appropriate desktop and policy (or policies) for each user group. For example, a hosted pooled desktop will be optimal if a user group requires high security. There are three classifications available:
			
		

		
			
				
					
						
							Security Level
						 
					
					
						
							Description
						 
					
				
				
					
						
							Low
						 
					
					
						
							Users can transfer data in and out of the virtualized environment.
						 
					
				
				
					
						
							Medium
						 
					
					
						
							All authentication and session traffic should be secured; users cannot install or modify their virtualized environment.
						 
					
				
				
					
						
							High
						 
					
					
						
							Besides traffic encryption, no data should leave the data center (such as through printing or copy/paste); all user access to the environment should be audited. Regulatory compliance may be required for a portion or all of the data involved.
						 
					
				
			
		

		
			
				Mobility – Mobility requirements are used to help determine the appropriate resource model for each user group. For example, if a user group faces intermittent network connectivity, any model requiring an active network connection is not applicable. There are four classifications available:
			
		

		
			
				
					
						
							Mobility
						 
					
					
						
							Requirement
						 
					
				
				
					
						
							Local
						 
					
					
						
							Always uses the same device, connected to an internal, high-speed, and secured network.
						 
					
				
				
					
						
							Roaming Local
						 
					
					
						
							Connects from different locations on an internal, high-speed, secured network.
						 
					
				
				
					
						
							Remote
						 
					
					
						
							Sometimes, it connects from external variable-speed, unsecured networks.
						 
					
				
				
					
						
							Hybrid
						 
					
					
						
							It connects from an internal location on a secured network and external, unsecured networks. 
						 
					
				
			
		

		
			
				Desktop Loss Criticality – Desktop loss criticality is used to determine the level of high availability, load balancing, and required fault tolerance measures. For example, if there is a high risk to the business if the user’s resource is unavailable, the user should not be allocated a laptop, as if that laptop fails, they will not be able to access their resources. Desktop loss critically should be used to inform recovery point objectives (RPO) and recovery time objectives (RTO). There are three classifications available:
			
		

		
			
				
					
						
							Desktop Loss Criticality
						 
					
					
						
							Requirement
						 
					
				
				
					
						
							Low
						 
					
					
						
							No major risk to products, projects, or revenue.
						 
					
				
				
					
						
							Medium
						 
					
					
						
							Potential risk to products, projects, or revenue.
						 
					
				
				
					
						
							High
						 
					
					
						
							Severe risk to products, projects, or revenue.
						 
					
				
			
		

		
			
				Workload –Types and number of applications accessed by the user impact overall density and the appropriate VDI model. There are three classifications available:
			
		

		
			
				
					
						
							User Type
						 
					
					
						
							Characteristics
						 
					
				
				
					
						
							Task Worker
						 
					
					
						
							1-2 office productivity apps or kiosks.
						 
					
				
				
					
						
							Knowledge Worker
						 
					
					
						
							2-10 office productivity apps with light multimedia use. 
						 
					
				
				
					
						
							Power User
						 
					
					
						
							Advanced applications, data processing, or application development.
						 
					
				
				
					
						
							Graphic-Intensive User
						 
					
					
						
							High-end graphics capabilities, 3D rendering, CAD, and other GPU-intensive tasks.
						 
					
				
			
		

		
			
				Note: 
			 

			
				Performance thresholds are not identified based on processor, memory, or disk utilization because these characteristics will change dramatically following the application rationalization and desktop optimization process. In addition, the user’s management tools and operating system will likely change during migration. Instead, workload is gauged based on the number and type of applications the user runs and the CPU and memory resources needed for the applications.
			 
		

		
			Assign VDI Models
		

		
			As with physical desktops, it is impossible to meet every user requirement with a single type of VDI. Different types of users need different types of resources. Some users may require simplicity and standardization, while others may require high levels of performance and personalization. Implementing a single VDI model across an entire organization will inevitably lead to user frustration and reduced productivity.
		 

		
			Citrix offers a complete set of VDI technologies combined into a single integrated solution. Because each model has different strengths, choosing the right model for each user group within the organization is important.
		 

		
			This list briefly explains each VDI model:
		 

		
			
				Hosted Apps - The hosted apps model delivers only the application interface to the user. This approach provides a seamless way for organizations to deliver a centrally managed and hosted application to the user’s local PC. The Hosted Apps model is often utilized when organizations must simplify the management of a few line-of-business applications. Hosted Apps includes a few variants:

				
					
						Windows Apps – The Windows apps model utilizes a server-based Windows operating system, resulting in many users accessing a single VM model.
					
					
						VM-Hosted Apps—The VM-hosted apps model utilizes a desktop-based Windows operating system, resulting in a single user accessing a single VM. This model is often used to overcome application compatibility challenges with a multi-user operating system, like Windows Server 2016, Windows Server 2019, or Windows Server 2022.
					
					
						Linux Apps – The Linux apps model utilizes a server-based Windows operating system, resulting in many users accessing a single VM model.
					
					
						Browser Apps – The browser apps model utilizes a server-based Windows operating system to deliver an app as a tab within the user’s local, preferred browser. This approach provides a seamless way for organizations to overcome browser compatibility challenges when users want to use their preferred browser (Internet Explorer, Microsoft Edge, Google Chrome, Mozilla Firefox, etc.). Still, the web application is only compatible with a specific browser.
					
				
			
			
				Shared Desktop – The shared desktop model hosts multiple-user desktops from a single, server-based operating system (Windows Server 2016, 2019, 2022, Red Hat, SUSE, and Ubuntu). The shared desktop model provides a low-cost, high-density solution; however, applications must be compatible with a multi-user server-based operating system. In addition, because multiple users share a single operating system instance, users are restricted from performing actions that negatively impact other users, for example, installing applications, changing system settings, and restarting the operating system.
			
			
				Pooled Desktop—The pooled desktop model provides users a random, temporary desktop operating system (Windows 10, Windows 11, Red Hat, SUSE, and Ubuntu). Because each user receives their own instance of an operating system, overall hypervisor density is lower than in the shared desktop model. However, pooled desktops remove the requirement that applications must be multi-user aware and support server-based operating systems.
			
			
				Pooled Desktop + User Personalization Layer (UPL) – The pooled desktop + User Personalization Layer (UPL)  provides all of the benefits of the pooled desktop model and allows you to personalize the individual user layer and preserve users’ data and locally installed applications across sessions. There are some applications that do not work in this context.
			
			
				Pooled Desktop + App Layering User Layer—The pooled desktop + App Layering User Layer provides the pooled desktop benefits and persists each user's profile settings, data, and locally installed applications. Some applications are not supported on the user layer and should not be installed locally. An overview of these applications can be found here.
			
			
				Persistent Desktop – The personal desktop model provides users with a statically assigned, customizable, persistent desktop operating system (Windows 10, Windows 11, Red Hat, SUSE, and Ubuntu). Because each user receives their own instance of an operating system, overall hypervisor density is lower than in the shared desktop model. However, personal desktops remove the requirement that applications must be multi-user aware and support server-based operating systems. Persistent desktops can be used when end users require the ability to install applications and need setting changes to persist after reboot.
			
			
				GPU Desktop – The GPU desktop model provides each user with a hardware-based graphics processing unit (GPU), allowing for higher-definition graphical content.
			
			
				vGPU—The vGPU model allows multiple virtual machines to directly access the graphics processing power of a single physical GPU to render graphics without slowing down the server CPU.
			
			
				Remote PC Access – The remote PC access desktop model provides users with secure remote access to their statically assigned, physical office PC.
			
			
				Web and SaaS application Access - Citrix Secure Private Access with Citrix Enterprise Browser provides employees, contractors, and partners zero trust access to web/SaaS applications from anywhere while maintaining the security posture on unmanaged or managed devices. This enhances the user experience and allows for more flexibility. It also protects corporate data and networks, provides complete visibility and governance, and enforces last-mile security. Additionally, it reduces cost, provides a simplified and unified way to manage secure access and limits complexity.
			
		

		
			Each user group should be compared against the following table to determine which VDI model best matches the overall user group requirements. In many environments, a single user might simultaneously utilize a desktop VDI model and an app VDI model.
		 

		
			
		 
	

	
		
			Citrix Tips for Success
		 

		
			Start with Windows apps, shared, and pooled desktops – As you can see in the VDI capability table above, the Windows apps, hosted shared, and pooled desktop models can be used in most situations. Reducing the number of VDI models and the required persistence VDI will help reduce deployment time, simplify management, and reduce costs (especially with public cloud resources).
		 

		
			Application Compatibility: Testing application compatibility with operating systems and other applications is essential. Some applications are not multi-session aware and, therefore, cannot be used on multi-session OS VDAs. Applications can also be sensitive to other applications if they have conflicting dependencies. 
		 

		
			Perfect match – It may not be possible to select a VDI model that is a perfect match for the user group. For example, you can’t provide users with a desktop that is highly secure and offers complete personalization at the same time. In these situations, select the VDI model that is the closest match to the organization’s highest priorities for the user group.
		 

		
			Desktop loss criticality – Only three VDI models meet the needs of a high desktop loss criticality user group (backup desktops available) – none of which allow for complete personalization. If a high-desktop loss criticality user group also requires the ability to personalize their desktop, they could be provided with a pool of backup desktops (hosted, shared, pooled) and their primary desktop. Although these desktops would not include customizations made to their primary desktop, they would allow users to access core applications.
		 

		
			Consider Operations &amp; Maintenance – The ongoing support of each VDI model should be factored in when deciding on a VDI model. For example, pooled desktops can be rebooted to a known good state, often leading to reduced maintenance versus a persistent desktop, where each desktop is unique. Additionally, solutions such as Microsoft SCCM or Intune must be budgeted for when deploying persistent desktops or Remote PC. 
		 

		
			Review Application Compatibility—Reviewing application compatibility helps detect potential conflicts between new and existing applications and operating systems. Ensuring compatibility at this stage reduces the risk of unexpected deployment-related issues.
		 
	

	
		Step 3: Define the Applications
	

	
		The next step is determining their required applications once the users have been divided into groups. This is a two-step process: 
		 
	 

	
		
			Application rationalization – Removing redundant applications from the inventory captured during the data capture will help simplify the application assessment.
		
		
			Link apps to users—Use the data capture process results to map applications to user groups.
		
	

	
		Application Rationalization
	

	
		The number of applications identified during the inventory is often surprising, even for organizations that believe they have high application control. Consolidating the list of applications can help reduce the complexity and overall time required.
	 

	
		These guidelines will help ensure that your application list is consolidated appropriately:
	 

	
		
			Multiple versions – Different versions of the same application may have been identified during the inventory. There are various reasons, including an inconsistent patching or upgrade process, decentralized application management, limited licenses, and situations where users require specific application versions for compatibility with other applications, macros, and document formats. Work with the application owners to reduce the number of versions required. The leading practice is standardizing on a single version of each application, typically the latest.
		
		
			Non-business applications – Applications not required by the business should be removed from the application inventory to reduce resource requirements and help simplify the overall project. Non-business-related applications are typically found in an application inventory where users can install their own applications, including games, communication clients, screensavers, peripheral software, and media players.
		
		
			Legacy applications – The inventory may identify legacy applications that have since been retired or are no longer required within the business. These applications may not have been removed from the desktops because there is no established process to do so or because there are always more high-priority activities to complete. These applications should be consolidated during the rationalization stage of the application assessment.
		
		
			Management applications—During the desktop virtualization project, the antivirus, application delivery, monitoring, inventory, maintenance, and backup applications will be completely redesigned across the organization and consolidated.
		
	

	
		Application Categorization
	

	
		Each application included in the project should be categorized based on certain criteria, which will help determine the most appropriate way to host and integrate the app. Each application can be installed directly into the image, virtualized in an isolated container and streamed to the desktop (application packaging like App-V, MSIX, etc.), captured in a unique layer, and attached to the virtual machine (Citrix App Layering), installed on a multi-user Citrix Virtual Apps host and deployed as an application (Hosted Windows App), or run locally. Due to the uniqueness of every application, many large-scale deployments simultaneously utilize multiple approaches.
	 

	
		Applications can be categorized as follows:
	 

	
		
			Common – Applications used by most users (greater than 75%).
		
		
			Anomalous – Applications used by a minority of users (less than 75%).
		
		
			Resource Intensive – Applications with high computing requirements like 1+ GB of RAM or more than 50% CPU resources.
		
		
			Technically Challenging – Applications that are complex to set up have extensive dependencies on other applications, or have specialized configurations.
		
	

	
		Application Characterization
	

	
		These characteristics can be identified for each application so that the right application delivery model can be selected during the design phase of the project:
	 

	
		
			Complex—An application should be classified as complex or technically challenging if it is difficult to set up, has extensive dependencies on other applications, or requires a specialized configuration, such as an Electronic Medical Records (EMR) application. Complex applications must be identified during the application assessment because they are not generally appropriate for installation in a pooled/personal desktop model or delivery by application streaming. Delivering complex applications as a hosted app often helps to reduce the complexity of the base desktop image.
		
		
			Demanding – Collecting application resource requirements allows the virtualization infrastructure to be sized and an appropriate application delivery model to be selected. For example, demanding or resource-intensive applications will not be delivered via a pooled/personal desktop model because there is limited control over how users share resources. There are two classifications available in the user assessment worksheet:
			
				
					Resource Intensive – Application requires 2-4GB+ of RAM or averages 50%+ CPU utilization. 
				
				
					None – The application is not resource intensive.
				
			
		
		
			Peripherals—If applications require connectivity with peripheral devices, identify the interface required to make them available when running from a virtual session.
		
		
			Restrictions – Application access may need to be restricted due to insufficient licenses/resources and to protect sensitive data/tools. For example, applications with limited licenses should not be installed on a base image shared with unlicensed users. There are three restricted access categories in the application assessment workbook:
			
				
					No—The application has no restrictions and can be accessed by any user within the organization.
				
				
					User Group – The application may be installed on a multi-user operating system, but only a specific group of users should be provided with an icon.
				
				
					Virtual Machine – The application should only be installed on a virtual machine accessible by authorized users, often because of licensing requirements.
				
			
		
	

	
		Step 4: Define the Project Team
	

	
		Desktop virtualization is a fundamental change that requires close collaboration between various business and technical teams to succeed. For example, the virtualization and desktop teams must work together to ensure that the virtual desktop image meets user needs while also being optimized for the data center or public cloud. Failure to build a cohesive project team with the right roles and skill sets can negatively impact performance, availability, user experience, and supportability while increasing costs and risk.
	 

	
		The following tables identify the business and technical roles required during an enterprise virtual desktop deployment. Although the list may seem large, many of these roles are only required briefly, and a single person may perform multiple roles. The project manager and Citrix architect are considered full-time roles, with other team members being brought in only when required. The project manager role is key to ensuring that the right people are involved in the project at the right time.
	 

	
		Business Roles
	

	
		This table is not meant to be prescriptive but to give examples of the various responsibilities and how they can be split between teams. Most organizations have the same people do multiple of these roles. 
	 

	
		
			
				
					
						Role
					 
				
				
					
						Description
					 
				
				
					
						Example Responsibilities
					 
				
			
			
				
					
						Project Sponsor
					 
				
				
					
						The project sponsor is a senior company executive who recognizes the benefits that desktop virtualization will bring to the business. The chief technology officer (CTO) often performs the role of project sponsor.
					 
				
				
					
						All steps
					 

					
						
							Define key project milestones
						
						
							Track high-level progress against the plan
						
						
							Track expenditure against the budget
						
						
							Manage scope changes
						
						
							Brief steering committee on progress
						
						
							Ensure project teams are in alignment on strategy
						
					
				
			
			
				
					
						Project Manager
					 
				
				
					
						A project manager plans, executes, and oversees the implementation of projects throughout an organization. They ensure projects are completed on time and within budget, and they collaborate with cross-functional teams.
					 
				
				
					
						
							Create and update the project plan
						
						
							Maintain issue and risk register
						
						
							Create weekly project reports and status meetings
						
						
							Maintain project racking
						
						
							Track expenditure against the budget
						
						
							Organize project workshops and meetings
						
						
							Ensure prerequisites are in place
						
					
				
			
			
				
					
						Business Manager
					 
				
				
					
						Depending on company structure and size, business managers oversee planning and performance at a department, region, or company level. A business manager understands the requirements necessary for their employees to be
					 

					
						successful.
					 
				
				
					
						Assess
					 

					
						
							Assist with application consolidation project.
						
						
							Provide details on the connectivity requirements of the user group, including offline usage
						
						
							Provide details on the risk tolerance of the user group
						
						
							Identify requirements for peripherals
						
					

					
						Deploy
					 

					
						
							Promote the benefits of desktop virtualization
						
						
							Assist with coordinating the rollout
						
					
				
			
			
				
					
						Business Continuity Manager
					 
				
				
					
						The business continuity manager ensures that an organization can continue functioning after a disruptive event such as a natural disaster, crime, or human/computer error.
					 
				
				
					
						Assess
					 

					
						
							Provide Citrix architect with a detailed understanding of the current business continuity plan.
						
					

					
						Design
					 

					
						
							Update the business continuity plan to incorporate the new Citrix infrastructure.
						
					

					
						Deploy
					 

					
						
							Test business continuity plan
						
					
				
			
			
				
					
						Test Manager
					 
				
				
					
						The test manager is responsible for ensuring that the test and user acceptance environments match the production environment as closely as
					 

					
						possible. The test manager helps to reduce risk by ensuring that changes are fully tested before being implemented in production.
					 
				
				
					
						Assess
					 

					
						
							Provide Citrix architect with a detailed understanding of current testing infrastructure and processes.
						
					

					
						Design
					 

					
						
							Work with Citrix architect to design an appropriate testing infrastructure and test plan for the new Citrix environment.
						
					

					
						Deploy
					 

					
						
							Ensure that testing design is implemented correctly and new Citrix infrastructure is fully tested before rollout
						
					
				
			
			
				
					
						Application owners
					 
				
				
					
						An application owner is a subject matter expert on specific applications deployed within the
					 

					
						business. Application owners ensure that application problems are resolved and upgrades/updates
					 

					
						are performed without issue. Application owners are also responsible for managing support agreements with the application vendors.
					 
				
				
					
						Assess
					 

					
						
							Assist with application consolidation project
						
						
							Identify application licensing limitations
						
						
							Provide details on security restrictions
						
						
							Provide details on application dependencies
						
						
							Provide location of backend resources
						
					

					
						Deploy
					 

					
						
							Provide installation prerequisites and install guide
						
						
							Assist Citrix team with installing and testing applications in the VDI environment
						
					
				
			
			
				
					
						Service desk manager
					 
				
				
					
						The service desk manager helps improve productivity and end-user satisfaction by ensuring that production issues are logged, escalated, and resolved in a timely manner. The service desk manager is also responsible for reporting on common issues, call volumes, and service desk performance.
					 

					
						The service desk manager also ensures that support staff is trained in the technology.
					 
				
				
					
						Assess
					 

					
						
							Identify common issues with the existing environment
						
						
							Provide details on support tools currently used
						
						
							Determine the current skill set for support staff and end users
						
					

					
						Design
					 

					
						
							Assist Citrix architect with designing a delegated administration model
						
						
							Participate in operations and support design workshops
						
						
							Work with the training manager to identify training requirements
						
						
							Create a training plan for support staff and end users
						
					

					
						Deploy
					 

					
						
							Monitor help desk calls for rollout-related issues
						
						
							Implement a training plan for support staff and end users
						
					
				
			
			
				
					
						Communications Manager
					 
				
				
					
						The communication manager is responsible for disseminating key information throughout the organization.
					 
				
				
					
						Design
					 

					
						
							Work with the project manager to create a communications plan
						
					

					
						Deploy
					 

					
						
							Relay benefits of desktop virtualization
						
						
							Inform users of key migration dates
						
						
							Ensure expectations are set accordingly
						
					
				
			
		
	

	
		Technical Roles
	

	
		This table is not meant to be prescriptive but to give examples of the various responsibilities and how they can be split between teams. Most organizations have the same people do multiple of these roles. 
	 

	
		
			
				
					
						Role
					 
				
				
					
						Description
					 
				
				
					
						Example Responsibilities
					 
				
			
			
				
					
						Citrix Architect
					 
				
				
					
						The Citrix architect acts as the design authority for all Citrix products and
					 

					
						liaises with other architects to ensure the Citrix infrastructure is successfully integrated into the organization.
					 
				
				
					
						Assess
					 

					
						
							Work with the project sponsor and key stakeholders to identify and prioritize key business drivers.
						
						
							Oversee user segmentation and app assessment
						
						
							Map VDI models to user groups
						
						
							Perform capabilities assessment to determine the current state of readiness.
						
						
							Identify areas of risk and provide remedial actions
						
					

					
						Design
					 

					
						
							Create a Citrix design that includes hardware and storage estimates
						
						
							Coordinate with other architects to integrate Citrix infrastructure into the organization
						
						
							Work with monitoring architect to ensure that Citrix environment is monitored appropriately
						
						
							Create operations and support design
						
						
							Create implementation and rollout design
						
						
							Create test plan
						
					

					
						Deploy
					 

					
						
							Ensure that the Citrix environment is implemented in accordance with the design
						
						
							Verify that implementation passes the test plan
						
						
							Creates change control requests
						
						
							Ensure that the Citrix design is implemented correctly
						
					
				
			
			
				
					
						Active Directory architect
					 
				
				
					
						Design authority on Microsoft Active Directory, including Organizational Units (OU) and Group Policy Objects (GPOs).
					 
				
				
					
						Assess
					 

					
						
							Provide Citrix architect with a detailed understanding of current Active Directory architecture.
						
					

					
						Design
					 

					
						
							Work with the Citrix architect to design OU structure, group policies, permissions, service accounts, etc. for the new Citrix environment
						
						
							Update Active Directory infrastructure design to reflect the centralization of user data and accounts
						
					

					
						Deploy
					 

					
						
							Ensure that the Active Directory design is implemented correctly
						
					
				
			
			
				
					
						Cloud/Virtualization Architect
					 
				
				
					
						Design authority on the server and desktop virtualization using Citrix XenServer, Microsoft Hyper-V, Nutanix Acropolis, or VMware vSphere.
					 
				
				
					
						Assess
					 

					
						
							Provide Citrix architect with a detailed understanding of current virtualization architecture.
						
					

					
						Design
					 

					
						
							Work with Citrix architect to design hardware, networking, storage, high availability, etc. for server and desktop virtualization
						
						
							Work with the monitoring architect to ensure that the virtualization environment is monitored appropriately
						
					

					
						Deploy
					 

					
						
							Ensure that the virtualization design is implemented correctly
						
					
				
			
			
				
					
						Network Architect
					 
				
				
					
						Design authority on networking, including routing, VLANs, DHCP, DNS, VPN, and firewalls.
					 
				
				
					
						Assess
					 

					
						
							Provide Citrix architect with a detailed understanding of current networking architecture.
						
					

					
						Design
					 

					
						
							Work with Citrix architect to design a physical network, virtual networks, routing, firewalls, quality of service, remote access, network optimization, etc., for the new Citrix environment
						
						
							Work with the monitoring architect to ensure that the network is monitored appropriately
						
					

					
						Deploy
					 

					
						
							Ensure that network design is implemented correctly
						
					
				
			
			
				
					
						Desktop Architect
					 
				
				
					
						If there is no separate desktop team, design authority on Microsoft desktop operating systems often falls under the Citrix architect.
					 
				
				
					
						Assess
					 

					
						
							Provide Citrix architect with a detailed understanding of the current desktop environment.
						
					

					
						Design
					 

					
						
							Work with Citrix architect to design core desktop virtual image, core applications, desktop optimizations, etc. for new Citrix environment
						
						
							Work with monitoring architect to ensure that the virtual desktops are monitored appropriately
						
					

					
						Deploy
					 

					
						
							Ensure that desktop design is implemented correctly
						
					
				
			
			
				
					
						Storage Architect
					 
				
				
					
						Design authority on storage solutions, including direct-attached
					 

					
						storage, storage-attached networks, and network-attached storage.
					 
				
				
					
						Assess
					 

					
						
							Provide Citrix architect with a detailed understanding of the current shared storage environment.
						
					

					
						Design
					 

					
						
							Work with Citrix architect to design storage architecture, tiers, sizing, connectivity, etc., for the new Citrix environment
						
						
							Work with the monitoring architect to ensure that storage is monitored appropriately
						
					

					
						Deploy
					 

					
						
							Ensure that storage design is implemented correctly
						
					
				
			
			
				
					
						Backup Architect
					 
				
				
					
						Design authority on backup and recovery, including virtual machines, desktops, servers, user data, and databases.
					 
				
				
					
						Assess
					 

					
						
							Provide Citrix architect with a detailed understanding of current backup architecture and processes.
						
					

					
						Design
					 

					
						
							Work with Citrix architect and disaster recovery architect to design backup architecture, process, schedule, retention, etc., for the new Citrix environment
						
					

					
						Deploy
					 

					
						
							Ensure that the backup design is implemented correctly
						
					
				
			
			
				
					
						Application packaging architect
					 
				
				
					
						Design authority on packaging applications for deployment via the systems management team.
					 
				
				
					
						Assess
					 

					
						
							Provide Citrix architect with a detailed understanding of the current application packaging process and status
						
					

					
						Deploy
					 

					
						
							Ensure that all required applications are packaged according to design
						
					
				
			
			
				
					
						Monitoring architect
					 
				
				
					
						Design authority on monitoring, including hardware, network, servers, storage, and security appliances.
					 
				
				
					
						Assess
					 

					
						
							Provide Citrix architect with a detailed understanding of current monitoring architecture and processes.
						
					

					
						Design
					 

					
						
							Work with Citrix architect to design monitoring architecture, metrics, alerts, etc., for new Citrix environment and supporting infrastructure
						
					

					
						Deploy
					 

					
						
							Ensure that the monitoring design is implemented correctly
						
						
							Provide regular reports on capacity and trends during the rollout 
						
					
				
			
			
				
					
						Systems management architect
					 
				
				
					
						Design authority on systems management, including server/desktop build process, patching, and automated application installation.
					 
				
				
					
						Assess
					 

					
						
							Provide Citrix architect with a detailed understanding of the current systems management processes.
						
					

					
						Design
					 

					
						
							Works with Citrix architect to define server/desktop build process, patching, and application delivery strategy for new Citrix environment
						
					

					
						Deploy
					 

					
						
							Ensure that the systems management design is implemented correctly
						
					
				
			
			
				
					
						Security Architect
					 
				
				
					
						Design authority on security, including desktops, servers, networks, and VPNs.
					 
				
				
					
						Assess
					 

					
						
							Provide Citrix architect with a detailed understanding of current security policy.
						
					

					
						Design
					 

					
						
							Work with Citrix architects to design security standards for the new Citrix environment, including authentication, encryption, port numbers, firewall rules, etc.
						
					

					
						Deploy
					 

					
						
							Ensure that security design is implemented correctly.
						
					
				
			
		
	

	
		 
	 

	
		Return to Citrix VDI Handbook Home                                                                                                                                                                         Next Section &gt; Design]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_06/image.png.bdfb5fbcf21ce4e2918ccf90d444d52f.png" length="21469" type="image/png"/><pubDate>Mon, 01 Jul 2024 13:01:14 +0000</pubDate></item><item><title>Citrix VDI Handbook</title><link>https://community.stage.citrix.com/tech-zone/build/tech-papers/citrix-vdi-handbook/</link><description>Welcome to the Citrix VDI Handbook. This guide is a collaborative effort between the Citrix Product Marketing, Product Management, and Professional Services teams and is designed to give you an in-depth understanding of Citrix Infrastructure, a pivotal technology in modern enterprise IT environments. Citrix solutions empower organizations to deliver secure, virtualized desktops and applications to users anywhere, on any device, ensuring a seamless and productive user experience while simplifying IT management and enhancing security.
 


	In this handbook, we will explore the fundamentals of Citrix, including its architecture, key features, deployment strategies, and leading practices. Whether you are a seasoned IT professional looking to optimize your current VDI implementation or a newcomer aiming to understand the benefits and intricacies of Citrix solutions, this handbook is an essential resource to help you navigate the complexities of virtual desktop and application delivery.
 


	The Citrix VDI Handbook offers detailed information and best practices for deploying and managing virtual desktop infrastructure (VDI) using Citrix technologies. It is a valuable resource for IT professionals, architects, and administrators responsible for designing, implementing, and maintaining Citrix-based environments. The Citrix VDI Handbook follows the Citrix Professional Services methodology, which has been successfully employed across thousands of Citrix virtualization projects and consists of five phases: 
	 
 


	 
	
 


	 
 


	
		Define: Builds the business case for virtualization by creating a high-level project roadmap, prioritizing activities, and estimating storage and hardware requirements.
	
	
		Assess: Key business drivers are rated to prioritize work efforts accordingly. In addition, the current environment is reviewed for potential problems and to identify use cases for the project. This information will set the direction for Citrix deployment, upgrade, or expansion.
	
	
		Design: Define the architecture required to satisfy key business drivers and success criteria identified during the assessment phase. Topics such as environment scalability, redundancy, and high availability are addressed.
	
	
		Deploy: During the deployment phase, the infrastructure is installed and configured as described in the design phase. Before users can access the environment, all infrastructure components should be thoroughly unit and regression-tested.
	
	
		Monitor: Define architectural and operational processes required to maintain the production environment.
	



	The Citrix VDI Handbook is provided here as a full PDF document but is also available online by section via the links below.
 


	Citrix VDI Handbook -2402 LTSR.pdf
 


	 
 


	
		The Citrix VDI Handbook provides content on the Assess, Design, and Monitor phases. Deployment information can be found in Citrix Product Documentation and Citrix Tech Zone.
	 



	Assess
 


	
		
			Define the Organization
		 
	
	
		
			Define the User Groups
		 
	
	
		
			Define the Applications
		 
	
	
		
			Define the Project Team
		 
	



	Design
 


	
		
			Conceptual Architecture
		 
	
	
		
			Layer 0: The Business Layer
		 
	
	
		
			Layer 1: The User Layer
		 
	
	
		
			Layer 2: The Access Layer
		 
	
	
		
			Layer 3: The Resource Layer
		 
	
	
		
			Layer 4: The Control Layer
		 
	
	
		
			Layer 5: The Compute Layer
		 
	



	Monitor
 


	
		Process 1: Support
	
	
		Process 2: Operations
	
	
		Process 3: Monitoring
	



	 
 


	
		Disclaimer
	 

	
		EXCEPT WHERE EXPRESSLY PROVIDED OTHERWISE BY CLOUD SOFTWARE GROUP, THE CONTENT IN THIS DOCUMENT IS PROVIDED &#x201C;AS IS&#x201D; AND CLOUD SOFTWARE GROUP HEREBY DISCLAIMS ALL EXPRESS OR IMPLIED REPRESENTATIONS, WARRANTIES, GUARANTIES, AND CONDITIONS, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES OR CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD TO BE LEGALLY INVALID. CLOUD SOFTWARE GROUP MAKES NO REPRESENTATIONS, WARRANTIES, GUARANTIES, OR CONDITIONS AS TO THE QUALITY, SUITABILITY, TRUTH, ACCURACY, OR COMPLETENESS OF ANY OF THE CONTENT CONTAINED ON THE WEBSITE.
	 

	
		The above disclaimer applies to any damages, liability, or injuries caused by any failure of performance, error, omission, interruption, deletion, defect, delay in operation or transmission, computer virus, communication line failure, theft or destruction of or unauthorized access to, alteration of, or use, whether for breach of contract, tort, negligence or any other cause of action.</description><pubDate>Mon, 01 Jul 2024 13:01:00 +0000</pubDate></item><item><title>Citrix VDI Handbook - Design</title><link>https://community.stage.citrix.com/tech-zone/build/tech-papers/citrix-vdi-handbook-design/</link><description><![CDATA[Design



	Designing a virtualization solution follows a proven process and aligns technical decisions with organizational and user requirements. Architects randomly jump from topic to topic without a standardized and proven process, leading to confusion and mistakes. The recommended approach focuses on working through five distinct layers:
 


	
 


	The top three layers, identified during the assessment phase's user segmentation section, are designed independently for each user group alongside the business layer. These layers define the users’ resources and how they access them. After completing these three layers, the foundational layers (control, compute, and operations) are designed for the entire solution.
 


	This process guides design thinking in that decisions made higher up impact lower-level design decisions.
 


	Conceptual Architecture



	The conceptual architecture helps define the overarching strategies for the solution based on business objectives and organizational structure.
 


	Although an organization’s conceptual architecture will change over time, it is worthwhile to start the design phase by defining the long-term objectives around delivery models and the physical geographical distribution of the solution.
 


	Decision: Delivery Model



	A Citrix solution can take on many delivery forms. The organization’s business objectives help select the right approach.
 


	The local IT team's management scope changes even though the infrastructure remains the same for all delivery models.
 


	
		On-premises: All components are hosted from the organization’s local data center. The on-premises model requires the local IT team to manage every aspect of the solution.
	



	
 


	
		Public Cloud - All components are hosted using Infrastructure as a Service (IaaS) from a public cloud infrastructure. The public cloud model eliminates hardware management from the local IT team‘s management scope.
	



	
 


	
		Hybrid Cloud - A single implementation executes from an on-premises data center and the public cloud. Even though components of the solution are using different hosting providers, the entire solution is a single solution using the same code and managed as a single entity. The local IT team continues to manage all aspects of the solution except for the hardware associated with the cloud-hosted resources.
	



	
 


	
		Citrix Cloud - The Citrix DaaS offering from Citrix Cloud breaks a typical deployment into multiple management domains. Citrix hosts and manages the access and control layer components in the Citrix Cloud. In contrast, the local IT team manages the resource layer components as an on-premises, public cloud, or hybrid cloud model. Citrix manages the hardware, sizing, and updates to the access and control components, while the local IT team manages the resources. In addition, if the public cloud hosts the resources, the local IT team does not have to manage the resource hardware.
	



	
 


	
		Note: 
	 

	
		Citrix Universal Hybrid Multi-Cloud or the Citrix Platform License must use the Public and Hybrid Cloud options. Citrix for Private Cloud licenses only support Citrix environments deployed fully on-premises.
	 



	Decision: Site Topology



	A Citrix Virtual Apps and Desktops or Citrix DaaS site groups desktops and applications together to form a single architectural and management entity. The site's persistent and dynamic data, including site configuration, desktop assignments, and session state, is stored in the site’s database. 
 


	A Citrix DaaS site is a cloud service that provides app and desktop virtualization. It gives IT control of on-prem or cloud-hosted virtual machines, applications, and security while providing anywhere access for any device. End users can use applications and desktops independently of the device’s operating system and interface. It is important to note that your cloud tenant is one site in Citrix DaaS. 
 


	 A site can be contained within a single location, span across multiple locations, or be a partial location. Through rigorous testing, a single Citrix Virtual Apps and Desktops or Citrix DaaS site can support 40,000 concurrent sessions.
 


	
 


	Zones (or Resource Locations in Citrix DaaS) subdivide a single site, often associated with geographical locations. There are several factors to consider when determining the overall topology of the Citrix Virtual Apps and Desktops and Citrix DaaS solution:
 


	
		Risk Tolerance – Create multiple sites to minimize the impact of a site-wide outage. For example, corruption of the site database could affect site-wide availability. For many organizations, the decreased risk from implementing multiple sites outweighs the additional management overhead and supporting infrastructure required. In Citrix DaaS, this would be done by having multiple Resource Locations (aka Zones).
	



	
		Design Tip
	 

	
		For resiliency reasons, a single zone should be limited to 10,000 VDAs.
	 



	 
 


	
		Security – Although delegated administration is available, high-security organizations may require complete separation between environments to demonstrate compliance with specific service-level agreements.
	



	
		Design Tip
	 

	
		If your organization requires complete separation for employees responsible for managing financial data, create two Citrix Virtual Apps and Desktops sites within the same datacenter - one for the financial employees and the second for all other employees.
	 



	 
 


	
		Administrative Boundaries – Due to billing/chargeback requirements or how IT is structured, multiple sites might be required to separate administrative boundaries.
	
	
		Geographical Connectivity—Although the zone implementation allows a single site to span geographical locations, there must be enough bandwidth between the satellite and primary zones for the site database to capture the session information. Higher latency or more significant zones impact the user's response times.
	



	
		
			
				
					CVAD 2402 LTSR 
				 
			
		
		
			
				
					Latency
				 
			
			
				
					45
				 
			
			
				
					90
				 
			
			
				
					160
				 
			
		
		
			
				
					Concurrent Requests
				 
			
			
				
					60
				 
			
			
				
					60
				 
			
			
				
					60
				 
			
		
		
			
				
					Avg Response Time (s)
				 
			
			
				
					7.3
				 
			
			
				
					16.2
				 
			
			
				
					28
				 
			
		
		
			
				
					Brokering requests per second
				 
			
			
				
					8.2
				 
			
			
				
					3.7
				 
			
			
				
					2.1
				 
			
		
		
			
				
					Time to launch 10k users
				 
			
			
				
					20m 19s
				 
			
			
				
					45m 04s
				 
			
			
				
					N/A
				 
			
		
	



	 
 


	
		
			
				
					Session Count 
				 
			
			
				
					Max concurrent session launches
				 
			
			
				
					Min site-to-site Bandwidth
				 
			
			
				
					Max site-to-site Round Trip Latency
				 
			
		
		
			
				
					Less than 50
				 
			
			
				
					20
				 
			
			
				
					1 Mbps
				 
			
			
				
					250ms
				 
			
		
		
			
				
					50 to 500
				 
			
			
				
					25
				 
			
			
				
					1.5 Mbps
				 
			
			
				
					100ms
				 
			
		
		
			
				
					500 to 1000
				 
			
			
				
					30
				 
			
			
				
					2 Mbps
				 
			
			
				
					50ms
				 
			
		
		
			
				
					1000 to 3000
				 
			
			
				
					60
				 
			
			
				
					8 Mbps
				 
			
			
				
					10ms
				 
			
		
		
			
				
					3000+
				 
			
			
				
					60
				 
			
			
				
					8 Mbps
				 
			
			
				
					5ms
				 
			
		
	



	Administrators should minimize the number of sites and zones to reduce architectural complexity and administrative effort.
 


	Decision: Image Management Strategy



	A Citrix Virtual Apps and Desktops solution requires creating and managing master image(s). Organizations must decide early what strategy to pursue for image management.
 


	Installed Images
 


	An installed image requires the administrator to install each image's operating system and applications. This approach is straightforward, but duplicates effort as the admin may install the same operating system and core applications across multiple master images.
 


	Maintaining master images also includes duplication of effort as the same operating system version and core applications are part of multiple images, each requiring the same update process.
 


	Scripted Images
 


	Administrators can automate many tasks associated with installed images with scripting or automation tools. Many operating systems and application installs support automated scripting, which mitigates the duplication of effort impact on the administrator’s time with installed images.
 


	Infrastructure as Code (IaC) deployments offer several advantages, such as improved scalability, reproducibility, and automation. Using code instead of manual processes to create and update images, administrators can reproduce images quickly at scale with reduced risk of human error. However, IaC deployments do require coding or development skill sets and also introduce an additional layer of complexity. 
 


	App Layering Layered Images
 


	Each unique operating system (Windows 11, Windows Server 2022), platform (Citrix Virtual Apps and Desktops 2402 VDA, Citrix Virtual Apps and Desktops 2403 VDA), and application (Microsoft Office, Adobe Acrobat, Google Chrome, and Mozilla Firefox) is a layer. A master image merges one operating system layer, one platform layer, and potentially several application layers.
 


	A layered image approach eliminates the duplication of effort challenges associated with installed and scripted images. Each unique layer is available to any master image. When an application requires an update, that layer receives the updates, and all master images utilizing the layer receive the update. If an organization requires ten unique Windows 11 images, each of the ten images shares the same Windows 11 OS layer. When the administrator needs to upgrade the VDA from version 2212 to 2402 across ten images, the administrator only updates a single platform layer.
 


	Initially, the layered image approach does require more time to deploy because the administrator must build the organization’s library of layers. However, once the layers are available, the time to maintain the images is drastically reduced. App Layering represents a significant change in how images are deployed and maintained compared to traditional image management methods. These changes must be coordinated with other teams, such as SCCM/Intune, Windows Server, etc., as the changes will be handled differently than in the rest of the org.
 


	Decision: Disaster Recovery



	High-level disaster recovery (DR) plans should be made before a Citrix environment design is completed. While plans and details can be subject to change, the business's DR needs will impact the overall site design.
 


	The main drivers of DR design are Recovery Time Objectives (RTO) and Recovery Point Objectives (RPO). These objectives govern how fast you need to be able to bring an environment back online in the event of an outage (RTO) and how much data the users can tolerate losing(RPO). These requirements often vary by use case, so it is important to assign tier classifications to the resources in the environment. Generally speaking, the shorter the RTO and RPO, the more expensive the DR solution. 
 


	Generally speaking, DR topology has two main options: active/active and active/passive. In an active/active deployment, users use both/all the datacenters actively, and if one site goes down, they failover to another datacenter. In an active/passive deployment, users only access their main datacenter for normal operations and failover to a (normally offline) datacenter in a DR event. For both options, there are considerations for the access, resource, control, and compute layers. 
 


	Layer 0: The Business Layer



	The business layer describes the reasons for the Citrix environment. This includes business drivers, such as reducing total ownership cost, enabling hybrid work, consolidating vendors, protecting sensitive information such as Intellectual Property from leaving the corporate network,  improving security for contractors, enabling you to bring your own device policies, etc. Technical objectives should also be discussed at this stage. Examples of technical objectives include centralizing image management and implementing a specific feature such as App Protection for contractors. Another key technical objective is to identify the pain points in the existing Citrix environment (if one exists) and what improvements need to be made.
 


	In this layer, it is also important to identify future growth opportunities, such as mergers and acquisitions or natural business growth, as these can impact the environment's design and scalability.
 


	All subsequent design decisions should incorporate these business and technical drivers. 
 


	Layer 1: The User Layer



	The top layer of the design methodology is the user layer, which each unique user group defines.
 


	The user layer appropriately sets the overall direction for each user group’s environment. This layer incorporates the assessment criteria for business priorities and user group requirements to define effective strategies for endpoints and the Citrix Workspace app. These design decisions affect each user group's flexibility and functionality.
 


	Endpoint Devices



	There are several endpoint devices that may be used with the Citrix environment, all with differing capabilities, including:
 


	
		Tablet-based
	
	
		Laptop
	
	
		Desktop PC
	
	
		Thin client
	
	
		Kiosks or shared workstations
	
	
		Smartphones
	



	The Citrix environment and client apps must be designed with endpoints in mind. End users may be using multiple endpoint devices and different types, and endpoints often vary by user group and workload. 
 


	Decision: Endpoint Ownership



	In many organizations, endpoint devices are corporate-owned and managed. However, many organizations have bring-your-own-device (BYOD) programs to improve employee satisfaction, reduce costs, and simplify device management. Even if BYOD is a business priority, it does not mean that every user should be allowed to use a personal device in the corporate environment.
 


	Certain user requirements identified during user segmentation can greatly impact the suitability of personal devices:
 


	
		Security – Users requiring a high level of security might be unable to bring a personal device into the secured environment to risk data theft.
	
	
		Unmanaged devices - 
		
			
				Bring your own devices (BYOD) - Companies often allow employees to use their own devices to access work resources. This reduces the management overhead of procuring and managing the endpoints. However, it is recommended to issue minimum requirements when using a BYOD program to reduce the number of issues potentially encountered by end users (for example, minimum CWA version, OS versions, and/or browser versions for accessing the Citrix environment) 
			
			
				Third-party users—Often, users accessing the environment who are not directly employed by the company are not directly employed by the company. These users can include third-party contractors or outsourced employees using computers managed by their companies. This reduces the company's control over these users' devices. Similar to BYOD, it is also recommended that these use cases issue minimum requirements to reduce support tickets.
			
		
	



	Decision: Endpoint Lifecycle



	Organizations may repurpose devices to extend refresh cycles or provide overflow capacity for contract workers. Many endpoints can offer more capabilities, giving them longer useful lifespans. In many cases, these hardware capabilities vastly outstrip the needs of a typical user. When coupled with the ability to virtualize application and desktop workloads, this provides new options to administrators, such as repurposing existing workstations. These options go well beyond the simple three-year PC refresh cycle. However, the benefits of repurposing or reallocating a workstation should be balanced against the following considerations.
 


	
		Minimum standards – While cost factors of repurposing existing workstations may be compelling, certain minimum standards should be met to guarantee a good user experience. At a minimum, it is recommended that repurposed workstations have a 2GHz processor, 4 GB of RAM, and 30 GB of free disk space.
	
	
		Business drivers—Priorities underpin the success of any major project. Organizations that have prioritized reducing capital expenditure by prolonging the hardware refresh cycle can benefit from repurposing hardware. Conversely, if an organization’s business drivers include reducing power consumption as part of an overall sustainability initiative, purchasing newer endpoints may be beneficial to take advantage of the latest power management capabilities available in the most modern devices.
	
	
		Workload – The type of work and VDI model for an end user can determine whether they are a good candidate for a repurposed endpoint or may be better served with a new device. Suppose the work performed by the individual involves locally installed applications. In that case, the individual may be best served by a new endpoint that offers the most powerful and recently updated processor and graphics architecture. However, if a user is largely performing tasks associated with virtualized applications that do not involve the latest multimedia capabilities, such as webcams, VoIP, and media redirection, then a repurposed workstation should be a viable alternative.
	



	This planning matrix outlines the considerations when repurposing existing hardware:
 


	
		
			
				
					Endpoint Provisioning Criteria
				 
			
			
				
					Repurpose Existing
				 
			
			
				
					Procure New
				 
			
		
		
			
				
					Capital restrained environment
				 
			
			
				
					X
				 
			
			
				 
			
		
		
			
				
					High number of virtualized applications
				 
			
			
				
					X
				 
			
			
				 
			
		
		
			
				
					Desire to prolong hardware refresh cycle
				 
			
			
				
					X
				 
			
			
				 
			
		
		
			
				
					High failure rate among existing desktops
				 
			
			
				 
			
			
				
					X
				 
			
		
		
			
				
					Outmoded client-side feature set 
				 
			
			
				 
			
			
				
					X
				 
			
		
		
			
				
					Power consumption or sustainability initiative
				 
			
			
				 
			
			
				
					X
				 
			
		
	



	Decision: Unified Endpoint Management (UEM)
 


	VDI allows users to work on any device from any location while still getting access to their apps and data. With distributed users accessing the environment across multiple devices, including mobile devices, administrators need to be able to secure and support the mobile devices centrally. 
 


	Administrators need to:
 


	
		Selectively wipe a device if lost, stolen, or non-compliant.
	
	
		Require passcode security standards.
	
	
		Define WiFi parameters for office locations.
	
	
		Integrate certificates to secure communications.
	
	
		Configure conditional access based on security guidelines. 
	



	Managing the distributed endpoints is only part of the challenge. Administrators need to define levels of access. Administrators need to secure and control access to the apps and data. Security becomes more concerned when users access corporate virtual apps and desktop resources from personal devices. A few things to consider when delivering Citrix Virtual Apps and Desktop apps to mobile devices:
 


	
		What resources can a jailbroken device access?
	
	
		Can users copy/paste between personal apps and Citrix Virtual Apps and Desktops apps?
	
	
		Can a device with no configured passcode get access to corporate resources?
	
	
		Can users continue to use a native or untrusted third-party email client?
	
	
		Can mobile device users access Intranet sites with a browser optimized for mobile devices or a secure enterprise browser?
	



	Unified Endpoint Management (UEM) solutions, like Citrix Endpoint Management and XenMobile, protect app data and let admins control app data sharing. UEM also allows for managing corporate data and resources separately from personal data.
 


	Decision: Mobile Device Management (MDM)
 


	VDI allows users to work on any device from any location while still getting access to their apps and data. With distributed users accessing the environment across multiple devices, including mobile devices, administrators need to be able to centrally secure and support the mobile devices, known as Mobile Device Management (MDM).
 


	MDM solutions, like Citrix Endpoint Management and XenMobile, enable organizations to protect devices and data on devices at a system level. For example:
 


	
		Selectively wipe a device if the device is lost, stolen, or out of compliance.
	
	
		Require passcode security standards
	
	
		Define geo-location device restrictions
	
	
		Define Wi-Fi parameters for office locations
	
	
		Integrate certificates to secure communications
	



	MDM is typically suitable for corporate-owned mobile devices because most users with personal devices do not want to give the IT team that much control over their personal devices.
 


	Decision: Mobile Application Management (MAM)



	With a distributed workforce accessing the Citrix Virtual Apps and Desktops environment across numerous devices, administrators must secure and control access to the apps and data. Security becomes a greater concern when users access corporate resources from personal devices. 
 


	A few things to consider when delivering Citrix Virtual Apps and Desktops apps to mobile devices:
 


	
		What resources can a jailbroken device access?
	
	
		Can users copy/paste between personal apps and Citrix Virtual Apps and Desktops apps?
	
	
		Can a device with no configured passcode get access to corporate resources?
	
	
		Can users continue to use a native or untrusted third-party email client?
	
	
		Can mobile device users access Intranet sites with a browser optimized for mobile devices or with a published desktop browser?
	



	MAM solutions, like Citrix Endpoint Management and XenMobile, protect app data and let admins control app data sharing. MAM also allows for managing corporate data and resources separately from personal data.
 


	MAM is often suitable for bring-your-own (BYO) devices because, although the device is unmanaged, corporate data remains protected. MAM can also be used for the following scenarios:
 


	
		Selectively wipe a device if the device is lost, stolen, or out of compliance without impacting the personal data
	
	
		Protect corporate app data with app data sharing controls. 
	
	
		Enable intranet site access seamlessly with per-app VPN functionality (Citrix microVPN) to Citrix mobile productivity and in-house apps
	
	
		Block access to MAM-enabled apps if a device is rooted or jailbroken
	



	Decision: Endpoint Form Factor



	The endpoints' capabilities and efficiencies offered in thin client form factors have grown. Even mid-range thin clients now have graphics capabilities that utilize HDX features such as multi-monitor support and Microsoft Teams Optimization while offering management and power efficiency benefits. It is important to note that thin clients support different workload levels (basic use cases vs graphics-intensive workloads). Be sure to discuss the requirements with your thin client vendor of choice.
 


	Most organizations will likely deploy a mixture of fully featured and thin clients. However, as explained in this table, certain endpoint devices are more appropriate when combined with certain VDI models.
 


	
		
			
				
					VDI Model
				 
			
			
				
					Thin Client
				 
			
			
				
					Desktop PC
				 
			
			
				
					Laptop
				 
			
			
				
					Tablet
				 
			
			
				
					Smartphone
				 
			
		
		
			
				
					Hosted Windows Apps
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
		
		
			
				
					Hosted Shared Desktop
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
			
				
					⭘
				 
			
			
				
					⭘
				 
			
		
		
			
				
					Hosted Pooled Desktop
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
			
				
					⭘
				 
			
			
				
					⭘
				 
			
		
		
			
				
					Hosted Static Desktop
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
			
				
					⭘
				 
			
			
				
					⭘
				 
			
		
		
			
				
					Hosted 3DPro Graphics Desktop
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
			
				
					⭘
				 
			
			
				
					X
				 
			
			
				
					X
				 
			
		
		
			
				
					Remote PC Access
				 
			
			
				
					X
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
			
				
					⭘
				 
			
			
				
					⭘
				 
			
		
	



	✔: Recommended / X: Not Recommended / ⭘: Viable
 


	Citrix Workspace app Selection



	The Citrix Workspace app is an easy-to-install software client that provides easy and secure access to applications and desktops from any device, including smartphones, tablets, PCs, Macs, Linux, and macOS.
 


	This section provides a series of design decisions to consider when deploying the Citrix Workspace app.
 


	Decision: Citrix Workspace app Type



	Citrix Workspace app is available as both a Long Term Service Release (LTSR) or Current Release (CR) for Windows and CR for other platforms (including Mac, Linux, ChromeOS, HTML5, Android, and iOS). The release timelines for each platform vary and should be considered when deciding which version of the Citrix Workspace app to deploy. It is important to recognize that there are certain differences between editions, and each user group may require a different Workspace app depending on their need. For the latest feature matrix, please refer here.
 


	Decision: Initial Deployment



	Several deployment options are available to deliver the Citrix Workspace app to an endpoint. Although it is usually a best practice to have a full version of the Citrix Workspace app deployed to an endpoint to provide the greatest level of functionality, it is important to consider fallback options such as the Citrix Workspace app for HTML5 for those situations where the installation of Citrix Workspace app is not possible. Note that although the Citrix Workspace app for HTML5 can be used as a fallback option, it is not recommended for general use due to the limited feature set and common browser restrictions around unsecured WebSockets connections (see CTX134123 for more information).
 


	These are some of the mechanisms commonly used to deploy and update the Citrix Workspace app:
 


	
		StoreFront – If Citrix StoreFront is available, administrators can deploy the Citrix Workspace app via a Receiverfor web site by enabling the “Client Detection” feature. When deployed, a Receiver for web site enables users to access StoreFront stores through a web page. If the Receiver for Web site detects that a user does not have a compatible version of the Citrix Workspace app, the user is prompted to download and install the Citrix Workspace app. The Workspace app clients can be hosted on the StoreFront server. In this case, admins are responsible for keeping the versions on the StoreFront server up to date. Users can decline the upgrade. Alternatively, users can visit citrix.com/downloads for the latest Workspace app files.
	
	
		Citrix Workspace – If using Citrix Workspace, you can configure the service to prompt users to install the latest version of the Citrix Workspace app if a local client is not detected. Users can decline the upgrade.
	
	
		Internal download site – Users may be prevented from downloading software from the Internet, even with permission to install applications. Administrators can create an internal website for the supported Citrix Workspace app version or host the installation media on a common software distribution point for a more seamless user experience. This could be an alternative to enabling Client Detection on the StoreFront Receiver for web site, which can result in an inconsistent user experience depending on the browser’s ActiveX settings.
	
	
		Markets and stores – The Citrix Workspace app is available in Windows, Android, and iOS stores.
	
	
		Enterprise software deployment – Many organizations employ an enterprise software deployment (ESD) or Mobile Application Management (MAM) solution. ESD/MAM solutions can be used to deploy Citrix Workspace App to managed endpoint devices. Employee-owned devices can only be managed if the user successfully registers the device with the management tool. 
	
	
		Master image—Most organizations deploy a group of master desktop images to each business-owned desktop, laptop, server, or virtual desktop. A common mechanism to ensure access to virtual desktops and applications is to include a supported version of the Citrix Workspace app in the master image. Subsequent Citrix Workspace app updates are handled manually or by enterprise software deployment tools.
	
	
		Group policy—For customers without a robust ESD solution, deploying and configuring the Citrix Workspace app via Microsoft Group Policy is possible. Sample start-up scripts for deploying the app are available in the product documentation. It should be noted that this method is limited to domain-joined or hybrid Azure AD-joined endpoints only.
	
	
		Manual install – All supported versions of the Citrix Workspace app are available from the Citrix Workspace app download site. Upon landing on this site, client detection is performed, and a platform and operating system-specific link is provided to allow users to download an appropriate edition of the Citrix Workspace app. It is important to note that no configuration will be accomplished via this method alone, so users will receive the first-time use prompt to enter a server URL or email address. This option is likely to be utilized with unmanaged devices.
	
	
		Citrix Global App Configuration service – Citrix Global App Configuration service (GACS) is a Citrix service used to configure, manage, and distribute Citrix Workspace app (CWA) or client app-specific end-user settings across all device OS. You can use the Citrix Workspace App Version setting to specify which Citrix Workspace app version your end users must use for optimal results. You can set up a rule that updates the app to the latest CR (Current Release) or LTSR (Long Term Service Release) version. You can also specify if the upgrade occurs automatically or if the end user can update the app manually. Global App Configuration Service can be used on both managed and unmanaged devices.
	



	This table describes these methods.
 


	
		
			
				
					Deployment Options 
				 
			
			
				
					Thin Clients
				 
			
			
				
					Desktop PC
				 
			
			
				
					Laptop
				 
			
			
				
					Tablet
				 
			
			
				
					Smartphone
				 
			
			
				
					Unmanaged
				 
			
		
		
			
				
					Base Image
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
			
				
					X
				 
			
			
				
					X
				 
			
			
				
					X
				 
			
		
		
			
				
					ESD
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
			
				
					X
				 
			
			
				
					X
				 
			
			
				
					X
				 
			
		
		
			
				
					MAM
				 
			
			
				
					X
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
			
				
					X
				 
			
		
		
			
				
					Group Policy
				 
			
			
				
					X
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
			
				
					X
				 
			
			
				
					X
				 
			
			
				
					X
				 
			
		
		
			
				
					Citrix Download site
				 
			
			
				
					X
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
			
				
					X
				 
			
			
				
					X
				 
			
			
				
					✔
				 
			
		
		
			
				
					Internal Download site
				 
			
			
				
					X
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
			
				
					X
				 
			
			
				
					X
				 
			
			
				
					X
				 
			
		
		
			
				
					App Store
				 
			
			
				
					X
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
		
		
			
				
					Global App Configuration Service
				 
			
			
				
					X
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
		
	



	✔: Recommended / X: Not Recommended
 


	Decision: Initial Configuration



	Citrix Workspace app must be configured to provide access to enterprise resources. The configuration method varies by Citrix Workspace app version, the form factor of the device, and lastly, the access method involved. Several methods may be viable for an organization. The method utilized is contingent on the resources (people, systems, time) available and larger organizational initiatives such as BYOD programs.
 


	These methods can be used to configure the Citrix Workspace app:
 


	
		Manually—If allowed, it is usually possible to manually configure the Citrix Workspace app by entering the server URL. This method should be reserved for administrators or users with advanced knowledge.
	
	
		Provisioning file – For environments running StoreFront, providing users with a provisioning file containing store information is possible. Provisioning files are exported from the StoreFront console. The file is saved with a “*.cr” extension and can then be placed on a shared network resource, a Receiver for Web site, or another web-based resource or emailed to users. The file can then be launched from an endpoint, automatically configuring the Citrix Workspace app to use the store(s). If users browse the Receiver for Web site and select the “Activate” option under their username, this also automatically downloads this same “.cr” file and configures the Workspace app client for users.
	
	
		Group policy – Microsoft Group Policy can be used to configure the Citrix Workspace app. This can be done via start-up scripts used to deploy the Workspace app by ensuring there is a value for the SERVER_LOCATION=Server_URL parameter or by using the ADMX/ADML template files included with the installation of Citrix Workspace app to set the StoreFront Account List option in conjunction with another Workspace app deployment method. Provide the URL of the server running Citrix StoreFront or Citrix Workspace service in the format https://baseurl/Citrix/storename/discovery. It should be noted that this method is limited to domain-joined or hybrid-Azure AD-joined endpoints only.
	
	
		Email-based discovery—The Citrix Workspace app can be configured by entering an email address. Based on Domain Name System (DNS) Service (SRV) records, the app determines the Citrix Gateway and StoreFront Server associated with the email address. The Citrix Workspace service can also use email-based discovery. The app then prompts users to log on to access virtual desktops and applications.
	
	
		Global App Configuration service - The Global App Configuration service provides a centralized setup for IT admins to easily configure Citrix Workspace app settings on Windows, Mac, Android, iOS, HTML5, and Chrome OS platforms. Each platform has different settings, such as Group policies, Registry, Default.ica, SF settings, and config files, and there are many ways to configure these settings. The Global App Configuration service allows you to push the settings to end-users from one centralized interface. Global App Configuration service can be used for StoreFront and Citrix Workspace URLs.
	



	Decision: Updates



	The Citrix Workspace app is in active development. As such, periodic updates are released to enhance functionality or address user issues. As with any actively developed product, the latest version of these products should be deployed to the endpoints so that users benefit from the latest functionality and maintain compliance with product support lifecycles. The Citrix Workspace app is released in two cadences: Current Release (CR) and Long Term Service Release (LTSR). LTSR releases are maintained for a longer time period but do not receive feature updates. CRs receive regular feature updates. Multiple methods are available to update the Citrix Workspace app and, if applicable, associated plug-ins.
 


	
		Automatic Update—The Citrix Workspace app includes an auto-update capability that automatically checks for newer versions. The auto-update service can be configured to allow users to defer updates and skip any updates that are not Long Term Service Release (LTSR) versions. Auto-update is unavailable for versions before Citrix Workspace app 2104 and Citrix Workspace app 1912 LTSR CU4.
	
	
		Enterprise software deployment—ESD tools give an organization direct control over the time/frequency of Workspace app updates to managed devices. However, additional thought must be given to updating unmanaged devices.
	
	
		Manual updates—Manual methods can be used to update the Citrix Workspace app when no automated solution is available. Whether deployed on Receiver for Web site, StoreFront, an internal Citrix Workspace app site, or an external site, these options require user involvement in updating the app. Due to the involved nature of manual updates and the opportunity for a user mistake, this option should only be considered a last resort.
	
	
		StoreFront/Workspace deployment - Administrators can manage Workspace app updates via either StoreFront or Citrix Workspace service. 
		
			
				StoreFront - If a locally deployed Citrix Workspace app cannot be detected, the user is prompted to download and install it. The default download location is the Citrix website, but you can also host the installers on the StoreFront server or elsewhere, as mentioned above. 
			
			
				Workspace—In the Workspace service, you can configure end users to be prompted with the latest release of the Workspace app if they do not already have it installed.
			
		
	
	
		Global App Configuration Service—Administrators can now manage the auto-update version for the organization's devices by setting the version in the maximumAllowedVersion property in the Global App Config Service. When the version is set, the Citrix Workspace app on the user’s device automatically prompts the user to upgrade to the version specified in the maximumAllowedVersion property.
	



	Layer 2: The Access Layer



	The second layer of the design methodology is the access layer, which is defined for each user group.
 


	Creating an appropriate design for the access layer is an important part of the virtualization process. This layer handles user validation through authentication and orchestrates access to all components necessary to establish a secure connection.
 


	The access layer design decisions are based on each user group's mobility requirements and the endpoint devices used.
 


	Authentication



	Getting access to resources is based on the user’s identity. Defining the authentication strategy considers the user’s entry point into the environment and how the user will authenticate.
 


	Decision: Authentication Provider



	Previously, accessing Citrix Virtual Apps and Desktops required users to have an Active Directory username and password, typically managed within an on-premises Active Directory environment. This setup worked well for organizations with internal users.
 


	However, with the increasing use of external contractors and or affiliates, organizations are seeking more flexible authentication options. Many are turning to third-party identity providers (IdPs) like Microsoft EntraID, Google, or Okta to manage user access instead of maintaining their own user accounts.
 


	Citrix has introduced the Federated Authentication Service to accommodate this shift, which allows integration with third-party IdPs. This simplifies the onboarding process and provides greater flexibility in managing user identities.
 


	Decision: Authentication Point



	Before a user connects to a virtual workload, they must successfully authenticate. The location of authentication is typically determined by the mobility requirements of the user groups, which are established during the user segmentation process. In Citrix Virtual Apps and Desktops, there are two primary authentication points:
 


	
		Citrix StoreFront—StoreFront provides authentication and delivers resources through the Citrix Workspace app, offering centralized enterprise stores for desktops, applications, and other resources. StoreFront authenticates users who can directly access the StoreFront URL, typically those on the corporate network. 
	
	
		NetScaler Gateway – NetScaler Gateway is an appliance that facilitates secure application access and provides granular application-level policy controls. It enables users to access applications and data securely from anywhere, ensuring remote access for users outside the corporate network.
	



	For Citrix DaaS, there is an additional authentication point option along with the two authentication points mentioned above:
 


	
		Citrix Workspace – Citrix Workspace aggregates and integrates Citrix Cloud services, enabling unified access to all the resources available to your end-users. Users authenticate to their resources using the primary identity provider configured for Workspace.
	



	This table lists preferred authentication points according to user group mobility requirements.
 


	
		
			
				
					User Group’s Mobility Requirement (Network location)
				 
			
			
				
					Preferred Authentication Point
				 
			
		
		
			
				
					Local (internal)
				 
			
			
				
					Storefront*
				 
			
		
		
			
				
					Roaming or mobile users (internal)
				 
			
			
				
					Storefront*
				 
			
		
		
			
				
					Remote (external)
				 
			
			
				
					NetScaler Gateway
				 
			
		
	



	 
 


	
		Note:
	 

	
		It is possible to route internal users through a NetScaler Gateway. The benefits of doing so include gaining HDX insights on internal user connections and HDX optimal routing.
	 



	Authentication for user groups requiring remote or mobile access may occur directly on StoreFront in certain scenarios. For instance, security policies in the DMZ might restrict access from the NetScaler Gateway to the domain, preventing support for Smart Card client certificate authentication. In such cases, authentication via StoreFront can be facilitated through a NetScaler SSL_BRIDGE virtual server, facilitating HTTPS traffic. 
 


	Typically, this virtual server would be deployed alongside a NetScaler Gateway on the same NetScaler appliance configured to provide HDX Proxy access to the virtual desktop environment. While this approach may sometimes be necessary, it's generally recommended that external users authenticate via NetScaler Gateway as a best practice.
 


	Decision: Authentication Policy



	Once the authentication point has been identified, the type of authentication must be determined. The primary methods available are:
 


	
		StoreFront supports several authentication methods, although not all are recommended depending on the user access method, security requirements, and network location. By default, StoreFront authenticates users directly with Active Directory. StoreFront can be optionally configured to delegate authentication to XML if required (such as if the StoreFront servers are in a domain that does not trust the user domains).

		
			
				Username and password – Requires users to login directly to the site by entering a username and password.
			
			
				Domain pass-through – Allows pass-through of domain credentials from users' devices. Users authenticate to their domain-joined Windows computers and are automatically logged on when they access their stores.
			
			
				NetScaler Gateway pass-through—This option allows pass-through authentication from NetScaler Gateway. Users authenticate to NetScaler Gateway and are automatically logged on when they access their stores.
			
			
				Smart card – Allows users to authenticate using smart cards and PINs through the Citrix Workspace app for Windows and NetScaler Gateway. To enable smart card authentication, user accounts must be configured either within the Microsoft Active Directory domain containing the StoreFront servers or within a domain with a direct two-way trust relationship with the StoreFront server domain. Multi-forest deployments involving two-way trust are supported.
			
			
				Anonymous—Allow users to access applications and desktops without presenting credentials to StoreFront or the Citrix Workspace app. Local anonymous accounts are created on demand on the Server VDA when sessions are launched. This requires a StoreFront store configured for authenticated access, a Server OS-based VDA, and a Delivery Group configured for unauthenticated users.
			
		
	



	
		Note:
	 

	
		Enabling anonymous access on the IIS hosting Citrix StoreFront is not advisable. With anonymous authentication, users can access resources without providing credentials, which risks exposing sensitive information to unauthorized individuals. This may result in unauthorized access, data breaches, and compromise of the Citrix environment.
	 



	
		
			
				Security Assertion Markup Language (SAML)  - Users can authenticate with their identity provider (IdP) credentials. SAML authentication provides seamless single sign-on (SSO) experiences for users, allowing them to access StoreFront stores and applications without entering separate credentials. This method is particularly beneficial for organizations that use identity federation with external partners or cloud-based services. To enable SAML authentication, StoreFront must be configured to trust the IdP's SAML assertions and properly map attributes to user accounts. The Citrix Workspace app and NetScaler Gateway can also be configured to support SAML authentication, ensuring consistent authentication experiences across different access points in the Citrix environment.
			
		
	
	
		NetScaler Gateway - The NetScaler Gateway supports several authentication methods. The list below includes those primarily used in virtual desktop environments. Each may be used individually but is often combined to provide multi-factor authentication.
		
			
				LDAPS – The lightweight directory access protocol over SSL (LDAPS) is used to access directory information services such as Microsoft Active Directory. NetScaler Gateway uses LDAPS to authenticate users and extract their group membership information.
			
			
				RADIUS (token) – Remote authentication dial-in user service (RADIUS) is a UDP-based network security protocol that provides authentication, authorization, and accounting. A network access server (NetScaler Gateway in this case) forwards credentials to a RADIUS server that can either check them locally or against a directory service. The RADIUS server could then accept the connection, reject the connection, or challenge and request a second form of authentication, such as a token.
			
			
				OAuth/OIDC—OAuth is a standard for providing users access to resources without exposing credentials. OpenID Connect (OIDC) is an authentication standard built on top of OAuth. NetScaler supports OAuth/OIDC.
			
			
				SAML - Security Assertion Markup Language (SAML) is an XML-based standard for exchanging authentication and authorization between Identity Providers (IdP) and Service Providers. NetScaler Gateway supports SAML authentication. 
			
			
				TACACS+ - You can configure a TACACS+ server for authentication. Like RADIUS authentication, TACACS+ uses a secret key, an IP address, and a port number.
			
			
				Client certificate – Users logging on to a NetScaler Gateway virtual server can also be authenticated based on a client certificate's attributes. Client certificates are usually disseminated to users through smartcards or common access cards (CACs) that are read by a reader attached to each user’s device.
			
			
				nFactor - NetScaler Gateway supports nFactor authentication, which allows administrators to configure flexible authentication policies based on various factors, such as user group, device type, and location. nFactor authentication provides enhanced security by combining different authentication methods in a single policy, allowing organizations to implement multi-factor authentication.
			
		
	
	
		Citrix Workspace – Citrix Workspace supports several authentication methods, which include the following:
	
	
		Active Directory – Citrix Cloud supports using on-premises Active Directory (AD) to authenticate end users. End users will use their AD credentials to authenticate. 
	
	
		Active Directory plus token—Citrix Cloud supports using a token as a second authentication factor for users signing in with AD. Users can generate tokens using any application that follows the time-based one-time password standard.
	
	
		Microsoft Entra ID (formerly Azure Active Directory (AAD)) – Citrix Cloud supports using Microsoft Entra ID. Using Entra ID with Citrix Cloud, you can leverage your AD and security policies, configure MFA authentication, use a branded sign-in page, and use federation to an identity provider of your choice.
	



	
		Note: 
	 

	
		You need to use the Citrix Federated Authentication Service with Citrix Cloud to enable single sign-on and prevent a second login prompt at the VDA
	 



	
		Citrix Gateway—Citrix Cloud supports using an on-premises Citrix Gateway as an identity provider to authenticate subscribers. The authentication options for an on-premises Gateway are in the above section. 
	
	
		Google Cloud Identity – Citrix Cloud supports using Google Cloud Identity as an identity provider to authenticate subscribers signing in to their workspaces. By connecting your organization’s Google account to Citrix Cloud, you can provide a unified sign-in experience for accessing Citrix Workspace and Google resources.
	
	
		Okta – Citrix Cloud supports using Okta as an identity provider to authenticate subscribers signing in to their workspaces. By connecting your Okta organization to Citrix Cloud, you can provide a common sign-in experience for your subscribers to access resources in Citrix Workspace. 
		
			
				Subscribers have a different sign-in experience after enabling Okta authentication in Workspace Configuration. Selecting Okta authentication provides federated sign-in, not single sign-on. Subscribers sign in to workspaces from an Okta sign-in page but may have to authenticate again when opening an app or desktop from Citrix DaaS. You must use the Citrix Federated Authentication Service with Citrix Cloud to enable single sign-on and prevent a second login prompt. 
			
			
				SAML 2.0 – Citrix Cloud supports using SAML (Security Assertion Markup Language) as an identity provider to authenticate Citrix Cloud administrators and subscribers signing in to their workspaces. You can use the SAML 2.0 provider of your choice with your on-premises Active Directory (AD).
			
		
	



	
		Note:
	 

	
		You need to use the Citrix Federated Authentication Service with Citrix Cloud to enable single sign-on and prevent a second login prompt at the VDA.
	 



	 
 


	
		
			
				Adaptive Authentication service – Adaptive Authentication is a Citrix Cloud service that enables advanced authentication (nFactor) for customers and users logging in to Citrix Workspace without needing an on-premises Gateway. The Adaptive Authentication service verifies the user identity and authorization levels based on location, device status, and end-user context. Using these factors, the Adaptive Authentication service intelligently chooses the appropriate authentication methods and enables access to authorized resources.
			
		
	



	The authentication type for a user group is often determined based on security requirements and the authentication point used. Prioritizing the highest security standards is non-negotiable. Choosing a less secure option is risky and increases the environment's vulnerability.
 


	Business Use Cases
 


	
		Retail—A small private retail company provides virtual desktop users access to non-sensitive data such as marketing catalogs and email. They are not required to adhere to security regulations such as Sarbanes Oxley. Therefore, LDAP authentication based on username and password has been implemented.
	
	
		Financial—A medium financial enterprise provides its virtual desktop users with access to confidential data, such as banking transaction records. These enterprises are governed by security regulations such as the Statement on Accounting Standards (SAS) 70 and must utilize multi-factor authentication for remote access users. LDAP authentication based on username and password has been implemented, along with SAML authentication.
	
	
		Government—A large federal institution provides virtual desktop users access to highly confidential data, such as private citizens’ personal records. These users are subject to regulations by the Department of Defense (DOD) security standards. LDAP authentication has been implemented based on username and password, along with Client Certificate authentication using CAC cards.
	
	
		Healthcare - A hospital utilizes Citrix to deliver its healthcare Electronic Medical Record (EMR) application to users. Doctors and nurses use thin client devices stationed at workstations and mobile carts known as Workstations on Wheels (WOW) to access and manage patient data. Per HIPAA regulations, healthcare workers must authenticate their identity before accessing patient information. Imprivata offers a solution to streamline the authentication process and ensure compliance with data security regulations.
	



	Decision: Multi-factor Authentication



	Multi-factor authentication (MFA) adds an extra layer of security to your accounts. Instead of just requiring user credentials to gain access, MFA adds a second form of verification, like a phone push notification, one-time code, or biometric scan, thus making it harder for unauthorized users to access accounts. 
 


	MFA is strongly recommended if the environment involves publicly facing URLs like NetScaler Gateway or Citrix Workspace URLs. These sites are accessible online, so anyone can view the access point and try to force login. While internal-only sites are less vulnerable as they require the end user to be on the corporate network, MFA is still recommended due to the abovementioned benefits.  
	It is important to note that if a SAML-based MFA is used, the environment will require Citrix Federated Authentication Service.
 


	StoreFront



	Citrix StoreFront authenticates users to Citrix Virtual Apps and Desktop resources. StoreFront enumerates and aggregates available desktops and applications into a single interface that users access through the Citrix Workspace app or browser from their endpoint devices.
 


	Decision: Store 



	The number of StoreFront Stores may vary depending on the use case. A single Store can be configured for both internal and external access. However, two Stores are recommended if there is a need to separate internal and external access. Additionally, the Stores can be separated based on the resources they enumerate, such as keyword inclusions/exclusions or managing Delivery Controllers (DDCs) or Cloud Connectors (CCs). It's essential to consider three key points when making this design decision:
 


	
		Segregating access traffic between Stores (internal vs external)
	
	
		Separate authentication use cases
	
	
		Default.ICA configurations for a specific use case may warrant a separate StoreFront Store
	



	It's important to note that increasing the number of StoreFront stores can significantly impact storage requirements due to increased IIS-related logging.
 


	Decision: High Availability



	If the server hosting StoreFront is unavailable, users cannot launch new virtual desktops, published applications, or manage their subscriptions. Therefore, at least two StoreFront servers should be deployed to prevent this component from becoming a single point of failure. By implementing a load-balancing solution, users will not experience an interruption in their service. Options include:
 


	
		Recommended: Hardware load balancing—An intelligent appliance that can verify the availability of the StoreFront service and actively load balance user requests appropriately. Citrix + NetScaler is a great example of a hardware load balancer. It is ideal for this purpose and comes pre-configured with StoreFront health checks.

		
			
				For the StoreFront load balancing VIP, it is recommended that the load balancing method be set to ‘LeastConnection’ and the Persistence setting to ‘SourceIP.’ 
			
		
	
	
		DNS round robin – Provides rudimentary load balancing across multiple servers without performing any checks on availability. If a StoreFront server becomes unavailable, DNS round robin would still route users to the failed server. Because of this, Citrix does not recommend DNS round-robin.
	
	
		Windows network load balancing—A Windows service capable of performing rudimentary checks to verify the server's availability but cannot determine the status of individual services. This can cause users to be forwarded to StoreFront servers, which cannot process new requests. The user would then not be able to access applications or desktops.
	



	Decision: Delivery Controller Reference



	StoreFront must be configured with the IP address or DNS name of at least one Delivery Controller in each Citrix Virtual Apps and Desktops site to provide users with desktops and applications. For fault tolerance, multiple controllers should be entered for each site and/or farm specified. By default, StoreFront treats a list of servers in failover order (active/passive). Ensuring that the communication between Citrix StoreFront and Delivery Controller is securely transmitted using TLS 1.3 encryption over HTTPS (port 443) is recommended.
 


	An active distribution of the user load (active/active) is recommended for large deployments or environments with a high logon load. This can be achieved using a load balancer with built-in XML monitors, such as Citrix + NetScaler, or by configuring StoreFront to load balance the list of Controllers instead of treating them as an ordered list. For the DDC load balancing VIP, it is recommended that the load balancing method be set to ‘LeastConnection.’
 


	Decision: Beacons



	Citrix Workspace app uses beacons (websites) to identify whether a user is connected to an internal or external network. Internal users are connected directly to StoreFront for authentication, while external users are connected via Citrix NetScaler Gateway. It is possible to control what users see by restricting applications based on which beacon they can access.
 


	The internal beacon should be a site that is not resolvable externally. By default, the internal beacon is the StoreFront base URL. This must be adjusted if the same external and internal URL is configured. The external beacon can be any external site that produces an HTTP response. Citrix Workspace app continuously monitors the status of network connections (for example, link up, link down, or change of the default gateway). When a status change is detected, the Citrix Workspace app first verifies that the internal beacon points can be accessed before checking the accessibility of external beacon points. StoreFront provides the Citrix Workspace app with the beacon points' HTTP(s) addresses during the initial connection/configuration download process and updates as necessary.
 


	Specifying at least two highly available external beacons that can be resolved from public networks is necessary.
 


	Decision: Resource Presentation



	By default, StoreFront allows users to choose (subscribe) to the resources they want to use after they log on (favorites) regularly. This approach allows users to restrict the resources they see on their home screen to those they use regularly. The resources chosen by every user for each store are recorded by the subscription store service and stored locally on each StoreFront server (synced automatically between servers in the same server group) so that they can be displayed on the Citrix Workspace app home screen from any device that the user connects from. Although subscriptions are per store and server group by default, administrators can configure two stores within a server group to share a subscription database and/or sync subscriptions between two identically named stores in two separate server groups on a defined schedule if required. This is recommended to sustain the subscription across two server groups for a better user experience.
 


	If favorites are not required or you do not wish to give users the ability to favorite resources, you can make the store mandatory, removing the option to favorite. This can reduce management overhead and the storage needed on the StoreFront servers. 
 


	Administrators should determine which applications should always be displayed to users on their home screen or the featured tab. These are generally common applications such as the Microsoft Office Suite and any other applications that every user in an environment may need. StoreFront can filter/present these resources using Keywords defined within the published application properties Description field.
 


	This table explores the Keyword options:
 


	
		
			
				
					Keyword
				 
			
			
				
					Description
				 
			
		
		
			
				
					Mandatory
				 
			
			
				
					Adds an application to the Home tab. Unlike favorites, users cannot remove mandatory applications from the Home tab. Has no effect if favorites are disabled for the store. 
				 
			
		
		
			
				
					Auto
				 
			
			
				
					When users log on to the store, the application is automatically favorited and added to their Home tab. Users can unfavorite such applications. This has no effect if favorites are disabled for the store.
				 
			
		
		
			
				
					TreatAsApp
				 
			
			
				
					Apply to desktops to force StoreFront to treat them as apps. The desktop is displayed on the Apps tab rather than the Desktops tab. In addition, it is not automatically started when the user logs on to the store website and is not accessed with the Desktop Viewer, even if the site is configured to do this for other desktops.
				 
			
		
		
			
				
					prefer=”pattern”
				 
			
			
				
					Where the application identifies a locally installed application. Applies only to the Citrix Workspace app on Windows. This indicates that the locally installed version of an application should be used in preference to the equivalent delivered instance if both are available.
				 
			
		
		
			
				
					Primary and Secondary
				 
			
			
				
					When using Multi-Site Aggregation, the one with the keyword primary specified will always be preferred over the one with the keyword secondary.
				 
			
		
	



	 
 


	Decision: Remote Access



	StoreFront stores can be accessible internally or internally and externally. Internal-only stores will only be accessible if the end-user device is connected to a corporate network that can resolve the StoreFront URL. If a store has remote access enabled, it can be accessible internally and through a Gateway. In environments where security requires the separation of internal and external traffic, internal and external users can use different stores. 
 


	When configuring remote access, you can configure a Callback URL. This is used to verify that requests received from NetScaler Gateway originate from that appliance. A callback URL is only needed if SmartAccess policies or password-less authentication methods (Smart Cards, SAML, and so on) are used. Additionally, it is recommended that you match the method used in the Gateway when configuring the Secure Ticketing Authorities (STAs) (HTTP vs. HTTPS, FQDN vs. IP). 
 


	Decision: HDX Optimal Routing



	In a multi-site Citrix Virtual Apps and Desktops solution, certain criteria route users to the optimal site, like the fastest response time or closest proximity. These algorithms do not consider the resources a user wants to access.
 


	Improper routing of a user’s session results in the following:
 


	
 


	
		The user is routed to the most preferred site based on proximity or response time.
	
	
		NetScaler Gateway proxies the HDX traffic to the correct resource, which could be across the corporate WAN. Ideally, optimized routing should resemble the following:
	



	
 


	
 


	
		User routed to the most preferred site based on proximity or response time
	
	
		Based on the selected resource, NetScaler Gateway reroutes the session to a NetScaler Gateway in the preferred site.
	
	
		NetScaler Gateway proxies the HDX traffic to the correct resource, which stays on the local LAN.
	



	Using the optimized HDX routing option within StoreFront offloads traffic from the corporate WAN and places it on the public network, improving the user experience during their Citrix session. 
 


	Decision: Aggregation Groups



	If the Citrix Virtual Apps and Desktops solution includes multiple delivery sites, StoreFront combines the available resources so the user has a single interface for all published resources.  However, if multiple sites publish the same resources, the user might experience confusion as a single application appears multiple times.
 


	 
 


	StoreFront Aggregation groups define how the resources in multiple sites merge to provide the user with a single, easy-to-understand view. StoreFront aggregates duplicate published resources into a single icon.
 


	 
 


	The administrator must determine how to load balance users across the different Citrix Virtual Apps and Desktops sites when the icon is an aggregation. The options are:
 


	
		Load Balancing – Used when duplicate sites are created based on capacity recommendations. StoreFront distributes user requests across all configured sites.
	
	
		Failover – Used when geographies need resources available during an outage or when migrating users from one site to another (like a Citrix migration project).
	



	The “Map users to controllers” settings are commonly referred to as “User Farm Mapping” as they control which Sites a given group of users are allowed to enumerate against, whether those Sites are aggregated or not. This functionality has two primary use cases: limiting enumeration and assigning aggregation settings.
 


	Limiting user mappings to stores with resources reduces the enumeration time by reducing the number of sites StoreFront has to contact. Aggregation settings can also be configured per user mapping, as it may be desired to assign different configurations to different sets of users—such as failover configurations or different combinations of sites. 
 


	You can override the specified deployment ordering for individual Citrix Virtual Apps and Desktops resources to define preferred deployments to which users are connected when they access a particular desktop or application. This enables you to, for example, specify that users are preferentially connected to a deployment specifically adapted to deliver a particular desktop or application but use other deployments for other resources. To do this, you can use keywords Primary to describe the desktop or application on the preferred deployment and Secondary to the resource on other deployments. 
 


	Documenting the users, stores, and aggregation methods during the design phase is advisable. For example:
 


	
		
			
				
					User Group
				 
			
			
				
					Available Stores
				 
			
			
				
					Load Balancing Stores
				 
			
			
				
					Failover Stores
				 
			
		
		
			
				
					NA_FinanceUsers
				 
			
			
				
					NA_West_Store, NA_East_Store, EMEA Store
				 
			
			
				
					NA_West_Store, NA_East_Store
				 
			
			
				
					EMEA_Store
				 
			
		
		
			
				
					EMEA_SalesUsers
				 
			
			
				
					EMEA_Store, NA_East_Store
				 
			
			
				
					EMEA_Store
				 
			
			
				
					NA_East_Store
				 
			
		
	



	 
 


	Decision: Scalability



	The number of Citrix Workspace app users supported by a single StoreFront server depends on the resources assigned and user activity level. Receiver for Web users will consume more RAM on average than native Workspace app users, but a minimum of 8GB plus 30 MB for each store (assuming one website per store) is recommended. For each store with favorites enabled, an additional 5 MB plus 8 MB for each 1000 favorites is recommended. Additionally, more sites/farms enumerated per store will increase CPU utilization and server response time.
 


	Depending on storage needs, a general starting point for StoreFront sizing is 4 vCPU with 8-16 GB of RAM. The CPU may need to be scaled up depending on the total number of connections and the load on the StoreFront servers. Each StoreFront server group should have at least 2 StoreFront servers but no more than 5. Tests have shown diminishing returns after a single StoreFront deployment grows beyond 3-4 StoreFront nodes with a maximum of 5 servers supported in a single server group. StoreFront server groups are not recommended to span data centers. 
 


	Citrix Workspace service



	Citrix Workspace is the cloud-based access option for Citrix Cloud users. As a cloud service, it is accessible to all users via the Internet. The Workspace configuration has different design considerations.
 


	Decision: Workspace URL



	You can enable one URL by default within the *.cloud.com domain. The end user will visit this URL to authenticate, enumerate, and launch. It is also possible to use your own custom domain. You can also configure up to 10 Workspace URLs; however, a custom domain is currently unsupported for multi-URLs. For multi-Workspace URLs, you can configure different branding, resource filtering, and authentication configurations for the separate URLs. 
 


	Decision: ICA proxy



	When using Citrix Workspace service, the default ICA proxy is Citrix Gateway Service. This is a Citrix Cloud service with Points of Presence (PoPs) around the globe that provides proxy sessions to end users. It is a turnkey service and does not require configuration. This service enables internal and external users to launch resources. 
 


	You can also configure Workspace to use an on-premises Gateway to route the ICA traffic if you want to control the routing of the session traffic, like through OGR. A caveat is that neither Service Continuity nor Local Host Cache is supported in this scenario.
 


	Another option is Internal Only. This restricts resource launch only to scenarios where the user’s endpoint can directly connect to the VDA, like from the corporate network. It is important to note that end users can still access their Workspace URLs from anywhere, but resource filtering can be used to hide the icons when connecting remotely. 
 


	Decision: Connection Routing



	By default, all user sessions are routed through the Cloud Connectors via the Gateway service. This can increase the usage of the Cloud Connectors and affect their scaling. To reduce the amount of traffic handled by the Cloud Connectors, it is recommended that you implement the Rendezvous protocol.
 


	
 


	 
 


	
 


	If your environment uses VDA versions from before 2203, you can use Rendezvous V1. V1 supports bypassing the Citrix Cloud Connectors for HDX session traffic only. If you use a VDA version after 2203+, you can use Rendezvous V2. V2 supports bypassing the Citrix Cloud Connectors for both control and HDX session traffic, reducing the amount of traffic through the Cloud Connector.
 


	With Direct Workload Connection in Citrix Cloud, you can optimize internal traffic to the resources to make HDX sessions faster. Ordinarily, users on both internal and external networks connect to VDAs through an external Gateway. This Gateway might be on-premises in your organization or provided as a service from Citrix. Direct Workload Connection allows internal users to bypass the Gateway and connect to the VDAs directly, reducing latency for internal network traffic. To set up Direct Workload Connection, you need network locations and public IP addresses corresponding to where clients launch resources in your environment.
 


	Decision: Service Continuity 



	Service Continuity is a high availability (HA) service for customers using Citrix Workspace and Citrix Gateway service. Service continuity allows users to connect to their DaaS apps and desktops during outages, as long as the device maintains a network connection to a resource location. 
 


	Service continuity uses Workspace connection leases to allow users to access apps and desktops during outages. Workspace connection leases are long-lived authorization tokens. Workspace connection lease files are securely cached on the user's device. Workspace connection lease files are signed and encrypted and are associated with the user and the user's device. 
 


	Enabling Service Continuity is highly recommended; otherwise, users may be unable to connect during resilience events. When Service Continuity is enabled, a Workspace connection lease allows users to access apps and desktops for seven days by default. You can configure Workspace connection leases to allow access for up to 30 days. If you have users accessing via HTML5, they will need the browser plug-in to use Service Continuity.
 


	Decision: Resource filtering /network location service



	It is possible to filter what resources appear to users based on their Workspace URL and network location.
 


	With the new multiple Workspace URL feature, you can filter and deliver resources based on end users' Workspace URL. To configure an access policy based on Workspace URLs, you need to apply the following SmartAccess filters. The filter values are also sent as SmartAccess tags to the DaaS service. 
 


	
		Citrix.Workspace.UsingDomain allows filtering of delivery group resources by the Workspace URL. The value is the fully qualified domain name of the Workspace URL.
	
	
		Citrix-Via-Workspace - Indicates that the end user is using the Workspace service rather than an on-premises StoreFront deployment.
	



	The Citrix Workspace Adaptive Access feature uses advanced policy infrastructure to enable access to Citrix DaaS based on the user’s network location. The location is defined using the IP address range or subnet addresses. To configure this, you must know the public IP ranges of the various access points.
 


	 
 


	
 


	Admins can define policies to enumerate or not enumerate virtual apps and desktops based on the user’s network location. Admins can also control the user actions by enabling or disabling clipboard access, printers, client drive mapping, and so on, based on the user’s network location. For example, admins can set up policies such that users accessing the resources from home have limited access to applications and users accessing the resources from branch offices have full access.
 


	Decision: Site Aggregation 



	Only one Citrix DaaS tenant can be mapped to the Citrix Workspace service. You can also aggregate Citrix Virtual Apps and Desktop sites to display their icons and provide access via Workspace. However, Service Continuity is not supported for Citrix Virtual Apps and Desktop sites. 
 


	NetScaler Gateway



	User groups utilizing NetScaler Gateway as their authentication point have additional design decisions to consider. These design decisions do not apply to non-NetScaler Gateway authentication points.
 


	Decision: Topology



	Selection of the network topology is central to planning the remote access architecture to ensure it can support the necessary functionality, performance, and security. The design of the remote access architecture should be completed in collaboration with the security team to ensure adherence to corporate security requirements. There are two primary topologies to consider each of which provides increasing levels of security:
 


	
		1-Arm (normal security)—With a 1-arm topology, the NetScaler Gateway utilizes one physical or logical bonded interface, with an associated VLAN and IP subnet to transport both. If increased security is desirable, separate DMZ and intranet traffic VLANs can be configured with the appropriate firewall policies for each network.
	



	
 


	
		2-Arm (high security) – With a 2-arm topology, the NetScaler Gateway utilizes two or more physically or logically bonded interfaces with associated VLANS and IP subnets. The frontend traffic for users is transported to one of these interfaces. The frontend traffic is isolated from backend traffic between the virtual desktop infrastructure servers and services, which is directed to a second interface. This allows using separate demilitarized zones (DMZs) to isolate frontend and backend traffic flows along with granular firewall control and monitoring.
	



	
 


	Decision: High Availability



	If the NetScaler Gateway is unavailable, remote users cannot access the environment. Therefore, at least two NetScaler Gateway hosts should be deployed per data center to prevent this component from becoming a single point of failure.
 


	When configuring NetScaler Gateway in a high availability (active/passive) pair, the secondary NetScaler Gateway monitors the first appliance by sending periodic messages, also called heartbeat messages or health checks, to determine if the first appliance accepts connections. If a health check fails, the secondary NetScaler Gateway tries the connection again for a specified time until it determines that the primary appliance is not working. If the secondary appliance confirms the health check failure, the secondary NetScaler Gateway takes over for the primary NetScaler Gateway.
 


	NetScaler Gateway can be deployed in a tiered design to provide high throughput, high availability, and scalability for HDX client traffic. Multi-tier NetScaler architectures enable expanding the number of supported HDX proxy users in a single data center while still using a single access URL. If done correctly, the user experience is no different from what you get using a single HA ADC pair. 
 


	NetScaler Gateway appliances or VMs operate in a cluster as a single system image to coordinate user sessions and manage traffic to network resources. The typical use case for using a cluster over a tiered architecture is simplifying management. With a cluster, a single management IP (CLIP) can be used to manage the entire cluster. With tiered, the top tier load balancer (used to distribute load across the Gateways) has a different configuration (and management IP) than the GW tier. However, this management difference can be automated with proper automation (Terraform, Ansible, etc.). A NetScaler Gateway cluster should be built with an odd number of nodes, e.g., 3, 5, or 7, with a max of 31 available. It is recommended to use odd-numbered cluster nodes to minimize issues with leader election for the cluster coordinator (CCO) node.
 


	Decision: Form Factor



	The key resource constraints must be identified to identify an appropriate NetScaler form factor to meet project requirements. Since all remote access traffic will be secured using the secure sockets layer (SSL), transported by Hypertext Transfer Protocol (HTTP) in the form of HTTPs, two resource metrics should be targeted:
 


	
		SSL throughput—SSL throughput is the gigabits of SSL traffic that may be processed per second (Gbps).
	
	
		SSL transactions per second (TPS) – The TPS metric identifies how often an Application Delivery Controller (ADC) may execute an SSL transaction per second. The capacity varies primarily by the key length required. TPS capacity is primarily a consideration during the negotiation phase when SSL is first set up. It is less of a factor in the bulk encryption/decryption phase, which is most of the session life. While TPS is an important metric to monitor, field experience has shown that SSL throughput is the most significant factor in identifying the appropriate NetScaler Gateway.
	
	
		User logon rate—The default rate is 30 logins/sec, and a NetScaler can safely scale up to 60. If the peak user logon rate is higher than 60 logins/sec, the design needs to be re-architected using the abovementioned tiered architecture.
	



	The bandwidth overhead for SSL depends on the packet size.  The smaller the packet size, the higher the overhead.  As ICA is typically a small packet protocol, an estimated 20% overhead for a typical interactive would be reasonable. If the app does bulk data transport, bandwidth overhead can be estimated at around 5%. In summary, this calculation depends on the type of app traffic.  If it isn’t possible to differentiate between highly interactive traffic and bulk data transport, then err at 20% overhead.
 


	SSL Throughput = Maximum Concurrent Bandwidth * 1.20
 


	For example, assuming 128Mbps maximum concurrent bandwidth, the appropriate NetScaler model can be determined as follows:
 


	~155 Mbps =128 Mbps ∗ 1.20
 


	The SSL throughput value should be compared to the throughput capabilities of various NetScaler form factors to determine the most appropriate one for the environment. Four main form factor groups are available, each providing broad scalability options.
 


	
		VPX – A NetScaler VPX device provides the same full functionality as hardware NetScaler. However, NetScaler VPXs can leverage ‘off-the-shelf’ servers for hosting. Typically, organizations create a baseline cap for the VPX instances at 500 users.
	
	
		MPX – A NetScaler MPX is the hardware version of the NetScaler devices. The MPX device is more powerful than the virtual NetScaler and can support network optimizations for larger-scale enterprise deployments, particularly when SSL offload will be configured, as this is done in software on the VPX versus dedicated SSL chips on the MPX.
	
	
		SDX—A NetScaler SDX blends virtual and physical NetScaler devices. An SDX machine is a physical device capable of hosting multiple virtual VPX NetScaler devices. This device consolidation aids in reducing the required shelf space. NetScaler SDXs are suitable for handling network communications for large enterprise deployments and/or multi-tenant hosting providers.
	
	
		BLX – A NetScaler BLX is a software form factor designed to run natively on bare metal. NetScaler BLX for bare metal runs as a Linux process on your hardware of choice. Because NetScaler BLX is a lightweight software package with no hypervisor or container overhead, you get extraordinarily fast performance. Certain use cases, such as full disk at rest disk encryption, can only be done with BLX.  BLX is also better suited for hyperscalers, as the hyperscalers tend to rate limit PPS, which causes varying performance randomly.  BLX, since it occupies the entire host (bare metal instance), helps mitigate PPS rate-limiting issues with public clouds.
	



	SSL throughput capabilities of the NetScaler form factors may be found in the NetScaler data sheet. However, actual scalability will depend on security requirements. NetScaler SSL throughput decreases with increasingly complex encryption algorithms and longer key lengths. Customers should generally prefer ECDHE over non-elliptic curve ciphers such as DHE. ECDHE is lighter on resources while providing the same level of cryptographic security. Also, this calculation represents a single primary NetScaler. At a minimum, N+1 redundancy is recommended, which would call for an additional NetScaler for the identical form factor and model.
 


	
		Note:
	 

	
		The Citrix NetScaler data sheet typically represents throughput capabilities under optimal performance conditions. However, performance is directly affected by security requirements. For example, if the RC4 encryption algorithm and a 1k key length are used, a VPX platform may be able to handle more than 500 HDX proxy connections. However, if a 3DES encryption algorithm and 2k key length are used (becoming more common), the throughput may be halved.
	 



	 
 


	Decision: NetScaler Console



	NetScaler Console and NetScaler Console service (formerly known as NetScaler ADM and ADM service) are solutions for managing all NetScaler deployments, including NetScaler MPX, NetScaler VPX, NetScaler SDX, NetScaler CPX, NetScaler BLX, and NetScaler Gateway that are deployed on-premises or in the cloud.
 


	NetScaler Console provides all the capabilities required to quickly set up, deploy, and manage application delivery in NetScaler deployments, with rich analytics of application health, performance, and security. Key benefits involve deploying pooled licensing, HDX insights, SSL certificate tracking, automated upgrades, CVE tracking, and more. 
 


	The NetScaler Console virtual appliance can be installed on Microsoft Hyper-V, VMware ESXi, Linux KVM, and XenServer platforms. The general requirements for the virtual appliance are a minimum of 32 GB RAM, 8 vCPUs, and 120 GB SSD. The NetScaler Console HA Deployment Guide can assist with the sizing requirements and overall deployment of the NetScaler Console virtual appliance.
 


	Decision: Pre-Authentication Policy



	An optional pre-authentication policy may be applied to user groups with NetScaler Gateway as their authentication point. Pre-authentication policies limit environmental access based on whether the endpoint meets certain criteria through Endpoint Analysis (EPA) Scans.
 


	Pre-authentication access policies can be configured to test antivirus, firewall, operating system, or registry settings. These policies can prevent access entirely or by Citrix Virtual Apps and Desktops to control session features such as clipboard mapping, printer mapping, and even the availability of specific applications and desktops. For example, if a user device does not have antivirus installed, a filter can be set to hide sensitive applications.
 


	This figure provides an overview of how multiple policies can be used to customize the features of a virtualization resource:
 


	
 


	
		Design Tip
	 

	
		Use EPA scans to scan for updated antivirus definitions. Use Domain SID to verify that users are members of the enterprise domain or that the endpoint devices have a specific certificate installed before allowing access.
	 



	Decision: Session Policy
 


	User groups with NetScaler Gateway as their authentication point must define corresponding session policies, which define the overall user experience after authentication.
 


	Organizations create session policies based on the type of Citrix Workspace app used. For session policy assignment, devices are commonly grouped as either non-mobile (such as Windows, Mac, and Linux, ChromeOS-based) or mobile (such as iOS or Android). Therefore, a decision on whether to provide support for mobile devices, non-mobile devices, or both should be made based on client device requirements identified during the assess phase.
 


	To identify device session policies, include expressions such as:
 


	
		Mobile devices - The expression is set to REQ.HTTP.HEADER User-Agent CONTAINS CitrixReceiver, which is given a higher priority than the non-mobile device policy, to ensure mobile devices are matched while non-mobile devices are not.
	
	
		Non-mobile devices – The expression is set to ns_true, which signifies that it should apply to all traffic sent to it.
	



	An alternative use of session policies is to apply endpoint analysis expressions. These session policies are applied post-authentication yet mimic the previously mentioned pre-authentication policies. The use of session policies is an option to provide a fallback scenario to endpoints that do not meet full security requirements, such as read-only access to specific applications.
 


	Decision: Session Profile



	Each session policy must have a defined corresponding session profile. The session profile defines the details required for the user group to gain access to the environment. Two primary forms of session profiles determine the access method to the virtual desktop environment:
 


	
		SSLVPN – Users create a virtual private network and tunnel all traffic configured by IP addresses through the internal network. The user’s client device can access permitted intranet resources as if it were on the internal network. This includes Citrix Virtual Apps and Desktops sites and any other internal traffic such as file shares or intranet websites. This is considered a potentially less secure access method since network ports and routes to services outside of the virtual desktop infrastructure may be opened, leaving the enterprise susceptible to risks that may come with full VPN access. Configuring traffic and authorization policies to limit this access can help mitigate this security issue, but this does introduce management overhead.
	
	
		These risks may include denial of service attacks, attempts at hacking internal servers, or any other form of malicious activity that may be launched from malware, Trojan horses, or other viruses via an Internet-based client against vulnerable enterprise services via routes and ports.
	



	Another decision to consider when SSLVPN is required is whether to enable split tunneling for client network traffic. By enabling split tunneling, client network traffic directed to the intranet by the Citrix Workspace app may be limited to routes and ports associated with specific services. By disabling split tunneling, all client network traffic is directed to the intranet; therefore, traffic destined for internal services and external services (Internet) traverse the corporate network. The advantage of enabling split tunneling is that exposure of the corporate network is limited, and network bandwidth is conserved. The advantage of disabling split tunneling is that client traffic may be monitored or controlled through web filters or intrusion detection systems.
 


	
 


	
		HDX proxy—With HDX Proxy, users connect to their virtual desktops and applications through the NetScaler Gateway without exposing internal addresses externally. The NetScaler Gateway is a micro VPN in this configuration and only handles HDX traffic. Other types of traffic on the client’s endpoint device, such as private mail or personal Internet traffic, do not use the NetScaler Gateway. A decision must be made whether this method is supported for each user group based on the endpoint and Citrix Workspace app used. HDX Proxy is a secure remote virtual desktop access method since only traffic specific to the desktop session can pass through to the corporate infrastructure. 
	



	
 


	Decision: Preferred Datacenter



	Enterprises often have multiple active datacenters that provide high availability for mission-critical applications. Some virtual desktops or applications may fall into that category, while others may only be accessed from a specific preferred datacenter. Therefore, the initial NetScaler Gateway a user authenticates to in a multi-active datacenter environment may not be within the preferred datacenter corresponding to the user’s VDI resources. StoreFront can determine the location of the user’s assigned resources and direct the HDX session to those resources; however, the resulting path may be sub-optional (WAN connection from the NetScaler Gateway to the virtual desktop/application resources as opposed to LAN connection).
 


	Static and dynamic methods are available to direct HDX sessions to their virtual desktop resources in their primary datacenter. The decision regarding which method to select should be based on the availability of technology to dynamically assign site links, such as Global Server Load Balancing (GSLB) along with the network assessment of intranet and Internet bandwidth as well as Quality of Service (QoS) capabilities.
 


	
		Note:
	 

	
		For more information on configuring the static and dynamic methods of GSLB, please refer to NetScaler Product Documentation - Configuring GSLB for Proximity
	 



	
		Static

		
			
				Direct—The user may be given an FQDN mapped to an A record dedicated to the primary datacenter NetScaler Gateway(s), allowing them to access their virtual desktop directly wherever they are. This approach eliminates a layer of complexity added with dynamic allocation. However, it also eliminates fault tolerance options, such as the ability to access the virtual desktop through an alternative intranet path when a primary datacenter outage is limited to the access infrastructure.
			
		
	
	
		Dynamic
		
			
				Intranet – For most dynamic environments, the initial datacenter selected for authentication is the one closest to the user. Protocols such as GSLB dynamic proximity calculate the least latency between the user’s local DNS server and the NetScaler Gateway. Thereafter, by default, the HDX session is routed through the same NetScaler Gateway to whichever datacenter is hosting the user’s virtual desktops and applications. The advantage of this approach is that most of the HDX sessions would traverse the corporate WAN, where quality of service may be used.
			
		
	



	
 


	
		Internet - Alternatively, the HDX session can be re-routed through an alternate NetScaler Gateway proximate to the backend VDI desktop / Citrix Virtual Apps server, resulting in most of the HDX session traveling over the Internet. For example, a user traveling in Europe with a dedicated desktop in the United States may be directed to a NetScaler Gateway hosted in a European datacenter based on proximity. However, when the user launches their desktop, an HDX connection will be established to the virtual desktop via a NetScaler Gateway hosted in the preferred datacenter in the United States. This conserves WAN network usage (at the cost of QoS) and is recommended in cases where the user’s Internet connection may provide a more reliable experience than the corporate WAN.
	



	
 


	Some customers will use a combination of these methods, such as geo-specific dynamic URLs, to provide fault tolerance within a geographic area (such as North America) without incurring the complexity of global GSLB.
 


	Site-to-Site Connectivity



	A Citrix Virtual Apps and Desktops site can span multiple locations. To successfully implement a multi-site solution, the design must consider the site-to-site links and Citrix Virtual Apps and Desktops session routing between locations to provide the best user experience.
 


	Layer 3: The Resource Layer



	The resource layer is the third layer of the design methodology, and the final layer focuses specifically on the user groups.
 


	The decisions made within the resource layer define the solution's overall user acceptance. Profiles, printing, applications, and overall desktop image design are pivotal in aligning the desktop with the user group’s requirements identified within the assess phase.
 


	User Experience



	Perception is everything when it comes to a good VDI experience. Users expect an experience similar to or better than a traditional physical desktop.
 


	Codecs, transport protocols, and self-service capabilities affect the overall experience. Poor-quality graphics, lagging video, or extremely long login times can destroy the user experience. A proper user experience design can meet any network challenge.
 


	Decision: Display Protocol



	Selecting the correct display protocol determines the quality of static images, video, and text within the user’s session and determines the impact on single-server scalability. Administrators have these options:
 


	
		10-Bit High Dynamic Range (HDR)—With 10-bit High Dynamic Range (HDR) virtual desktop sessions, you can use enhanced encoding and decoding capabilities to render high-quality images and videos with an extended range of colors and greater contrast and brightness. You can also customize the white luminance level, Extended Display Identification Data (EDID), and visual quality to improve the user experience.
	
	
		H.264 - One of the most commonly used codecs, has great hardware and software decoding support. However, as screen resolutions increased and technology like HDR was introduced, H.264’s limitations became apparent. It does come at the expense of CPU processing time, reducing single-server scalability.
	
	
		H.265 - High-Efficiency Video Coding (HEVC), also known as H.265, is the successor to H.264. H.265 offers users the same quality at a lower bandwidth, improving user experience in bandwidth-restricted situations. However, H.265 requires a GPU for encoding and decoding. Performing this on a CPU is possible, but it influences overall performance and decreases scalability.
	
	
		AV1 - Citrix added support for the AV1 video codec. As AV1 is a newer codec, it has newer graphics card requirements for both the VDA and endpoint. The benefit of AV1 is that it has superior image compression, better image quality, and lower bandwidth usage compared to H.264 and H.265.
	



	The video codecs above can be automatically detected to optimize the VDA and endpoint communication. The decoding capabilities are evaluated when using Workspace App for Windows, and the VDA can select the best codec to use when connecting. Using a video codec, whether it is H.264, H.265, or AV1, provides a richer experience than adaptive JPEG at the expense of CPU to compress regions of fluid movement. Network bandwidth will generally be less with a video codec compared to Adaptive JPEG for multimedia workload, and newer codecs have better compression and, therefore, lower bandwidth usage. Running your own tests with your specific use case is highly recommended.
 


	Decision: Video Codec Policy



	Consider the workload of each user group when configuring Citrix policies to set the video codec and ensure the desired quality and user experience. When creating these policies, administrators have these options:
 


	
		Thinwire—Thinwire, part of the Citrix HDX technology stack, is the default display-remoting technology used in Citrix Virtual Apps and Desktops and Citrix DaaS. Display remoting technology allows graphics generated on one machine to be transmitted, typically across a network, to another machine for display. Graphics are generated from user input, such as keystrokes or mouse actions. Thinwire operates in two encoding modes: Thinwire full-screen H.264 or H.265 and Thinwire with selective H.264 or H.265. 
	
	
		HDX 3DPro - By using HDX 3D Pro, you can deliver graphically intensive applications as part of hosted desktops or applications on Single-session OS machines. HDX 3D Pro supports physical host computers (including desktop, blade, and rack workstations) and, along with Citrix’s entire graphics stack,  supports GPU Passthrough and GPU virtualization technologies offered by XenServer, vSphere, and Hyper-V (passthrough only) hypervisors. You can create VMs with exclusive access to dedicated graphics processing hardware using GPU Passthrough. You can install multiple GPUs on the hypervisor and assign VMs to each of these GPUs on a one-to-one basis. Using GPU virtualization, multiple virtual machines can directly access the graphics processing power of a single physical GPU.
	



	Decision: Transport Protocol



	There are three ways to transport the HDX protocol across the network:
 


	
		TCP—Uses the industry-standard TCP transport protocol. It is a common transport protocol over LAN and low-latency WAN connections, but it suffers when connection distances increase, thus increasing latency and incurring more retransmissions.
	
	
		EDT uses a Citrix proprietary transport protocol called Enlightened Data Transport, based on UDP. It is most common on long-distance WAN links for high latency/packet loss networks. EDT provides a more interactive user experience without increasing the server's CPU load, but it consumes more network bandwidth than TCP.
	
	
		Adaptive Transport—Uses TCP and EDT transport protocols. EDT is used unless the network does not support transporting EDT over the network, in which case it automatically changes to TCP.
	



	Most environments will use Adaptive Transport as the standard transport option unless the network does not have the appropriate firewall ports opened or NetScaler Gateway configured appropriately. NetScaler Gateway 14.1.12.30 or later is recommended. Alternatively, the Citrix Gateway Service with Rendezvous enabled can also be used. For the detailed requirements for using AV1, see the product documentation.
 


	Decision: Logon Optimization



	The logon process must be completed when a user logs onto a Citrix Virtual Apps and Desktops session. This includes session initialization, user profile loading, group policy preferences execution, drive mapping, printer mapping, logon script execution, and desktop initialization. Each process takes time and increases the duration of the login.
 


	Most organizations include many mappings and complex logon scripts. When each of these items executes, the logon time drastically increases.
 


	
 


	Figure 21: Components of logon duration
 


	Minimizing GPOs and start-up scripts and removing unnecessary startup applications and tasks will help improve logon performance. In addition to these general recommendations, Citrix has features to help further improve logon performance. 
 


	Workspace Environment Management removes drive mappings, printer mappings, logon scripts, and roaming profiles from the standard login process. With asynchronous processing, Workspace Environment Management applies the mappings/scripts/profiles in the background after the session, and the desktop initializes. The user receives the same environment but receives their desktop interface faster. To use this asynchronous processing, most environments should consider implementing drive mappings, logon scripts, roaming profiles, and GPOs via Workspace Environment Manager. 
 


	The Citrix Optimizer is a tool that allows administrators to apply sets of recommended and optional configuration changes to both single and multi-session VDAs. It should be run before publishing an image to configure settings to optimize logon and run-time performance. The Optimizer can be run in three modes: Analyze, Optimize, and Rollback. Administrators should re-run the Optimizer Tool after image updates to re-apply the required settings should they be changed during the update process. 
 


	Citrix Director provides granular information on login times broken down by various phases, such as brokering, authentication, profile load, and interactive session. It also shows trends for the overall environment, comparing a particular user's average to their peers. Additional info can be found here.
 


	User Profiles



	A user’s profile is critical to delivering a consistently positive experience within a virtual desktop or application scenario. A well-designed virtual desktop solution can fail if users are frustrated due to lengthy logon times or lost settings.
 


	The user profile solution chosen must align with the personalization characteristics of the user group captured during the assess phase and the VDI model selected.
 


	Decision: Profile Type



	This section provides an overview of the available profile types and guides the optimal user profile for each VDI model.
 


	
		Local profiles – Local profiles are stored on each server or desktop operating system and are initially created based on the default user profile. Therefore, a user accessing these resources would create an independent profile on each system. Users can retain changes to their local profile on each individual system, but changes are only accessible for future sessions on that system. Local profiles require no configuration; if a user logging into a server or desktop operating system does not have a profile path administratively defined, a local profile is created by default.
	
	
		Roaming profiles – Roaming profiles are stored in a centralized network repository for each user. Roaming profiles differ from local profiles in that the information in the profile (whether it is a printer, a registry setting, or a file stored in the documents folder) can be made available to user sessions accessed from all systems in the environment. Configuring a user for a roaming profile requires an administrator to designate the user’s profile path (for virtual desktops) or terminal server profile path to a particular network share. The first time the user logs on to a server or desktop operating system, the default user profile is used to create the user’s roaming profile. During logoff, the profile is copied to the administrator-specified network location.
	
	
		Mandatory profiles – Mandatory profiles are typically stored in a central location for many users. However, the user’s changes are not retained at logoff. Configuring a user for a mandatory profile requires an administrator to create a mandatory profile file (NTUSER.MAN) from an existing roaming or local profile and assign users with a terminal services profile path. This can be achieved using Microsoft Group Policy, customizing the user properties in Active Directory, or Citrix Profile Management.
	
	
		Citrix Profile Management (CPM) – CPM combines a robust profile core (a mandatory or a local default profile) with user-specific registry keys or files merged during login. This technique lets administrators control which changes are retained tightly and keep the user profiles small. Furthermore, CPM addresses the last write wins issue using mature queuing techniques that automatically detect and prevent simultaneous writes that could potentially overwrite changes made in another session. Thus minimizing user frustration resulting from lost profile changes when accessing multiple servers or virtual desktops simultaneously. In addition, CPM captures and records only the changes within the profile rather than writing the entire profile at logoff. 
	
	
		Profile Containers—Profile containers redirect the entire Windows user profile to a file, typically in a VHD or VHDX format, and store it in a storage repository, most often an SMB share. Profile containers are mounted when a user logs into a virtual session and can resolve issues with slow logins and improve the overall login experience. Citrix Profile Management provides a profile container in VHDX format. 
	



	This table compares the capabilities of each profile type:
 


	
		
			
				
					Feature
				 
			
			
				
					Local
				 
			
			
				
					Roaming
				 
			
			
				
					Mandatory
				 
			
			
				
					CPM
				 
			
			
				
					CPM Container
				 
			
		
		
			
				
					Central management/roams with user
				 
			
			
				
					X
				 
			
			
				
					✔
				 
			
			
				
					⭘
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
		
		
			
				
					User settings are stored persistently.
				 
			
			
				
					⭘
				 
			
			
				
					✔
				 
			
			
				
					X
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
		
		
			
				
					Granular capture of user settings
				 
			
			
				
					X
				 
			
			
				
					X
				 
			
			
				
					X
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
		
	



	✔: Functionality available / X: Functionality not available / ⭘: Optional
 


	Understanding each user group's personalization requirements and the VDI model assigned is important for selecting the optimal profile type.
 


	This table provides recommendations on selecting the appropriate user profile type based on VDI resources:
 


	
		
			
				 
			
			
				
					Local
				 
			
			
				
					Roaming
				 
			
			
				
					Mandatory
				 
			
			
				
					CPM
				 
			
			
				
					CPM Container
				 
			
		
		
			
				
					User setting persistence required (personalization characteristic: basic/complete)
				 
			
		
		
			
				
					Hosted Windows App
				 
			
			
				
					X
				 
			
			
				
					✔
				 
			
			
				
					X
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
		
		
			
				
					Hosted Shared Desktop
				 
			
			
				
					X
				 
			
			
				
					✔
				 
			
			
				
					X
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
		
		
			
				
					Hosted Pooled Desktop
				 
			
			
				
					X
				 
			
			
				
					✔
				 
			
			
				
					X
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
		
		
			
				
					Hosted Static Desktop
				 
			
			
				
					⭘
				 
			
			
				
					✔
				 
			
			
				
					X
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
		
		
			
				
					Hosted 3D Pro Graphics Desktop
				 
			
			
				
					⭘
				 
			
			
				
					✔
				 
			
			
				
					X
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
		
		
			
				
					Remote PC Access
				 
			
			
				
					✔
				 
			
			
				
					⭘
				 
			
			
				
					X
				 
			
			
				
					⭘
				 
			
			
				
					⭘
				 
			
		
		
			
				
					User setting persistence not required or not desired (personalization characteristic: none)
				 
			
		
		
			
				
					Hosted Windows App
				 
			
			
				
					X
				 
			
			
				
					X
				 
			
			
				
					✔
				 
			
			
				
					X
				 
			
			
				
					X
				 
			
		
		
			
				
					Hosted Shared Desktop
				 
			
			
				
					X
				 
			
			
				
					X
				 
			
			
				
					✔
				 
			
			
				
					X
				 
			
			
				
					X
				 
			
		
		
			
				
					Hosted Pooled Desktop
				 
			
			
				
					✔
				 
			
			
				
					X
				 
			
			
				
					✔
				 
			
			
				
					X
				 
			
			
				
					X
				 
			
		
		
			
				
					Hosted Static Desktop
				 
			
			
				
					X
				 
			
			
				
					X
				 
			
			
				
					✔
				 
			
			
				
					X
				 
			
			
				
					X
				 
			
		
		
			
				
					Hosted 3D Pro Graphics Desktop
				 
			
			
				
					⭘
				 
			
			
				
					X
				 
			
			
				
					✔
				 
			
			
				
					X
				 
			
			
				
					X
				 
			
		
		
			
				
					Remote PC Access
				 
			
			
				
					⭘
				 
			
			
				
					X
				 
			
			
				
					✔
				 
			
			
				
					X
				 
			
			
				
					X
				 
			
		
	



	✔: Recommended / X: Not Recommended / ⭘: Viable
 


	Decision: Folder Redirection



	Redirecting special folders can supplement any of the described profile types. While redirecting profile folders, such as user documents and favorites, to a network share is a good practice to minimize profile size, architects need to be aware that applications may frequently read and write data to profile folders such as AppData, causing potential issues with file server utilization and responsiveness. It is important to thoroughly test profile redirection before implementation in production to avoid these issues. Therefore, it is important to research profile read/write activities and to perform a pilot before moving to production. Microsoft Outlook is an example of an application that regularly performs profile read activities, as the user signature is read from the user profile every time an email is created.
 


	This table provides general recommendations to help identify the appropriate folders to redirect:
 


	
		
			
				
					Folder
				 
			
			
				
					Local
				 
			
			
				
					Roaming
				 
			
			
				
					Mandatory
				 
			
			
				
					Hybrid File-Based
				 
			
			
				
					Profile Container
				 
			
		
		
			
				
					Application Data
				 
			
			
				
					X
				 
			
			
				
					⭘
				 
			
			
				
					X
				 
			
			
				
					⭘
				 
			
			
				
					⭘
				 
			
		
		
			
				
					Contacts
				 
			
			
				
					X
				 
			
			
				
					✔
				 
			
			
				
					X
				 
			
			
				
					⭘
				 
			
			
				
					⭘
				 
			
		
		
			
				
					Desktop
				 
			
			
				
					X
				 
			
			
				
					✔
				 
			
			
				
					X
				 
			
			
				
					⭘
				 
			
			
				
					⭘
				 
			
		
		
			
				
					Downloads
				 
			
			
				
					X
				 
			
			
				
					⭘
				 
			
			
				
					X
				 
			
			
				
					⭘
				 
			
			
				
					⭘
				 
			
		
		
			
				
					Favorites
				 
			
			
				
					⭘
				 
			
			
				
					✔
				 
			
			
				
					⭘
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
		
		
			
				
					Links
				 
			
			
				
					X
				 
			
			
				
					✔
				 
			
			
				
					X
				 
			
			
				
					⭘
				 
			
			
				
					⭘
				 
			
		
		
			
				
					Documents
				 
			
			
				
					⭘
				 
			
			
				
					✔
				 
			
			
				
					⭘
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
		
		
			
				
					Music
				 
			
			
				
					⭘
				 
			
			
				
					✔
				 
			
			
				
					⭘
				 
			
			
				
					⭘
				 
			
			
				
					⭘
				 
			
		
		
			
				
					Pictures
				 
			
			
				
					⭘
				 
			
			
				
					✔
				 
			
			
				
					⭘
				 
			
			
				
					⭘
				 
			
			
				
					⭘
				 
			
		
		
			
				
					Videos
				 
			
			
				
					⭘
				 
			
			
				
					✔
				 
			
			
				
					⭘
				 
			
			
				
					⭘
				 
			
			
				
					⭘
				 
			
		
		
			
				
					Saved Games
				 
			
			
				
					X
				 
			
			
				
					⭘
				 
			
			
				
					X
				 
			
			
				
					⭘
				 
			
			
				
					⭘
				 
			
		
		
			
				
					Start menu
				 
			
			
				
					X
				 
			
			
				
					X
				 
			
			
				
					X
				 
			
			
				
					X
				 
			
			
				
					X
				 
			
		
	



	✔: Recommended / X: Not Recommended / ⭘: Optional
 


	Decision: Folder Exclusion



	Excluding folders from being persistently stored as part of a roaming or hybrid profile can help reduce profile size and logon times. By default, Windows excludes the AppData\Local and AppData\LocalLow folders, including all subfolders, such as History, Temp, and Temporary Internet Files. In addition, the downloads and saved games folders should also be excluded. All redirected folders should also be excluded from the profile solution.
 


	There are also recommended exclusions for Google Chrome and Firefox,
 


	Decision: Profile Caching



	Local caching of roaming or hybrid user profiles on a server or virtual desktop is the default Windows behavior and can reduce login times and file server utilization/network traffic. With profile caching, the system only has to download changes made to the profile. The downside of profile caching is that it can consume significant amounts of local disk storage on multi-user systems, such as hosted shared desktop hosts.
 


	Local profile caches should not be deleted, even when non-persistent machines are restored to a pristine state on logoff, as deleting the cached profile consumes time and CPU resources. Administrators should consider deleting profile caches on multi-session desktop and application systems to reduce disk storage consumed by individual user profiles over time. 
 


	Configuring the “Delay before deleting cached profiles” Citrix policy sets an optional extension to the delay before locally cached profiles are deleted at logoff. Extending the delay is useful if a process keeps files or the user registry hive open during logoff. This can also reduce logoff times for large profiles.
 


	Decision: Profile Permissions



	For security reasons, user profile shares should always be configured to restrict permissions so others cannot view a given user’s profile. Administrators, by default, cannot access user profiles but can take ownership of files and folders as an auditable action. If default administrator access is desired, the “Add the Administrators security group to roaming user profiles” policy setting can be configured. The configuration of this policy should be aligned with the security and privacy considerations of the user groups captured during the assess phase. For more information on the permissions required for the file share hosting user profiles and data, please refer to Microsoft’s Deploy Roaming Profiles article.
 


	Decision: Profile Path



	Determining the network path for the user profiles is one of the most significant decisions during the user profile design process. In general, it is strongly recommended that you leverage a redundant and high-performance file server, NAS device, or cloud-based solution such as Azure Files.
 


	Four topics must be considered for the profile share:
 


	
		Performance – File server performance will affect logon and logoff times, and depending on other decisions, such as redirected folders and profile streaming, can impact the user’s experience within the session. A single file server cluster may not be sufficient for large virtual desktop infrastructures to handle peak activity periods. The file server address and share name must be adjusted to distribute the load across multiple file servers.
	
	
		Location – User profiles are transferred over the network using the SMB protocol, which performs poorly on high-latency network connections. Furthermore, WAN connections are typically bandwidth-constrained, which can add additional delay to the profile load process. Therefore, the file server should be located in close proximity to the servers and virtual desktops to minimize logon times.
	
	
		Operating system platforms—User profiles are tightly integrated with the underlying operating system, and reusing a single user profile for different operating systems is not recommended. 
	
	
		Availability—The RTO and RPO goals will guide the availability strategy of the user profiles. It is generally recommended that profile data be backed up on a regular basis. It is critical that users have access to the data they need in a DR scenario.
	



	Two methods can be used to address these challenges based on Windows' built-in technology:
 


	
		User object – An individual profile path can be specified for every user object in Active Directory containing the file server name and profile directory. Since only a single profile path can be specified per user object, ensuring a separate profile is loaded for each operating system platform is impossible.
	
	
		Computer group policy or system variables – The user profile path can also be configured through computer-specific group policies or system variables. This enables administrators to ensure a user profile is dedicated to the platform. Since computer-specific configurations affect all system users, all user profiles will be written to the same file server. Dedicated Citrix Virtual Apps and Desktops delivery groups must be created per file server to balance user profiles across multiple servers.
	



	
		Note:
	 

	
		Microsoft does not support DFS-N combined with DFS-R for actively used user profiles. For more information, please refer to this Microsoft article.
	 



	 
 


	When using Citrix Profile Management, a third option is available to address these challenges:
 


	
		User object attributes and variables – Citrix Profile Management enables the administrator to configure the profile path using a computer group policy and the attributes of the user object in Active Directory to specify the file server dynamically. To achieve this, three steps are required:

		
			
				Create a DNS alias (for example, fileserver1) that refers to the actual file server.
			
			
				Populate an empty LDAP attribute of the user object (for example, l or UID) with the DNS Alias.
			
			
				Configure Citrix Profile Management using GPO to use a profile path that refers to the LDAP attribute (for example, if attribute UID is used, the profile path becomes \\#UlD#\Profiles\profiledirectory)
			
		
	



	In addition, Citrix Profile Management automatically populates variables to specify the profile path dynamically based on the operating system platform. Valid profile management variables are:
 


	!CTX_PROFILEVER! – Expands to the profile version depending on the profile version.
 


	
		!CTX_OSBITNESS! – Expands to x86 or x64, depending on the bit level of the operating system.
	
	
		!CTX_OSNAME! – Expands to the short name of the operating system, for example, Win11
	
	
		!ctx_localsettings! – Expands to AppData\Local
	
	
		!ctx_roamingappdata! – Expands to AppData\Roaming
	
	
		!ctx_startmenu! – Expands to AppData\Roaming\Microsoft\Windows\Start Menu
	
	
		!ctx_internetcache! - Expands to AppData\Roaming\Microsoft\Windows\Temporary Internet Cache
	
	
		!ctx_localappdata! –Expands to AppData\Local
	



	By combining both capabilities of Citrix Profile Management, a fully dynamic user profile path can be created, which can be load-balanced across multiple file servers and ensure profiles of different operating system platforms are not mixed. An example of a fully dynamic user profile path is shown here:
 


	\\#UID#\profiles$\%USERNAME%.%USERDOMAIN%\!CTX_OSNAME!!CTX_OSBITNESS!
 


	Decision: Profile Streaming



	
		Note: 
	 

	
		The following design decision only applies to those environments that use Citrix Profile Management.
	 



	With user profile streaming, files and folders contained in a profile are fetched from the user store (file server) to the local computer when a user accesses them. Citrix Profile Management immediately reported that the profile load process had been completed during the login process, reducing profile load time.
 


	Citrix recommends enabling profile streaming for all scenarios, and in Citrix Virtual Apps and Desktops 2402 LTSR, this is enabled by default. If a local cached copy of the user profile is desired for performance reasons, enabling the “Always Cache” setting and configuring a size of 0 is recommended. This ensures that the user profile is downloaded in the background and enables the system to use this cached copy going forward.
 


	
		Design Tip: 
	 

	
		Some poorly written applications might load faster if their AppData has already been streamed to the VDI resource. Enabling the “Always Cache” option for profile streaming can help improve performance when the AppData folder is not redirected.
	 



	Decision: Active Write Back
 


	
		Note: 
	 

	
		The following design decision only applies to those environments that use Citrix Profile Management.
	 



	By enabling the active write-back feature, Citrix Profile Manager detects when an application has written and closed a file and copies the file back to the network copy of the profile during idle periods. This feature can be tremendously beneficial when a single user leverages multiple virtual desktops or hosted shared desktops simultaneously. However, Citrix Profile Management does not copy any registry changes back to the network except during an ordered logoff. As such, there is a risk that the registry and files may get out of alignment on non-persistent systems, where locally cached profile information is wiped upon reboot. Therefore, it is recommended to disable active write-back functionality for non-persistent scenarios.
 


	Decision: Configuration Approach



	
		Note: 
	 

	
		The following design decision only applies to those environments that use Citrix Profile Management.
	 



	Citrix Profile Management can be configured using a “.ini” file, Microsoft Group Policy, Citrix WEM, or Citrix Policy. Each option offers the same configuration settings, and selecting one over the other is based on factors such as ease of use. For example, Group Policy may be recommended because it allows administrators to perform Windows and Citrix profile configurations simultaneously, minimizing the tools necessary for profile management.
 


	Decision: Replication



	While having an active/active datacenter on a network level is easily accomplished with GSLB, replicating user data makes having a fully active/active deployment complex in most situations. Administrators need to consider the implications for profile and other user data requirements to have an active/active configuration where users are not statically assigned to a specific datacenter. Profile solutions are not active-active, so user personalization will be limited or depend on intra-datacenter latency, which can impact the overall user experience.  
 


	User data, such as Windows profiles and documents, should be synchronized between datacenters for redundancy and failover purposes. Although it is recommended to replicate user data between datacenters, the replication would be an active/passive configuration. This means the data can only be actively consumed from a single datacenter. The reason for this limitation is the distributed file-locking method inside Windows that only allows a single user to write to a file actively. Therefore, active/active replication of user data is not supported. Any supported configuration consists of a one-way replication of data active in a single datacenter at any time.
 


	Citrix Profile Management can be configured to replicate profile data between datacenters. On user login, CPM determines the datastore with the latest profile by default or may be configured to select either the earliest configured or best-performing store for access. Once users log off their session, the profile data is written back across all configured stores. This provides redundancy for the user profile but does not provide in-session failover capabilities.
 


	User Data
 


	To be effective, users must be able to access their data. For the user to have a good experience, the data must be in close proximity to the application. As the distance between the application and data increases, latency also increases, which slows down any file operation (opening, saving, modifying).
 


	In a VDI-based environment, administrators must understand where users store their data and the impact of access.
 


	Decision: User Data Location



	Users traditionally stored their data on their local device or on a network file server designated with a drive mapping. Due to IT storage space limitations or the inability to have the data follow the user to other mobile devices, users turned to free, cloud-based storage offerings like OneDrive, ShareFile, DropBox, and Box. To get access to the data, the user would install the storage vendor’s agent on their traditional Windows PC, allowing them direct access to the cloud-hosted storage repository. One of the primary considerations for cloud-based storage offerings is caching on files upon connection, which can cause high network and I/O utilization and potentially fill up drive space. These solutions should utilize on-demand sync such as files are only downloaded upon access.
 


	Administrators must design the solution to consider user storage using a Multi-Agent Strategy. In VDI, users require the admin to install and configure the agent for each storage provider, which assumes the storage agent supports the non-persistent VDI model. Each agent is a new application that the admin must manage and maintain.
 


	Decision: User Data Access



	A critical aspect of a successful VDI solution is for the user experience to remain the same as that of a traditional PC. If users open files from within the application or navigate with File Explorer to access a file, that functionality must continue functioning.
 


	A user’s data can exist on the local PC, network file share, or cloud.
 


	
 


	With local PCs, on-premises network shares, and cloud-hosted storage options available to users, administrators must understand how users' data access affects the infrastructure and VDI experience.
 


	
		Direct Data Access – Users access a file on a remote server (on-premises Windows server or cloud-hosted storage provider). The distance between the application and the file directly affects the experience. Longer distances equate to higher latency. Each file operation (navigate, open, close, save, etc.) takes longer as the latency between application and file storage increases. Windows file servers are often located in the same data center as the user’s VDI desktop, making direct data access feasible. Still, cloud-hosted solutions and local PC Access will experience poor response times if the connection between the VDI desktop and the storage repository has high latency.
	
	
		Local Synchronization – With a traditional PC, users are accustomed to having files local, which mitigates any slow application response times due to extremely low latency. Many cloud-hosted solutions synchronize data, enabling access speeds similar to a local storage model. Many cloud-hosted solutions provide full synchronization or user-configured partial synchronization of certain folders and files. With partial synchronization, only the synchronized files are visible and accessible on the device, causing user confusion. Full and partial synchronization increases VDI costs. For non-persistent VDI, each session is an entirely new desktop requiring synchronization of the user’s folders/files, which takes time, network bandwidth, and VDI storage space. Every file synchronized to the VDI desktop must be stored within the organization’s data center for the duration of the VDI session.
	
	
		On-Demand Synchronization – When navigating Explorer, users see a complete but virtual file/folder structure even though those files/folders do not physically exist on the desktop. Selecting a file begins an automatic synchronization to the VDI desktop for that single file. At this point, file access is local, creating a user experience like a traditional PC. When the user saves or closes the file, the file synchronizes back to the cloud. Only the files accessed synchronize, eliminating the waste incurred with the local data access model. As only accessed files synchronize, the impact on the underlying storage infrastructure and associated storage costs is minimal.
	



	
		
			
				 
			
			
				
					Direct Data Access
				 
			
			
				
					Local Synchronization
				 
			
			
				
					On-Demand Synchronization
				 
			
		
		
			
				
					Network File Server
				 
			
			
				
					✔
				 
			
			
				
					N/A
				 
			
			
				
					N/A
				 
			
		
		
			
				
					Cloud-hosted
				 
			
			
				
					X
				 
			
			
				
					X
				 
			
			
				
					✔
				 
			
		
		
			
				
					Local PC
				 
			
			
				
					X
				 
			
			
				
					X
				 
			
			
				
					✔
				 
			
		
	



	✔: Recommended / X: Not Recommended
 


	Decision: Data Recovery



	File corruption is an issue most users experience. Improperly shutting down the application or PC (hitting the power button instead of closing the application and shutting down the operating system gracefully) often causes many corruption issues.
 


	A few options exist to provide users with data recovery options:
 


	
		Multi-File – With a traditional PC, users have few recovery options if the files are local. Users often manually create a new copy of the file each day to provide some level of recovery. This solution is hard to manage.
	
	
		Backup/Restore – Administrators can implement a backup and restore solution to help recover files. However, these solutions rarely work with local files, and for a network file share, the backup process usually only runs nightly or weekly. In addition, restoring a corrupted file requires the user to call support.
	
	
		Versioning—Cloud-hosted options include file versioning, which automatically creates new file versions as changes are saved. Versioning requires no user intervention and allows users to recover from corruption quickly and with little data loss.
	



	Policies



	Policies provide the basis for configuring and fine-tuning Citrix Virtual Apps and Desktop environments, allowing organizations to control connection, security, and bandwidth settings based on various combinations of users, devices, or connection types.
 


	It is important to consider Microsoft and Citrix policies when making policy decisions to ensure that all user experience, security, and optimization settings are considered. Please refer to the Citrix Policy Settings Reference for a list of all Citrix-related policies.
 


	Decision: Preferred Policy Engine



	Organizations can configure Citrix policies via Citrix Studio or through Active Directory group policy using Citrix ADMX files, which extend group policy and provide advanced filtering mechanisms.
 


	Using Active Directory group policy allows organizations to manage Windows and Citrix policies in the same location and minimizes the administrative tools required for policy management. Group policies are automatically replicated across domain controllers, protecting the information and simplifying policy application.
 


	Citrix administrative consoles should be used if Citrix administrators cannot access Active Directory policies. Architects should select one of the above two methods appropriate for their organization’s needs and use it consistently to avoid confusion with multiple Citrix policy locations.
 


	It is important to understand how the aggregation of policies, known as policy precedence, flows to understand how a resultant set of policies is created. With Active Directory and Citrix policies, the precedence is as follows:
 


	
		
			
				
					Policy Precedence
				 
			
			
				
					Policy Type
				 
			
		
		
			
				
					Processed first (lowest precedence)
				 
			
			
				
					 Local server policies
				 
			
		
		
			
				
					Processed second
				 
			
			
				
					Citrix policies created using Citrix Studio
				 
			
		
		
			
				
					Processed third
				 
			
			
				
					Site-level AD policies
				 
			
		
		
			
				
					Processed fourth
				 
			
			
				
					Domain-level AD policies
				 
			
		
		
			
				
					Processed fifth
				 
			
			
				
					Highest level OU in domain
				 
			
		
		
			
				
					Processed sixth
				 
			
			
				
					Next-level OU in domain
				 
			
		
		
			
				
					Processed last (highest precedence)
				 
			
			
				
					Lowest level OU containing object
				 
			
		
	



	Policies from each level are aggregated into a final policy that is applied to the user or computer. In most enterprise deployments, Citrix administrators do not have the rights to change policies outside their specific OUs, which will typically be the highest level for precedence. In cases where exceptions are required, applying policy settings from higher up the OU tree can be managed using “block inheritance” and “no override” settings. Block inheritance stops settings from higher-level OUs (lower precedence) from being incorporated into the policy. It is preferred that block inheritance is applied at the Citrix level in the OU structure. However, the block inheritance setting will not be applied if a higher-level OU policy is configured with no override. Given this, care must be taken in policy planning, and available tools such as the “Active Directory Resultant Set of Policy” tool or the “Citrix Group Policy Modeling” wizard should be used to validate the observed outcomes with the expected outcomes.
 


	
		 
		Note: 
	 

	
		Some Citrix policy settings, if used, must be configured through Active Directory group policy, such as Controllers and Controller registration port, as these settings are required for VDAs to register. These settings can also be configured via AD GPO.
	 



	 
 


	Decision: Policy Integration



	Organizations often require Active Directory and Citrix policies to create a completely configured environment when configuring policies. Using both policy sets, the resultant set of policies can become confusing to determine. In some cases, particularly with respect to Windows Remote Desktop Services (RDS) and Citrix policies, similar functionality can be configured in two different locations. For example, enabling client drive mapping in a Citrix policy and disabling client drive mapping in an RDS policy is possible. The ability to use the desired feature may depend upon the combination of the RDS and Citrix policies. It is important to understand that Citrix policies build upon functionality available in Remote Desktop Services. If the required feature is explicitly disabled in the RDS policy, Citrix policy will not be able to affect a configuration as the underlying functionality has been disabled.
 


	To avoid this confusion, it is recommended that RDS policies only be configured where required. There is no corresponding policy in the Citrix Virtual Apps and Desktops configuration, and the configuration is specifically needed for RDS use within the organization. Configuring policies at the highest common denominator will simplify understanding the resultant set of policies and troubleshooting policy configurations.
 


	Decision: Policy Scope



	Once policies have been created, they must be applied to groups of users and/or computers based on the required outcome. Policy filtering allows policies to be applied against the requisite user or computer groups. With Active Directory-based policies, a key decision is whether to apply a policy to computers or users within site, domain, or organizational unit (OU) objects. Active Directory policies are broken down into user configuration and computer configuration. By default, the settings within the user configuration apply to users who reside within the OU at logon, and settings within the computer configuration are applied to the computer at system startup and will affect all users who log in to the system. The challenges of policy association with Active Directory and Citrix deployments revolve around three core areas:
 


	
		Citrix environment-specific computer policies – Citrix servers and virtual desktops often have computer policies created and deployed specifically for the environment. Applying these policies is easily accomplished by creating separate OU structures for the servers and the virtual desktops. Specific policies can then be created and confidently applied to only the computers within the OU and below and nothing else. Depending on requirements, virtual desktops and servers may be subdivided within the OU structure based on server roles, geographical locations, or business units.
	
	
		Citrix-specific user policies – When creating policies for Citrix Virtual Apps and Desktops, several policies are specific to user experience and security that are applied based on the user’s connection. However, the user’s account could be located anywhere within the Active Directory structure, creating difficulty simply applying user configuration-based policies. Applying the Citrix-specific configurations at the domain level is undesirable as the settings would be applied to every system the user logs into. Simply applying the user configuration settings at the OU where the Citrix servers or virtual desktops are located will also not work, as the user accounts are not within that OU. The solution is to apply a loopback policy, which is a computer configuration policy that forces the computer to apply the assigned user configuration policy of the OU to any user who logs onto the server or virtual desktop, regardless of the user’s location within Active Directory. Loopback processing can be applied by either merging or replacing settings. Replace is generally recommended. Using replace overwrites, the entire user GPO is governed by the policy from the Citrix server or virtual desktop OU. Merge will combine the user GPO with the GPO from the Citrix server or desktop OU. As the computer GPOs are processed after the user GPOs when merge is used, the Citrix-related OU settings will have precedence and be applied in the event of a conflict. Replacing settings is generally recommended for operational simplicity since other user-level policies may not be required or desired within the Citrix configuration. For more information, please refer to the Microsoft article.
	
	
		Active Directory policy filtering – In more advanced cases, a policy setting may be needed for a small subset of users, such as Citrix administrators. In this case, loopback processing will not work, as the policy should only be applied to a subset of users, not all users who log in to the system. Active Directory policy filtering can be used to specify specific users or groups of users to which the policy is applied. A policy can be created for a specific function, and then a policy filter can be set to apply that policy only to a group of users, such as Citrix administrators. Policy filtering is accomplished using the security properties of each target policy.
	



	Citrix policies created using Citrix Studio have specific filter settings available, which may be used to address policy-filtering situations that cannot be handled using group policy. Citrix policies may be applied using any combination of the following filters:
 


	
		
			
				
					Filter Name
				 
			
			
				
					Filter Description
				 
			
			
				
					Scope
				 
			
		
		
			
				
					Access control
				 
			
			
				
					Applies a policy based on access control conditions through which a client connects. For example, users connecting through a Citrix NetScaler Gateway can have specific policies applied. This requires the Callback URL to be configured within StoreFront.
				 
			
			
				
					User settings
				 
			
		
		
			
				
					Client IP Address
				 
			
			
				
					This filter applies a policy based on the IPv4 or IPv6 address of the user device used to connect the session. If IPv4 address ranges are used, care must be taken with this filter to avoid unexpected results. 
				 
			
			
				
					User Settings
				 
			
		
		
			
				
					Client Name
				 
			
			
				
					Applies a policy based on the client name, either an exact match or using a wildcard 
				 
			
			
				
					User Settings
				 
			
		
		
			
				
					Delivery Group
				 
			
			
				
					Applies a policy based on the delivery group membership of the desktop running the session 
				 
			
			
				
					User and Computer settings
				 
			
		
		
			
				
					Delivery Group type
				 
			
			
				
					Applies a policy based on the type of desktop or application
				 
			
			
				
					User and Computer settings
				 
			
		
		
			
				
					Organizational unit
				 
			
			
				
					Applies a policy based on the OU of the desktop or server running the session. 
				 
			
			
				
					User and Computer settings
				 
			
		
		
			
				
					Tag
				 
			
			
				
					It applies a policy based on any tags to the session's desktop. Tags are strings that can be added to virtual desktops in Citrix Virtual Apps and Desktops environments to search for or limit access to desktops.
				 
			
			
				
					User and Computer settings
				 
			
		
		
			
				
					User or Group 
				 
			
			
				
					Applies a policy based on the user's Active Directory group membership when connecting to the session.
				 
			
			
				
					User settings
				 
			
		
	



	Decision: Baseline Policy
 


	A baseline policy should contain all common elements required to deliver a high-definition experience to most users within the organization. A baseline policy creates the foundation for user access and any exceptions that may need to be created to address specific access requirements for groups of users. It should be comprehensive to cover as many use cases as possible and should have the lowest priority, for example, 99 (a priority number of “1” is the highest priority), to create the simplest policy structure possible and avoid difficulties in determining the resultant set of policies. This way, exceptions to the baseline policy can be set at a higher priority. The unfiltered policy provided by Citrix as the default policy can be used to create the baseline policy; it should be set to the lowest priority and disabled. 
 


	A baseline policy configuration should also include Windows policies. Windows policies reflect user-specific settings that optimize the user experience and remove features that are not required or desired in a Citrix Virtual Apps and Desktops environment. For example, one common feature turned off in these environments is Windows update. In virtualized environments, particularly where desktops and servers may be streamed and non-persistent, Windows update creates processing and network overhead, and changes made by the update process will not persist a restart of the virtual desktop or application server. In many cases, organizations also use Windows software update service (WSUS) to control Windows updates. In these cases, updates are applied to the master disk and made available by the IT department on a scheduled basis.
 


	In addition to the above considerations, an organization’s final baseline policy may include settings specifically created to address security requirements and common network conditions or to manage user device or user profile requirements.
 


	Decision: Peripherals



	Citrix integrates with various peripheral devices, such as generic USB peripherals, client drives, specialty keyboards, and more. If your users do not require peripheral access, it is recommended that you restrict it. You can read more about peripheral policies and default settings in our product documentation.
 


	Decision: Audio and Unified Communications



	Citrix offers a wide array of policies to help improve audio performance during a virtual session. Our policy settings are in our product documentation. 
 


	One specific capability to call out is Browser Content Redirection (BCR). BCR prevents the rendering of web pages on the VDA side, which saves compute for your VDA resources. You can specify websites to be redirected (like YouTube, for example). BCR is supported on the Citrix Workspace app for Windows and Linux.
 


	Citrix offers various optimization packs to improve softphone performance for different vendors in a Citrix environment. Using optimization packs and following the recommended settings in the product documentation will improve softphone performance. Citrix also provides specific guidance on optimizing Microsoft Teams for use in your VDI environment. 
 


	Printing



	Citrix Virtual Apps and Desktops support a variety of printing solutions. Understanding the available technologies, their benefits, and their limitations is important for planning and successfully implementing the proper printing solution.
 


	Decision: Printer Provisioning



	The process of creating printers at the start of a Citrix Virtual Apps and Desktops session is called printer provisioning. There are multiple approaches available:
 


	
		Auto Created – Auto-creation is a form of dynamic provisioning that attempts to create some or all of the available printers on the client device at the start of a user session. This includes locally attached printers as well as network-based printers. Auto-creating all client printers can increase the session logon time as each printer is enumerated during the logon process.
	
	
		Session-Based – Session printers are a set of network-based printers assigned to users.
		
			
				Proximity-based session printers are filtered by IP subnet. The network printers created under this policy may vary based on where the user’s endpoint device is located. Proximity printing is recommended when Users roam between different locations using the same endpoint device (e.g., laptop, tablet) and where thin clients are used, which cannot connect directly to network-based printers.
			
			
				Session printers may be assigned using the “Session Printer” policy or the “Printer Assignments” policy. The “Session Printer” policy is intended to set default printers for a farm, site, large group, or OU. The “Printer Assignments” policy assigns multiple printers to multiple users. If both policies are enabled and configured, the session printers will be merged into a single list.
			
		
	
	
		Universal Printer – The Citrix Universal Printer is a generic printer object that is auto-created at the start of a session and is not linked to a printing device. When using the Citrix Universal Printer, it is not required to enumerate the available client printers during login, which can greatly reduce resource usage and decrease user logon times. The Citrix Universal Printer will default print to the client’s default printer. However, the behavior can be modified to allow users to select any compatible local or network-based printers. The Citrix Universal Printer is best suited for these scenarios:
		
			
				The user requires access to multiple printers, both local and network-based, which may vary for each session.
			
			
				The user’s login performance is a priority, and the Citrix policy “Wait for printers to be created” must be enabled due to application compatibility.
			
			
				The user is working from a Windows-based device or thin client.
			
		
	



	
		Note: 
	 

	
		Other options for provisioning printers, such as Active Directory group policy, “follow-me” centralized print queue solutions, and third-party print management solutions, can be used to provision printers into a Citrix session.
	 



	Decision: Printer Drivers



	Managing printer drivers in a Citrix environment can be tedious, especially in large environments with hundreds of printers. Fortunately, several methods are available in Citrix Virtual Apps and Desktops to assist with print driver management.
 


	
		User Installed – When adding a printer within a Citrix Virtual Apps and Desktops session and the native print driver is unavailable, the user can install the drivers manually. Many different print drivers can potentially be installed on different resources, creating inconsistencies within the environment. Troubleshooting printing problems and maintenance of print drivers can become challenging since every hosted resource may install different sets of print drivers. User-installed drivers are not recommended to ensure consistency and simplify support and troubleshooting.
	
	
		Administrator installed – Administrators will pre-install the necessary print drivers in the VDA image. This ensures consistency and simplifies support and troubleshooting.
	
	
		Automatic Installation – When connecting a printer within a Citrix Virtual Apps and Desktops session, a check is made to see if the required print driver is already installed in the operating system. If the print driver is not already installed, the native print driver, if one exists, will be installed automatically. If users roam between multiple endpoints and locations, this can create inconsistencies across sessions since users may access a different hosted resource every time they connect. When this type of scenario occurs, troubleshooting printing problems and maintenance of print drivers can become very challenging since every hosted resource may have different sets of print drivers installed. Automatic installed drivers are not recommended to ensure consistency and simplify support and troubleshooting.
	
	
		Universal Print Driver – The Citrix Universal Printer Driver (UPD) is a device-independent print driver designed to work with most printers. The Citrix Universal Printer Driver (UPD) simplifies administration by reducing the number of drivers required on the master image. For auto-created client printers, the driver records the application output and sends it, without any modification, to the end-point device. The endpoint uses local, device-specific drivers to finish printing the job to the printer. The UPD can be used with the Citrix Universal Print Server (UPServer) to extend this functionality to network printers.
	



	Decision: Printer Routing



	Print jobs can be routed along different paths: through a client device or through a print server.
 


	
		Client Device Routing—Client devices with locally attached printers (printers attached through USB, LPT, COM, TCP, etc.) route print jobs directly from the client device to the printer.
	
	
		Windows Print Server Routing – Print jobs sent to auto-created network-based printers will be routed from the user’s session to the print server by default. However, the print job will take a fallback route through the client device when any of the following conditions are true:
		
			
				The session cannot contact the print server
			
			
				The print server is on a different domain without a trust established
			
			
				The native print driver is not available within the user’s session
			
		
	
	
		Citrix Universal Print Server Routing – Print job routing follows the same process as Windows Print Server Routing except that the Universal Print Driver is used between the user’s session and the Citrix Universal Print Server.
	



	The specifics of print job routing are based on the printer provisioning method. Auto-created and user-added printers can route print jobs based on the following diagrams:
 


	
 


	However, if the printers are provisioned as session printers, the print job routing options change slightly. The jobs can no longer route through the user’s endpoint device.
 


	
 


	The recommended option is based on the network location of the endpoint device, the user’s
 


	session and the print server
 


	
		Client Device Routing

		
			
				Use for locally attached printer implementations.
			
			
				The use of a Windows endpoint device and printer are on the same high-speed, low-latency network as the Windows Print Server.
			
		
	
	
		Windows Print Server Routing
		
			
				Use if the printer is on the same high-speed, low-latency network as the Windows Print Server and user session.
			
		
	
	
		Windows Print Server Routing (with Universal Print Server)
		
			
				Use if non-Windows endpoint devices and printers are on the same high-speed, low-latency network as the Windows Print Server.
			
		
	



	Decision: Printer Server Redundancy



	Network-based printers managed with a Microsoft print server or the Citrix Universal Print Server should be configured with redundancy to eliminate a single point of failure. The Citrix Universal Print Server redundancy should be defined within a Citrix Policy.
 


	Applications



	Properly integrating an application requires understanding compatibility and how the user/business requirements influence the appropriate delivery method.
 


	Decision: Compatibility



	VDI typically requires significant changes to an organization’s application delivery and management strategy. For example, many organizations will take the opportunity to upgrade their desktop operating system and simplify management by reducing the number of applications installed into the base image using techniques such as application streaming and application layering. These are significant changes that require comprehensive compatibility testing. Important compatibility requirements that may need to be verified include:
 


	
		Operating System - The application must be compatible with the preferred operating system.
	
	
		Multi-User—Some applications may be more appropriate for delivery via a hosted shared desktop or Windows App. In these situations, the application's compatibility must be verified against the multi-user capabilities of a server operating system like Windows Server 2022.
	
	
		Interoperability—Some applications may experience complications if they coexist on the same operating system. Possible causes include shared registry hives, dlls, INI files, and incompatible dependencies. Application interoperability issues should be identified so that appropriate remediation steps can be taken or an alternative delivery model can be selected.
	
	
		Dependency—Applications may need to interact with each other to provide users with a seamless experience. For example, applications that present information in PDF format require a suitable PDF viewer. The dependent (child) applications are often version-specific to the parent application.
	
	
		Application virtualization—Application virtualization techniques, like streaming and layering, help simplify image management by reducing the number of applications installed in the base image. However, not all applications are suitable for streaming and layering because they may install device drivers, use COM+, or form part of the operating system.
	



	Application compatibility can be achieved by combining manual user testing, utilizing pre-verified lists maintained by the software vendor, or using an automated application compatibility solution, which runs through thousands of tests to verify compatibility.
 


	Decision: Application Delivery Method



	It is unlikely that a single delivery method will meet all requirements. Several application delivery methods can be considered based on the outcome of the application categorization assessment process and the overall image management strategy (installed images, scripted images, and layered images).
 


	Choosing one of the appropriate application delivery methods helps improve scalability, management, and user experience.
 


	
		Installed app—The application is part of the base desktop image. The installation process involves copying dll, exe, and other files to the image drive and modifying the registry.
	
	
		Streamed App (Microsoft App-V): The application is profiled and delivered to desktops across the network on demand. Application files and registry settings are placed in a virtual desktop container and isolated from the base operating system and each other, which helps to address compatibility issues. Microsoft App-V is scheduled for end-of-life in April 2026.
	
	
		MSIX App Attach—MSIX app attach enables you to attach applications from an application package to user sessions dynamically. Applications aren't installed locally on session hosts or images, making creating custom images for your session hosts easier and reducing operational overhead and costs for your organization. Delivering applications with MSIX app attach also gives you greater control over which applications your users can access in a remote session.
	
	
		Layered App (Citrix App Layering) – Each layer contains a single application, agent, or operating system. Layering simplifies ongoing maintenance, as an OS, agent, and application exist in a single layer; update the layer, and all deployed images containing that layer are updated. App Layering has two different delivery options:
		
			
				Layered Image – An administrator can easily create new, deployable images by integrating one OS layer, one platform layer (Citrix Virtual Apps and Desktops VDA, Provisioning Services agent), and many application layers.
			
			
				Elastic Layer—A Citrix Virtual Apps and Desktops user can dynamically receive a new app layer based on logon. On a Virtual App host, an elastic layer is session-aware, where an attached layer is only available to a user’s session if the user is granted access to the layer.
			
		
	
	
		Hosted Windows App - An application installed on a multi-user Virtual Apps host and deployed as an application, not a desktop. A user accesses the hosted Windows app seamlessly from the VDI desktop or endpoint device, hiding the fact that the app executes remotely.
	
	
		Local App – An application deployed on the endpoint device. The application interface appears within the user’s hosted VDI session even though it executes on the endpoint.
	



	This table recommends the preferred approaches for integrating applications into the solution.
 


	
		
			
				
					App Category
				 
			
			
				
					Installed App
				 
			
			
				
					Streamed App
				 
			
			
				
					MSIX App Attach
				 
			
			
				
					Layered App
				 
			
			
				
					Hosted Windows App
				 
			
			
				
					Local App
				 
			
		
		
			
				
					Common
				 
			
			
				
					✔
				 
			
			
				
					⭘
				 
			
			
				
					⭘
				 
			
			
				
					✔
				 
			
			
				
					⭘
				 
			
			
				
					X
				 
			
		
		
			
				
					Departmental
				 
			
			
				
					⭘
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
			
				
					X
				 
			
		
		
			
				
					User
				 
			
			
				
					X
				 
			
			
				
					⭘
				 
			
			
				
					⭘
				 
			
			
				
					✔
				 
			
			
				
					⭘
				 
			
			
				
					✔
				 
			
		
		
			
				
					Management
				 
			
			
				
					✔
				 
			
			
				
					X
				 
			
			
				
					X
				 
			
			
				
					✔
				 
			
			
				
					⭘
				 
			
			
				
					X
				 
			
		
	



	✔: Recommended / X: Not Recommended / ⭘: Optional
 


	Virtual Machines



	Virtual resources require proper allocation of the processor, memory, and disk. These decisions directly impact the amount of hardware required and the user experience.
 


	The key to successful resource allocation is ensuring that virtual desktops and applications offer similar performance levels to physical desktops. Otherwise, productivity and overall user satisfaction will be affected. However, allocating resources to virtual machines above their requirements is inefficient and expensive for the business.
 


	The resources allocated should be based on the workload characteristics of each user group identified during the assessment phase.
 


	Decision: Virtual Processor (vCPU)



	For hosted desktop-based VDI models (hosted pooled desktops and hosted static desktops), the general recommendation is for two or more vCPUs per virtual machine to execute multiple threads simultaneously. Although a single vCPU could be assigned for extremely light workloads, users will likely experience session hangs.
 


	Decision: CPU Optimization



	In a shared and virtualized environment, a single user can monopolize CPU resources due to a runaway process or an intense data processing operation in Excel. If the processor is oversubscribed, it cannot fulfill other users’ requests, resulting in a hung session.
 


	Citrix Workspace Environment Management, a Citrix Virtual Apps and Desktops component, incorporates CPU optimization. When a process consumes a certain percentage of the CPU over a defined timeframe, the process priority lowers from normal to low or very low, giving all remaining processes a higher priority and overcoming the runaway process risk. CPU optimization will also remember processes that triggered CPU protection and automatically start the process at a lower priority on future launches.
 


	Most environments should enable CPU optimization as a default configuration.
 


	Decision: Virtual Memory (vRAM)



	The amount of memory allocated to each resource is a function of the user’s expected workload and application footprint. Assigning insufficient memory to the virtual machines will cause excessive paging to disk, resulting in a poor user experience; allocating too much RAM increases the overall cost of the solution.
 


	This table provides guidance on the virtual RAM that should be assigned based on workload.
 


	
		
			
				
					User Workload
				 
			
			
				
					Operating System
				 
			
			
				
					vRAM Configured for Scale
				 
			
		
		
			
				
					Light 
				 
			
			
				
					Windows 10
				 
			
			
				
					4 GB
				 
			
		
		
			
				
					Windows 11
				 
			
			
				
					4 GB
				 
			
		
		
			
				
					Windows Server 2022
				 
			
			
				
					256 MB per user
				 
			
		
		
			
				
					Medium 
				 
			
			
				
					Windows 10 
				 
			
			
				
					8 GB
				 
			
		
		
			
				
					Windows 11
				 
			
			
				
					8 GB
				 
			
		
		
			
				
					Windows Server 2022
				 
			
			
				
					640 MB per user
				 
			
		
		
			
				
					Heavy
				 
			
			
				
					Windows 10
				 
			
			
				
					16 GB
				 
			
		
		
			
				
					Windows 11
				 
			
			
				
					16 GB
				 
			
		
		
			
				
					Windows Server 2022
				 
			
			
				
					1024 MB per user
				 
			
		
	



	 
 


	
		Note:
	 

	
		Windows Server 2022 recommendations are based on the hosted Windows app and hosted shared desktop VDI model.
	 

	
		If used, the Machine Creation Services and Citrix Provisioning cache in RAM should be added to the virtual machine RAM specifications.
	 



	Decision: RAM Optimization



	Even though users only work within a single application at a time, most have five or more applications running idle. When a process moves from active to idle, the application and operating system release a portion of the process’s active working set of memory to free up system resources. However, this is only a small percentage of the applications working set. The rest remains locked for the application, severely limiting available system resources.
 


	Using RAM Optimization within Citrix Workspace Environment Management, idle applications (have not been interacted with by a user) for a certain time are forced to release excess memory until they are no longer idle. When the application returns to an active state, the released memory is loaded back into the active working set.
 


	Most environments should enable RAM optimization as a default configuration. If certain processes encounter issues with optimization, a RAM optimization exclusion list is available.
 


	Decision: Disk Cache



	The amount of storage that each VM requires will vary based on the workload and the image type. If creating a hosted personal desktop without leveraging an image management solution, each VM will require enough storage for the entire OS and locally installed applications.
 


	Deploying machines through Machine Creation Services or Citrix Provisioning can substantially reduce the storage requirements for each virtual machine. Disk space requirements for the write cache, the temporary storage locations for write operations performed by the virtual machines, and difference disk, which captures any changes made to the virtual machine, such as user-installed applications or saved files, will depend on application usage and user behavior. However, the following table provides a starting point for estimating disk space requirements based on a machine sized with vCPU and vRAM per the earlier guidelines.
 


	
		
			
				
					User Workload
				 
			
			
				
					Operating System
				 
			
			
				
					Storage Space (Differencing Disk/ Write Cache Disk)
				 
			
		
		
			
				
					Light
				 
			
			
				
					Windows 10
				 
			
			
				
					10 GB
				 
			
		
		
			
				
					Windows 11
				 
			
			
				
					10 GB
				 
			
		
		
			
				
					Windows Server 2022
				 
			
			
				
					20 GB
				 
			
		
		
			
				
					Medium
				 
			
			
				
					Windows 10
				 
			
			
				
					15 GB
				 
			
		
		
			
				
					Windows 11
				 
			
			
				
					15 GB
				 
			
		
		
			
				
					Windows Server 2022
				 
			
			
				
					60 GB
				 
			
		
		
			
				
					Heavy
				 
			
			
				
					Windows 10
				 
			
			
				
					20 GB
				 
			
		
		
			
				
					Windows 11
				 
			
			
				
					20 GB
				 
			
		
		
			
				
					Windows Server 2022
				 
			
			
				
					60 GB
				 
			
		
	



	 
 


	Decision: RAM Cache



	Provisioning Services and Machine Creation Services can utilize a portion of the virtual machine’s RAM as a buffer for the storage cache. The RAM cache improves the performance of traditional storage by sharing the virtual machine’s non-paged pool memory.
 


	However, the following table provides a starting point for estimating RAM cache requirements based on a machine sized with vCPU and vRAM, per the earlier guidelines.
 


	
		
		
		
		
	
	
		
			
				
					User Workload
				 
			
			
				
					Operating System
				 
			
			
				
					RAM Cache Configured for Scale
				 
			
			
				
					RAM Cache Configured for Experience
				 
			
		
		
			
				
					Light
				 
			
			
				
					Windows 10
				 
			
			
				
					128 MB
				 
			
			
				
					256 MB
				 
			
		
		
			
				
					Windows 11
				 
			
			
				
					128 MB
				 
			
			
				
					256 MB
				 
			
		
		
			
				
					Windows Server 2022
				 
			
			
				
					4 GB
				 
			
			
				
					4 GB
				 
			
		
		
			
				
					Medium
				 
			
			
				
					Windows 10
				 
			
			
				
					256 MB
				 
			
			
				
					512 MB
				 
			
		
		
			
				
					Windows 11
				 
			
			
				
					256 MB
				 
			
			
				
					512 MB
				 
			
		
		
			
				
					Windows Server 2022
				 
			
			
				
					8 GB
				 
			
			
				
					8 GB
				 
			
		
		
			
				
					Heavy
				 
			
			
				
					Windows 10
				 
			
			
				
					512 MB
				 
			
			
				
					1024 MB
				 
			
		
		
			
				
					Windows 11
				 
			
			
				
					512 MB
				 
			
			
				
					1024 MB
				 
			
		
		
			
				
					Windows Server 2022
				 
			
			
				
					10 GB
				 
			
			
				
					10 GB
				 
			
		
	



	 
 


	
		Note: 
	 

	
		The Machine Creation and Provisioning Services cache in RAM should be added to the virtual machine RAM specifications if used. Additionally, if additional RAM is available on the host, the RAM Cache amounts can be increased to provide even greater performance levels.
	 



	 
 


	Decision: Storage IOPS



	Storage performance is limited by the number of operations it can handle per second, referred to as IOPS. Under-allocating storage IOPS results in a VDI desktop with slow-loading apps, web pages, and data.
 


	The following table provides starting guidance on the number of storage IOPS generated per user based on workload and operating system. Storage IO activity will be higher during user logon/logoff.
 


	
		
			
				
					User Workload
				 
			
			
				
					Operating System
				 
			
			
				
					Storage IOPS (without RAM-Based Cache)
				 
			
			
				
					Storage IPS (with RAM-Based Cache)
				 
			
		
		
			
				
					Light
				 
			
			
				
					Windows 10
				 
			
			
				
					12 IOPS
				 
			
			
				
					1 IOPS
				 
			
		
		
			
				
					Windows 11
				 
			
			
				
					12 IOPS
				 
			
			
				
					1 IOPS
				 
			
		
		
			
				
					Windows Server 2022
				 
			
			
				
					4 IOPS
				 
			
			
				
					0.5 IOPS
				 
			
		
		
			
				
					Medium
				 
			
			
				
					Windows 10
				 
			
			
				
					20 IOPS
				 
			
			
				
					1 IOPS
				 
			
		
		
			
				
					Windows 11
				 
			
			
				
					20 IOPS
				 
			
			
				
					1 IOPS
				 
			
		
		
			
				
					Windows Server 2022
				 
			
			
				
					6 IOPS
				 
			
			
				
					1 IOPS
				 
			
		
		
			
				
					Heavy
				 
			
			
				
					Windows 10
				 
			
			
				
					35 IOPS
				 
			
			
				
					3 IOPS
				 
			
		
		
			
				
					Windows 11
				 
			
			
				
					35 IOPS
				 
			
			
				
					3 IOPS
				 
			
		
		
			
				
					Windows Server 2022
				 
			
			
				
					8 IOPS
				 
			
			
				
					1 IOPS
				 
			
		
	



	Decision: IO Prioritization
 


	With shared environments, every user’s IO process receives equal resources. A user running an IO-intensive task can affect mission-critical applications. Citrix Workspace Environment Management allows administrators to define IO priorities for processes.
 


	If a process requires more IO resources or monopolizes IO resources, the process and process priority can be manually increased or decreased via the console. 
 


	Decision: Graphics (GPU)



	The CPU renders graphical processing with software without a graphical processing unit (GPU). A graphical processing unit (GPU) can be leveraged to improve server scalability and user experience or enable graphically intensive applications. During the desktop design, deciding how the GPU (if used) will be mapped to the virtual machines is important. There are three methods available.
 


	
		Pass-Through GPU – Each physical GPU is passed through to a single virtual machine (hosted apps or desktops).
	
	
		Hardware Virtualized GPU – Using a hypervisor’s vGPU technology (NVIDIA, Intel, AMD) the GPU is virtualized and shared between multiple machines. Each virtual machine has the full functionality of GPU drivers and direct access to the GPU.
	
	
		Software Virtualized GPU—The hypervisor manages the GPU and intercepts requests made by the VDI desktops. This process is used if a GPU is not installed within the host.
	



	
		
			
				
					Hypervisor/Hyperscale
				 
			
			
				
					Pass-through GPU
				 
			
			
				
					Hardware Virtualized GPU (NVIDIA)
				 
			
			
				
					Hardware Virtualized GPU (Intel)
				 
			
			
				
					Hardware Virtualized GPU (AMD)
				 
			
			
				
					Software Virtualized GPU
				 
			
		
		
			
				
					XenServer 8
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
			
				
					X
				 
			
			
				
					X
				 
			
			
				
					✔
				 
			
		
		
			
				
					Citrix Hypervisor 8.2 LTSR
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
		
		
			
				
					Microsoft Hyper-V
				 
			
			
				
					✔
				 
			
			
				
					X
				 
			
			
				
					X
				 
			
			
				
					X
				 
			
			
				
					✔
				 
			
		
		
			
				
					VMware ESX
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
		
		
			
				
					Nutanix AHV
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
			
				
					X
				 
			
			
				
					X
				 
			
			
				
					X
				 
			
		
		
			
				
					Microsoft Azure
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
			
				
					X
				 
			
		
		
			
				
					Amazon Web Services
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
			
				
					X
				 
			
			
				
					✔
				 
			
			
				
					X
				 
			
		
		
			
				
					Google Cloud Platform
				 
			
			
				
					✔
				 
			
			
				
					X
				 
			
			
				
					X
				 
			
			
				
					X
				 
			
			
				
					X
				 
			
		
	



	✔: Available / X: Not Supported
 


	User groups that heavily use graphical applications will often require a hardware-virtualized GPU.
 


	Layer 4: The Control Layer



	Active Directory



	Decision: Forest Design



	By default, multi-forest deployments do not have inter-domain trust relationships between the forests. An AD administrator can establish trusting relationships between multiple forests, allowing users and computers from one forest to authenticate and access resources from another.
 


	For forests that have inter-domain trusts, it is recommended that the appropriate settings be configured to allow the Delivery Controllers to communicate with both domains. Citrix Virtual Apps and Desktops sites for each forest must be configured and deployed when the appropriate trusts are not configured. This section outlines the requirements and calculates the size needed to deploy Citrix Virtual Apps and Desktop environments successfully. 
 


	For more information about deploying Citrix Virtual Apps and Desktops in a complex Active Directory environment, please refer to the Citrix Virtual Apps and Desktops product documentation. 
 


	Decision: Organization Unit Structure



	The infrastructure components for a Citrix Virtual Apps and Desktops deployment should reside within their own dedicated organizational units (OUs), separating VDAs and infrastructure servers for management purposes. By having their own OUs and enabling block inheritance for the OU, the objects inside will have greater flexibility with their management while allowing Citrix administrators to be granted delegated control over the appropriate OU.
 


	A sample Citrix OU structure can be seen here:
 


	
 


	Decision: User Groups



	Whenever possible, permissions and authorization should be assigned to user groups rather than individual users, eliminating the need to edit many resource permissions and user rights when creating, modifying, or deleting user accounts.
 


	Permission application example:
 


	
		An application published to one group of 1,000 users requires the validation of only one object for all 1,000 users.
	
	
		The same application published to 1,000 individual user accounts requires the validation of all 1,000 objects.
	



	User groups can be set along application users, business units, or use cases - as long as they can be identified easily. 
 


	Database



	The majority of Citrix products discussed within this document require a database. Citrix Virtual Apps and Desktops requires three SQL server databases:
 


	
		Site: (also known as site configuration) stores the running site configuration, plus the current session state and connection information.
	
	
		Logging: This database (Configuration Logging) stores information about site configuration changes and administrative activities. It is used when the configuring logging feature is enabled (default = enabled).
	
	
		Monitoring: stores data used by Director, such as session and connection information.
	



	Refer to the Citrix Virtual Apps and Desktops product documentation for additional information on the database requirements and how to provide high availability.
 


	Decision: Edition



	There are multiple editions of Microsoft SQL Server: Enterprise, Standard, Web, Developer, and Express. Based on the capabilities of the various SQL Server editions available, Standard or Enterprise editions of SQL are used to host the Citrix Virtual Apps and Desktops databases in production environments. 
 


	
		SQL Express should not be used for production databases due to its lack of supportability, scalability, and performance.
	 



	The Standard or Enterprise editions of SQL provide adequate features to meet most environments' needs. For more information on the databases supported by Citrix products, please refer to the Citrix Database Support Matrix. Different versions of Citrix products support different versions of the SQL server; therefore, it is important to check the support matrix to ensure the version of SQL server used is compatible with the Citrix product being deployed.
 


	Decision: Database Server Sizing



	The SQL server must be sized correctly to ensure an environment's performance and stability. Since every Citrix product uses the SQL server differently, no generic, all-encompassing sizing recommendations can be provided. Instead, per-product SQL server sizing recommendations are provided below.
 


	Citrix Virtual Apps and Desktops Delivery Controllers use the database as a message bus for broker communications, storing configuration data, and monitoring and configuration log data. Databases are constantly being used, and the performance impact on the SQL server can be considered high.
 


	Based on results from Citrix internal scalability testing, the following SQL server specifications for a server hosting all Citrix Virtual Apps and Desktops databases are recommended:
 


	
		4 Cores / 4 GB RAM for environments up to 5,000 users
	
	
		4 Cores / 8 GB RAM for environments up to 15,000 users
	
	
		8 Cores / 16 GB RAM for environments with 15,000+ users
	



	The database files and transaction logs should be hosted on separate hard disk subsystems to cope with many transactions. For example, registering 20,000 virtual desktops during a 15-minute boot storm causes ~500 transactions/second, and 20,000 users logging on during a 30-minute logon storm causes ~800 transactions / second on the Virtual Apps and Desktops Site database.
 


	Citrix Provisioning—Besides static configuration data provisioning servers, database servers store runtime and auditing information. Depending on the boot and management pattern, the database's performance impact can be considered low to medium.
 


	Based on this categorization, a SQL server specification of 4 Cores and 4 GB RAM is recommended as a good starting point. The SQL server should be carefully monitored during the testing and pilot phase to determine its optimal configuration.
 


	Decision: Instance Sizing



	When sizing a SQL database, two aspects are important:
 


	
		Database file – Contains the data and objects such as tables, indexes, stored procedures, and views stored in the database.
	
	
		Transaction log file – Contains a record of all transactions and database modifications made by each transaction. The transaction log is a critical component of the database, and if there is a system failure, it might be required to bring the database back to a consistent state. The usage of the transaction log varies depending on which database recovery model is used:
		
			
				Simple recovery—No log backups are required. Log space is automatically reclaimed to keep space requirements small, essentially eliminating the need to manage the transaction log space. Changes to the database since the most recent backup are unprotected. In the event of a disaster, those changes must be redone.
			
			
				Full recovery requires log backups. No work is lost due to a lost or damaged database data file. Data from any arbitrary point in time can be recovered (for example, before application or user error). Full recovery is required for database mirroring.
			
			
				Bulk-logged – Requires log backups. This is an adjunct of the full recovery model that permits
			
		
	



	For further information, refer to  Microsoft SQL Server Recovery Models.
 


	Understanding the disk space consumption for common database entries is important for estimating storage requirements. This section outlines the storage requirements on a per-product basis and provides sizing calculations.
 


	Citrix Virtual Apps and Desktops uses three distinct databases:
 


	
		The Site database contains the static configuration and dynamic runtime data.
	
	
		The Monitoring database contains monitoring data, such as connection and session information, accessible via Director.
	
	
		The Logging database (accessible via Studio) records each administrative change and activity performed within the site.
	



	Site Database
 


	Since the database of a Citrix Virtual Apps and Desktops site contains static configuration data and dynamic runtime data, the size of the database file depends not only on the physical size of the environment but also on user patterns. The following factors all impact the size of the database file:
 


	
		The number of connected sessions
	
	
		The number of configured and registered VDAs
	
	
		The number of transactions occurring during logon
	
	
		VDA heartbeat transactions
	



	Determining the size of the transaction log for the Site database is difficult due to factors that can influence the log, including:
 


	
		The SQL Database recovery model
	
	
		Launch rate at peak times
	
	
		The number of desktops being delivered
	



	During Virtual Apps and Desktops scalability testing, Citrix observed the transaction log growth rate at 3.5MB an hour when the system is idle and a ~32 KB per-user-per-day growth rate. In a large environment, transaction log usage requires careful management and a regular backup to prevent excessive growth. This can be achieved using scheduled jobs or maintenance plans.
 


	Monitoring Database



	The Monitoring database is expected to be the largest of the three databases since it contains historical information about the site. Its size is dependent on many factors, including:
 


	
		Number of Users
	
	
		Number of sessions and connections
	
	
		Number of workers
	
	
		Retention period configuration
	
	
		Number of transactions per second. The monitoring service tends to execute updates in batches. It is rare for the number of transactions per second to go above 20.
	
	
		Regular consolidation calls from the Monitoring service cause background transactions.
	
	
		Overnight processing is carried out to remove data outside the configured retention period.
	



	The transaction log size for the Monitoring Database is very hard to estimate, but scalability testing showed a growth rate of about 30.5 MB an hour when the system is idle and a per-user per day growth rate of ~9 KB.
 


	Logging Database



	The Logging Database is typically the smallest of the three databases. Its size and the size of the related transaction log depend on the daily administrative activities initiated by Studio, Director, or PowerShell scripts. Therefore, its size is difficult to estimate. The more configuration changes are performed, the larger the database will grow. Some factors that can affect the size of the database include:
 


	
		The number of actions performed in Studio, Director, and PowerShell.
	
	
		Minimal transactions occur on the database when no configuration changes occur.
	
	
		The transaction rate during updates. Updates are batched whenever possible.
	
	
		Data is manually removed from the database. Data within the Configuration Logging Database is not subject to any retention policy. Therefore, it is not removed unless done so manually by an administrator.
	
	
		Activities that impact sessions or users include session logoff and reset.
	
	
		The mechanism used for deploying desktops.
	



	In environments not using Machine Creation Services (MCS), the database size tends to fall between 50 and 100 MB. For MCS environments, database size can easily exceed 200MB due to logging all VM build data.
 


	Temporary Database



	In addition to the Site, Monitoring, and Configuration Logging databases, SQL Server provides a system-wide temporary database (tempdb) to store Read-Committed Snapshot Isolation data. Citrix Virtual Apps and Desktops use this SQL Server feature to reduce database lock contention. Citrix recommends that all databases use Read-Committed Snapshot Isolation. 
 


	The size of the tempdb database will depend on the number of active transactions, but it is generally not expected to grow more than a few MBs. The performance of the tempdb database does not impact the performance of Citrix Virtual Apps and Desktops brokering, as any transactions that generate new data require tempdb space. Citrix Virtual Apps and Desktops tend to have short-lived transactions, which help keep the size of the tempdb small.
 


	The tempdb is also used when queries generate large intermediate result sets. Guidance and sizing of the tempdb can be found in this Microsoft article.
 


	Citrix Provisioning



	The Citrix Provisioning farm database contains static configuration and configuration logging (audit trail) data. Please review the Database Sizing section of the Citrix Provisioning pre-install documentation for all the details required for proper database sizing. 
 


	During the Citrix Provisioning farm setup, a database with an initial file size of 20MB and a growth size of 10 MB is created. The database log's initial size is 10 MB and a growth size of 10%. Due to the nature of the data in the Provisioning farm database, the transaction log is not expected to grow very quickly unless a large amount of configuration is performed.
 


	In contrast to Citrix Virtual Apps and Desktops, which also offer the ability to track administrative changes, the related information is not written in a dedicated database but directly in the Provisioning farm database. It is recommended that the audit trail data be archived regularly to limit the size of the Provisioning database.
 


	Decision: Database Location



	The Logging and Monitoring databases are in the Site Configuration database by default. Citrix recommends changing the location of these secondary databases as soon as the site configuration has been completed to simplify sizing, maintenance, and monitoring. All three databases can be hosted on the same server or on different servers. Ideally, these SQL databases are provided enough resources to ensure reliable performance and data integrity for the Citrix environment. For more information, please refer to Change Citrix database locations.
 


	
		Note: 
	 

	
		You cannot change the location of the configuration logging database when mandatory logging is enabled.
	 



	Decision: High-Availability
 


	This table highlights the impact of Citrix Virtual Apps and Desktops and Citrix Provisioning during a database outage.
 


	
		
			
				
					Component
				 
			
			
				
					Impact of Database Outage
				 
			
		
		
			
				
					Site database
				 
			
			
				
					Users cannot connect or reconnect to a virtual desktop unless Local Host Cache is enabled. Local Host Cache allows users to reconnect to their applications and desktops even when the site database is unavailable.
				 
			
		
		
			
				
					Monitoring database
				 
			
			
				
					Director will not display historical data, and Studio cannot be started. Brokering of incoming user requests and existing user sessions will not be affected.
				 
			
		
		
			
				
					Logging database
				 
			
			
				
					If allow changes are allowed when the database is disconnected and has been enabled within Citrix Virtual Apps and Desktops logging preferences, an outage of the configuration logging database will have no impact (other than configuration changes not being logged). Otherwise, administrators cannot make any changes to the site configuration. Users are not impacted. 
				 
			
		
		
			
				
					Provisioning database
				 
			
			
				
					When offline database support is enabled and the database becomes unavailable, the stream process uses a local copy of the database to retrieve information about the provisioning server and the target devices supported by the server. This allows provisioning servers and the target devices to remain operational. However, when the database is offline, the console and the management functions become unavailable: Auto Add target devices, vDisk creation and updates, Active Directory password changes, Stream process startup, Image update service, and PowerShell and MCLI-based management. If offline database support is not enabled, all management functions become unavailable, and the boot and failover of target devices will fail, impacting end users.
				 
			
		
	



	Besides the built-in database redundancy options, Microsoft SQL Server and the underlying hypervisor (in virtual environments) offer many high-availability features. These enable administrators to ensure single server outages will have a minimal impact (if any) on the Citrix Virtual Apps and Desktops infrastructure. The following SQL/Hypervisor high-availability features are available:
 


	
		AlwaysOn Failover Cluster Instances – Failover clustering provides high-availability support for an entire instance of Microsoft SQL Server. A failover cluster combines two or more nodes or servers using a shared storage. A Microsoft SQL Server AlwaysOn Failover Cluster Instance appears on the network as a single computer but has functionality that provides failover from one node to another if the current node becomes unavailable. The transition from one node to the other node is seamless for the clients connected to the cluster. AlwaysOn Failover cluster Instances require a Windows Server Failover Clustering (WSFC) resource group. The number of nodes supported in the WSFC resource group will depend on the SQL Server edition.
	
	
		AlwaysOn Availability Groups – AlwaysOn Availability Groups is an enterprise-level, high-availability disaster recovery solution that enables administrators to maximize the availability of one or more user databases. AlwaysOn Availability Groups require the Microsoft SQL Server instances to reside on Windows Server failover clustering (WSFC) nodes. Like failover clustering, a single virtual IP/network name is exposed to the database users. In contrast to failover clustering, shared storage is not required since the data is transferred using a network connection. Both synchronous and asynchronous replication to one or more secondary servers is supported. Compared to clustering, secondary servers can actively process incoming read-only requests, backups, or integrity checks. This feature can offload user resource enumeration requests to a secondary SQL server in Citrix environments to scale out an SQL server infrastructure. Since the data on active secondary servers can lag multiple seconds behind the primary server, the read-only routing feature cannot be used for other Citrix database requests at this point in time.
	



	This table outlines the recommended high-availability features for Citrix databases.
 


	
		
			
				
					Database Component
				 
			
			
				
					AlwaysOn Failover Cluster
				 
			
			
				
					AlwaysOn Availability Groups
				 
			
		
		
			
				
					Site
				 
			
			
				
					⭘
				 
			
			
				
					✔
				 
			
		
		
			
				
					Logging
				 
			
			
				
					⭘
				 
			
			
				
					✔
				 
			
		
		
			
				
					Monitoring
				 
			
			
				
					⭘
				 
			
			
				
					✔
				 
			
		
		
			
				
					Provisioning
				 
			
			
				
					⭘
				 
			
			
				
					X
				 
			
		
		
			
				
					Session Recording
				 
			
			
				
					⭘
				 
			
			
				
					⭘
				 
			
		
	



	✔: Recommended / X: Not Supported/ ⭘: Viable
 


	Citrix Licensing



	Citrix offers four platform subscription-based licensing options, with the best Citrix and NetScaler functionality tailored to fit your environment.
 


	
		Citrix Universal Hybrid Multi-Cloud—This subscription includes Citrix Virtual Apps and Desktops Premium, Citrix DaaS Premium, 1000 GB of NetScaler throughput with unlimited instances, and Citrix Endpoint Management. It also includes deploying sites and VDAs on the public cloud.
	
	
		Citrix Platform License—This subscription, available by invitation only, includes Citrix Virtual Apps and Desktops Premium, Citrix DaaS Premium, unlimited NetScaler instances and capacity, Citrix Secure Private Access, Citrix Endpoint Management, Citrix Analytics for Performance &amp; Security, and uberAgent.
	
	
		Citrix for Private Cloud – This subscription is for fully on-premises environments only.
	
	
		NetScaler Fixed Capacity - This subscription is for stand-alone fixed-capacity NetScaler instances. 
	



	This table provides additional details about what is included in each Citrix subscription:
 


	
		
			
				
					Citrix Feature
				 
			
			
				
					NetScaler Fixed Capacity
				 
			
			
				
					Citrix for Private Cloud
				 
			
			
				
					Citrix Universal Hybrid Multi-Cloud
				 
			
			
				
					Citrix Platform License
				 
			
		
		
			
				
					IT Managed CR/LTSR App and Desktop Control Plane
				 
			
			
				
					X
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
		
		
			
				
					HDX, Policy Control, Adaptive Authentication, StoreFront
				 
			
			
				
					X
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
		
		
			
				
					Hybrid Multi-Cloud Workload Support
				 
			
			
				
					X
				 
			
			
				
					X
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
		
		
			
				
					Citrix Managed Cloud Control Plane
				 
			
			
				
					X
				 
			
			
				
					X
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
		
		
			
				
					Mobile Device Management
				 
			
			
				
					X
				 
			
			
				
					X
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
		
		
			
				
					NetScaler Application Delivery and Security
				 
			
			
				
					✔
				 
			
			
				
					X
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
		
		
			
				
					NetScaler app and API Observability / NetScaler Console
				 
			
			
				
					X
				 
			
			
				
					X
				 
			
			
				
					✔
				 
			
			
				
					✔
				 
			
		
		
			
				
					Security and Performance Insights with WIEM Integration
				 
			
			
				
					X
				 
			
			
				
					X
				 
			
			
				
					X
				 
			
			
				
					✔
				 
			
		
		
			
				
					Secure Access to Web and SaaS Apps
				 
			
			
				
					X
				 
			
			
				
					X
				 
			
			
				
					X
				 
			
			
				
					✔
				 
			
		
		
			
				
					Enterprise-wide deployment
				 
			
			
				
					X
				 
			
			
				
					X
				 
			
			
				
					X
				 
			
			
				
					✔
				 
			
		
	



	✔: Included / X: Not Included
 


	For more information on Citrix licensing, refer to our product terms. For more information on the features included in each subscription, please view our feature matrix.
 


	Decision: Sizing



	Internal scalability testing has shown that a single virtual license server with two cores and 8 GB of RAM can issue approximately 50-60 licenses per second. Citrix recommends you maintain 50,000 or fewer concurrent connections per License Server. If necessary, the specification of the license server can be scaled up to support a higher number of license requests per second.
 


	Decision: High-Availability



	For a typical environment, a single license server is sufficient. Should the license server become unavailable, dependent Citrix products will enter a 30-day grace period, which provides more than enough time to resolve connectivity issues and/or restore or rebuild the license server.
 


	If the license server and the Citrix product do not communicate within 2 heartbeats (5-10 min), the Citrix product will enter a grace period and allow connections up to 30 days. Once communication with the license server is re-established, the license server will reconcile the temporary and actual licenses.
 


	Citrix supports clustering for the license server if additional redundancy is required. Clustering the License Server lets users continue working during failure, and users cannot detect when one server in a cluster fails over another.
 


	For more information, refer to Citrix Licensing Technical Overview.
 


	Delivery Controllers



	Decision: Server Sizing



	Controller scalability is based on CPU utilization. The more processor cores available, the more a controller can support virtual desktops. Each VDA startup, registration, enumeration, and launch request impacts the controller’s processor. As the storm increases in intensity, the controller's CPU utilization will increase. If the CPU reaches a critical threshold of roughly 80%, the site must either scale up or out.
 


	Adding additional CPU cores to a Controller will lower the overall CPU utilization, thus allowing for greater numbers of desktops supported by a single controller. This is only feasible when dealing with virtualized controllers, as adding virtual CPUs is fairly straightforward. The other alternative is to add another controller to the site configuration. The controller would have the same configuration as other controllers, and the load would be evenly distributed across all controllers, thus helping to reduce the overall load on each single controller.
 


	Testing has shown that a single Delivery Controller, with Local Host Cache enabled, can support more than 10,000 desktops using the following configuration.
 


	
		
			
				
					Component
				 
			
			
				
					Specification
				 
			
		
		
			
				
					Processor
				 
			
			
				
					4 vCPU
				 
			
		
		
			
				
					Memory
				 
			
			
				
					8 GB RAM
				 
			
		
		
			
				
					Network
				 
			
			
				
					10 GBps networking
				 
			
		
		
			
				
					Host Storage
				 
			
			
				
					40GB Shared storage
				 
			
		
		
			
				
					Operating System
				 
			
			
				
					Windows Server 2022
				 
			
		
		
			
				
					Citrix Virtual Apps and Desktops
				 
			
			
				
					2402 LTSR
				 
			
		
	



	The following formula can calculate the number of Delivery Controllers required for a Citrix site.
 


	Number of Controllers = (Number of Active Sessions per Site/10,000) + 1
 


	Decision: High Availability
 


	If the server hosting the Controller is unavailable, users cannot access their virtual desktops or published applications. Therefore, at least two Controllers (N+1 redundancy) should be deployed per zone on different physical servers to prevent this component from becoming a single point of failure. The others can manage connections and administer the site if one controller fails.
 


	The locations of all Controllers are specified on the VDA, allowing it to automatically failover if communication with one controller is unavailable. The VDA checks the following locations in order, stopping at the first place it finds the Controller:
 


	
		A persistent storage location is maintained for the auto-update feature. This location contains controller information when auto-update is enabled and after the VDA successfully registers for the first time after installation.
	
	
		The VDA checks the following locations for its initial registration after installation or when auto-update is disabled.
	
	
		Policy settings (Controllers, Controller SIDs).
	
	
		The Controller information under the VDA ListofDDCs registry key. The VDA installer initially populates these values based on the information specified when installing the VDA.
	
	
		OU-based discovery. This is a legacy method maintained for backward compatibility.
	
	
		The Personality.ini file created by Machine Creation Services.
	



	Citrix recommends utilizing the policy setting  or managing the ListOfDDCs registry key to ensure VDAs connect to the correct Delivery Controllers. This feature will simplify the environment's management by keeping VDAs updated when adding and removing Controllers.
 


	Decision: Local Host Cache



	Even if the SQL database is highly available, there is the risk of not having access to the database if the network connection between the delivery controller and the SQL database fails, which is an important concern for sites that span geographical locations. To overcome this risk, the Delivery Controllers can utilize the Local Host Cache feature that creates a local copy of the SQL database, which can be used only if the Delivery Controller loses contact with the database.
 


	The following must be considered when using Local Host Cache:
 


	
		Elections—When the zones lose contact with the SQL database, an election nominates a single Delivery Controller as master. All remaining controllers go into idle mode. The winner of the election is determined in alphabetical order.
	
	
		Sizing—When using LHC mode, a single Delivery Controller is responsible for all VDA registrations, enumerations, launches, and updates. The elected controller must have enough resources (CPU and RAM) to handle the entire load for the zone. A single controller can scale to 10,000 users, influencing the zone design.
		
			
				RAM – The LHC services can consume 2+GB of RAM depending on the duration of the outage and the number of user launches during the outage.
			
			
				CPU—LHC can use up to 4 cores in a single socket. Because of this, maximizing the number of cores per socket (e.g., multiplying 4 cores by 1 socket if using 4 vCPU) is recommended.
			
			
				Storage – During LHC  mode, storage space increased 1MB every 2-3 minutes, averaging 10 logons per second.
			
		
	
	
		Power Options – Powered-off virtual resources will not start when the Delivery Controller is in Local Host Cache mode. Pooled virtual desktops that reboot at the end of a session are placed into maintenance mode. To override this default behavior, the following PowerShell commands must be run:
	



	Site Wide:
 


	Set-BrokerSite -ReuseMachinesWithoutShutdownInOutageAllowed $true
 


	For each affected Delivery Group, run the following PowerShell command:
 


	Set-BrokerDesktopGroup -Name "name" -ReuseMachinesWithoutShutdownInOutage $true
 


	To enable the Delivery Group setting by default, run the following PowerShell command:
 


	Set-BrokerSite -DefaultReuseMachinesWithoutShutdownInOutage $true
 


	
		Consoles – Studio and PowerShell are unavailable when using LHC mode.
	



	It is recommended to test LHC mode to ensure that everything is working correctly. You can do this by forcing LHC mode. Our Tech Paper has more information about Local Host Cache.
 


	Decision: XML Service Encryption



	In a typical session, the StoreFront server passes credentials to the Citrix XML Service on a Controller. The Citrix XML protocol exchanges all data using clear text, except for passwords transmitted using obfuscation.
 


	If the traffic between the Storefront servers and the Controllers can be intercepted, it will be vulnerable to the following attacks:
 


	
		Attackers can intercept the XML traffic and steal resource set information and tickets.
	
	
		Attackers with the ability to crack the obfuscation can obtain user credentials.
	
	
		Attackers can impersonate the Delivery Controller and intercept authentication requests.
	



	Citrix XML traffic will be isolated on a dedicated physical or virtual datacenter network for most organizations, making interception unlikely. However, for security, it is recommended to use SSL encryption to send StoreFront data over a secure HTTPS connection.
 


	Decision: Server OS Load Management



	Default Load Management policies are applied to all Server OS delivery groups. The default settings specify the maximum number of sessions a server can host at 250 and do not consider CPU and Memory usage. Capping session count does not provide a true indication of load, which can lead to an overburdening of Server OS delivery groups, resulting in a degradation of performance or an underutilization of Server OS delivery groups, resulting in an inefficient usage of resources.
 


	Citrix recommends creating unique “custom” Load Management policies for each Delivery Group based on performance and scalability testing. Different rules and thresholds can be applied to each Delivery Group depending on the different resource bottlenecks identified during testing. For more information on the available load management policy configurations, click here. Refer to Load management policy settings.
 


	If adequate testing cannot be performed before production, Citrix recommends implementing the following “custom“ Load Management policy, which can be applied to all servers as a baseline:
 


	
		CPU Usage - Full Load: 80%
	
	
		CPU usage excluded process priority – Below Normal or Low
	
	
		Memory Usage - Full Load: 80%
	
	
		Memory Usage base load – Report zero load (MBs): 786
	
	
		Concurrent logon tolerance – 2
	
	
		Maximum number of sessions – X
	



	The “Maximum number of sessions” policy is included for capping purposes – this is considered a best practice for resiliency. Organizations can choose an initial value of 250 (denoted by “X‟ above). It is highly recommended that this value and others be customized based on the results from scalability testing.
 


	Cloud Connector



	Citrix DaaS within Citrix Cloud utilizes a set of services contained within the Citrix Cloud Connector. These services allow communication between the VDAs, StoreFront (if applicable), and the cloud-based Delivery Controllers. Redundant Cloud Connector virtual machines must be placed in each data center/resource location containing VDA hosts.
 


	Traffic from the Cloud Connectors to Citrix Cloud is encrypted by default. Similar to a delivery controller, it is recommended that traffic between the Cloud Connectors and the other infrastructure components be encrypted. Cloud Connectors also have the same Local Host Cache considerations as Delivery Controllers.
 


	Decision: Server Sizing



	Cloud Connector scalability is based on CPU utilization. The more processor cores available, the more virtual desktops a cloud connector can support. Each desktop startup, registration, enumeration, and launch request affects the cloud connector’s processor. The cloud connector's CPU utilization will increase as the storm intensifies. If the CPU reaches a critical threshold of roughly 80%, the site must either scale up or out.
 


	Citrix internal testing has shown that using the following configuration, with Local Host Cache enabled, a single resource location with one Cloud Connector can support up to 10,000 VDAs.
 


	
		
			
				
					Component
				 
			
			
				
					Specification
				 
			
		
		
			
				
					Number of VMs (with N+1 Fault Tolerance)
				 
			
			
				
					3
				 
			
		
		
			
				
					Processors per VM
				 
			
			
				
					4 vCPU
				 
			
		
		
			
				
					Memory per VM
				 
			
			
				
					8 GB RAM
				 
			
		
	



	 
 


	
		Note: 
	 

	
		Citrix recommends three Cloud Connectors in each resource location to maintain a highly available connection to Citrix Cloud during Cloud Connector updates. 
	 



	If you are using Citrix Workspace and Citrix Gateway services, it is recommended that you implement the Rendezvous protocol to reduce the traffic handled by the Cloud Connector. Rendezvous enables the VDAs to talk to Citrix Cloud directly, thus bypassing the Cloud Connectors. However, this does require the VDAs to have access to certain Citrix Cloud URLs via the Internet.
 


	Cloud Connector Topology



	Cloud Connectors are needed in each Resource Location in your site. Resource Locations often represent different data centers, hardware, or public cloud subscriptions. For example, if you had two on-premises data centers and one Azure subscription, you would need at least three Resource Locations.
 


	There are limits to the number of domains, machines, sessions, and hosting connections that can be supported in a single Resource Location. If your Resource Location exceeds those limits, it will need to be split into multiple Resource Locations within Citrix Cloud.
 


	Connector Appliance for Cloud Services



	The Connector Appliance is also used to connect your environment to Citrix Cloud. The Connector Appliance is a Linux-based appliance that does not broker DaaS connections. Instead, it provides the following functions:
 


	
		Connecting Active Directory to Citrix Cloud, enables AD management, allowing the use of AD forests and domains within your resource locations. It removes the need to add any additional AD trusts.
	
	
		Image Portability Service -simplifies the management of images across platforms. This feature is useful for managing images between an on-premises resource location and one in a public cloud. The Citrix Virtual Apps and Desktops REST APIs can be used to automate the administration of resources within a Citrix Virtual Apps and Desktops site.
	
	
		Citrix Secure Private Access - enables administrators to provide a cohesive experience that integrates single sign-on, remote access, and content inspection into a single solution for end-to-end access control.
	



	The Connector Appliance platform is part of Citrix Cloud Platform and Citrix Identity Platform and can process data, including the following information:
 


	
		IP addresses or FQDNs
	
	
		Device, user, and resource location identifiers
	
	
		Timestamps
	
	
		Event data
	
	
		User and group details from Active Directory (for example, used for authenticating and searching for users and groups)
	



	Citrix Provisioning



	Citrix Provisioning uses streaming technology to simplify the deployment of virtual and physical machines. Computers are provisioned and re-provisioned in real-time from a single shared disk image. In doing so, administrators can completely eliminate the need to manage and patch individual systems. Instead, all image management is performed on the master image.
 


	Decision: Platform



	Citrix Provisioning is supported on various platforms, including hypervisors and hyperscalers. For on-premises hypervisors managed by the customer, Citrix is committed to supporting VMware, Nutanix, Microsoft, and XenServer. This support article covers more details about the supported versions.
 


	Citrix Provisioning is also supported on Azure and GCP. The next section will discuss public cloud considerations in greater detail.
 


	Decision: Public Cloud



	When building out a PVS deployment in the public cloud, there are some additional considerations.
 


	Microsoft Azure: 
 


	Knowing the subscription limits is important when building out deployments in Azure. A single subscription can only create 5,000 VMs, so a hub-and-spoke model will be required if your PVS farm exceeds that limit. Additionally, ensure that the subnet sizes are large enough to scale for the PVS farm. You can find more deployment guidance in our Reference Architecture. 
 


	
 


	There are some limitations to using PVS on Azure.  For example, BDM boot is the only boot option that is supported. If you plan to use Azure File Services to provide storage for vDisks, you must create a Premium Storage Account. Additionally, the PVS API is not supported, and there are GUI and wizard limitations. From a VM perspective, you must create template VMs in each Azure region you wish to use. At this time, only standard SSD is supported. You can find other requirements in our product documentation.
 


	Google Cloud Platform
 


	When building out deployments in GCP, it’s important to know the subscription limits. A single project can only create 3000 VMs, so a hub-and-spoke model will be required if your PVS farm exceeds that limit. Additionally, ensure that the subnet sizes are large enough to scale for the PVS farm.
 


	
 


	It is important to note that PVS in GCP supports only server operating systems. Windows 10, Windows 11, and sole tenant nodes are not supported. Additionally, power management of target devices cannot be done via the PVS console. You can find other requirements in our product documentation.
 


	Decision: Topology



	A Citrix Provisioning farm represents the top level of the Provisioning infrastructure, which can be further broken down into sites. All provisioning servers in a farm share the same SQL database and Citrix license server.
 


	Each site is a logical entity containing provisioning servers, vDisk pools, and target device collections. Although all sites within a farm share the same database, target devices can only fail over to other provisioning servers within the same site.
 


	
 


	Some factors must be considered when determining the overall Citrix Provisioning topology:
 


	
		Network—Provisioning servers constantly communicate with the farm database to retrieve system configuration settings. Therefore, separate farms should be created for each physical location where target devices reside unless they are connected to the database server by a fast and robust connection.
	
	
		Administration—Organizations may need to separate departmental, regional, or countrywide administrative duties. Additional Citrix Provisioning farms will add some complexity to the environment's management. However, this overhead is typically limited to initial configuration, desktop creation, and image updates.
	
	
		Organization—A practical reason for building multiple sites is organizational changes. For example, two companies may have recently merged through acquisition but must keep resources separate while integration occurs. Configuring the organization to use separate sites is one way to keep the businesses separate but managed centrally through the Citrix Provisioning console.
	
	
		Platform—As discussed above, PVS is supported on various hypervisors, Azure, and GCP. Separate farms should be created for each platform unless they are connected to the database server by a fast and robust connection.
	



	Only create additional sites if the business requirements warrant it. A single site per farm is easier to manage and requires no additional configuration.
 


	Decision: Device Collections



	Device collections provide the ability to create and manage logical groups of target devices. Creating device collections simplifies device management by allowing actions at the collection level rather than the target device level.
 


	
 


	Device collections can represent physical locations, subnet ranges, chassis, or different organizational departments. They can also be used to separate production target devices from test and maintenance ones logically.
 


	Consider creating device collections based on vDisk assignment so that the status of all target devices assigned to a particular vDisk can be quickly identified.
 


	Decision: High Availability



	Citrix Provisioning is a critical component of the virtual desktop infrastructure. These recommendations should be followed to eliminate single points of failure.
 


	
		Provisioning Server – At least two provisioning servers should always be implemented per site. Sufficient redundancy should be incorporated into the design so that a single server failure does not reduce the number of target devices supported per site. Citrix recommends an N+1 approach; no more than 2,000 VDA servers are handled per Citrix Provisioning server. When limited to 2,000 VDAs per server, if one server is unavailable, the other N servers pick up the total load for all VDAs.
	
	
		DNS Alias FQDN—Consider using a DNS Alias FQDN to locate the PVS servers for the initial logon process. Using this method, up to 32 servers can participate in the initial login process to assure high availability during boot up.
	
	
		vDisks and Storage—For vDisk stores hosted on local, Direct-Attached Storage (DAS), or Storage Area Network (SAN), replication should synchronize the vDisks. If using Network-Attached Storage (NAS), ensure the vDisks are hosted on a highly available network share.
	



	
		Networking—Provisioning Servers need a minimum of 1GB of network throughput; however, when streaming modern versions of Windows it is recommended to use 20 to 40GB NICs. Additionally, if the Provisioning Servers are virtual machine, it is recommended that hardware that supports SR-IOV is used to provide the best throughput.
	



	
		Design Tip: 
	 

	
		Consider putting all Citrix Provisioning servers and VDAs in a site using the same streaming subnet so no routing is required.
	 



	Trivial File Transfer Protocol (TFTP) is a communications protocol for transferring configuration or boot files between machines. Provisioning services can use TFTP to deliver the bootstrap file to target devices. There are several options available to make the TFTP service highly available. Some of the more commonly used options are:
 


	
		DNS Round Robin – A DNS entry is created for the TFTP service with multiple A records corresponding to the TFTP services running on the provisioning servers in the farm. This method is not recommended since the state of the TFTP service is not monitored. Clients could potentially be sent to a non-functioning server.
	
	
		Hardware load balancer—Use a hardware load balancer like NetScaler to create virtual IPs corresponding to the provisioning servers. The NetScaler can intelligently route traffic between the provisioning servers. If one of the servers becomes unavailable, NetScaler will automatically stop routing TFTP requests to that server. This is the best method for making TFTP highly available, but it takes additional time to set up.
	
	
		Multiple DHCP Option 66 entries – This method is easy to implement but requires a DHCP service that supports entering multiple entries in option 66. Microsoft DHCP server allows one option 66 entry, so this method would not be feasible in environments with Microsoft DHCP services. If using a non-Microsoft DHCP server or appliance, check with the manufacturer to verify that multiple option 66 entries are supported.
	



	There are other options available that can achieve the same result without having to use TFTP:
 


	
		Proxy DHCP—The provisioning server's PXE service provides the bootstrap information. If one of the servers is down, the next available server in the farm can provide the bootstrap information. This method requires the provisioning servers to be in the same broadcast domain as the target devices. If other PXE services are running on the network (Altiris, SCCM, etc.), multiple VLANs may be required to keep the PXE services from interfering.
	



	
		Boot Device Manager – Use the Boot Device Manager to create a bootstrap file that is either placed on the local hard drive or used as a bootable ISO file. If the ISO file or bootable hard disk (BDM Boot Disk)  is used, create an ISO file using BDM.exe, upload it to the hypervisor where the VDAs will run, and update the VM template to attach the ISO and change the boot order. Specify PXE boot as the method when running the Virtual Apps and Desktops Setup Wizard. When either method is utilized, the TFTP service is not used at all.
	



	
		Note: 
	 

	
		Boot Device Manager is the only boot method available if you use PVS on Azure or GCP.
	 



	High availability should always be incorporated into the Provisioning Services design. Although high availability may require additional resources and increased costs, it will provide a highly stable environment so that users experience minimal impact due to service outages.
 


	Decision: Bootstrap Delivery



	A target device initiates the boot process by loading a bootstrap program, which initializes the streaming session between the target device and the provisioning server. There are three methods by which the target device can receive the bootstrap program:
 


	Using DHCP Options –
 


	
		When the target device boots, it sends a broadcast for its IP address and boot information. DHCP will process this request and provide an IP and scope option settings 66 (the name or IP address of the Provisioning Services TFTP server) and 67 (the name of the bootstrap file).
	



	
		Note: 
	 

	
		If a load balancer is being used for the TFTP service, the address of the load balancer is entered in option 66.
	 



	 
 


	
		Using TFTP, the target device sends a request for the bootstrap file to the provisioning server, which downloads the boot file from the server.
	
	
		The target device boots the assigned vDisk image.
	



	
		 
		Note: 
	 

	
		To receive PXE broadcasts, the UDP/DHCP Helper must be configured when targets are not on the same subnet as the DHCP servers.
	 



	Using PXE Broadcasts –
 


	
		When a target device boots from the network, it sends a broadcast for an IP address and boot information. DHCP processes this request and provides an IP address. In addition, all provisioning servers that receive the broadcast return the boot server and boot file name information. The target device merges the information received and starts the boot process.
	
	
		Using TFTP, the target device sends a request for the bootstrap file to the provisioning server, which responds first. The target device then downloads the boot file from the provisioning server.
	



	
		Note:
	 

	
		Ensure no other PXE services, such as the Altiris PXE service, are used on the same subnet or isolated using VLANs; otherwise, conflicts may occur with Provisioning Services.
	 

	
		When targets are not on the same subnet as the DHCP and PVS servers, the UDP/DHCP Helper must be configured to receive PXE broadcasts.
	 



	Using Boot Device Manager – The Boot Device Manager (BDM) creates a boot file that target devices obtain through a physical CD/DVD, a mounted ISO image, or a virtual hard disk assigned to the target device. A BDM partition can be upgraded in one of three ways:
 


	
		by collection
	
	
		by a group of highlighted devices
	
	
		by a single device
	



	A summary of the advantages and disadvantages of each delivery method is listed in this table.
 


	
		
			
				
					Delivery Method
				 
			
			
				
					Advantages
				 
			
			
				
					Disadvantages
				 
			
		
		
			
				
					DHCP Options
				 
			
			
				
					Easy to implement
				 
			
			
				
					This requires changes to the production DHCP service, which may only allow one option 66 entry. It also requires a UDP/DHCP helper for targets on different subnets.
				 
			
		
		
			
				
					PXE
				 
			
			
				
					Easy to implement
				 
			
			
				
					It can interfere with other running PXE services on the same subnet. A UDP/DHCP helper is required for targets on different subnets.
				 
			
		
		
			
				
					BDM ISO
				 
			
			
				
					Does not require PXE or TFTP services.
				 
			
			
				
					Booting physical target devices requires extra effort. BDM ISO is regarded as a single point of failure if a single file is used.
				 
			
		
		
			
				
					BDM Boot Disk
				 
			
			
				
					Using a DNS Alias FQDN with the BDM Boot Disk can expand to up to 32 individual PVS server addresses.
				 
			
			
				
					 
				 
			
		
		
			
				
					BDM Partition
				 
			
			
				
					The BDM boot partition upgrade does not require PXE, TFTP, or TSB. 
				 
			
			
				
					Booting physical target devices requires extra effort. An extra 8MB partition is created for each target device.
				 
			
		
	



	 
 


	
		Note:
	 

	
		With UEFI, the choice of initial login server is random, so the load is shared automatically.
	 



	Decision: vDisk Format



	Citrix Provisioning supports the use of fixed-size or dynamic vDisks.
 


	
		Fixed-size disk—For vDisks in private mode, fixed-size disks prevent disk fragmentation and offer improved write performance over dynamic disks.
	
	
		Dynamic disk – Dynamic disks require less storage space than fixed-size disks but offer significantly lower write performance. Although vDisks in Shared mode do not perform writes to the vDisk, the time required to complete vDisk merge operations will increase with dynamic disks. This is not a common occurrence, as more environments choose to create new vDisks when updating.
	



	Since most reads will be to the System Cache in RAM, there is no significant change in performance when utilizing fixed-size or dynamic disks. In addition, dynamic disks require significantly less storage space, so they are recommended.
 


	Decision: vDisk Replication



	vDisks hosted on a local, Direct Attached Storage or a SAN must be replicated between vDisk stores whenever a vDisk is created or changed. Citrix Provisioning supports the replication of vDisks from local stores to the provisioning server and across multiple sites that use shared storage. The replication of vDisks can be performed manually or automatically:
 


	
		Manual – Manual replication is simple but time-consuming, depending on the number of vDisks and vDisk stores. If an error occurs during the replication process, administrators can immediately catch it and take the appropriate steps to resolve it. The risk of manual replication is vDisk inconsistency across the provisioning servers, resulting in load balancing and failover not working properly. For example, if a vDisk is replicated across three servers and then one of the vDisks is updated, that vDisk is no longer identical and will not be considered if a server failover occurs. Even if the same update is made to the other two vDisks, the timestamps on each will differ; therefore, the vDisks are no longer identical.
	
	
		Automated – Automated replication is faster than the manual method due to the required number of vDisks and vDisk Stores for large environments. Some automated tools, such as Microsoft DFS-R, support bandwidth throttling and Cross File Remote Differential Compression (CF-RDC), which use heuristics to determine whether destination files are similar to the replicated file. If so, CF-RDC will use blocks from these files to minimize the data transferred over the network. The risk of automated replication is that administrators do not typically monitor replication events in real-time and do not respond quickly when errors occur unless the automation tool has an alerting feature. Some tools can be configured to automatically restart the copy process in case of failure. For example, Robocopy supports “resume copying” if the network connection is interrupted.
	



	For medium and large projects, use a tool to automate vDisk replication. Select a tool that can resume from network interruptions, copy file attributes, and preserve the original timestamp.
 


	
		Note: 
	 

	
		Load balancing and high availability will not work unless the vDisks have identical timestamps.
	 



	Decision: Server Sizing



	Generally, a Citrix Provisioning server is defined with the following specifications:
 


	
		
			
				
					Component
				 
			
			
				
					Specification
				 
			
		
		
			
				
					Model
				 
			
			
				
					Virtual
				 
			
		
		
			
				
					Processor
				 
			
			
				
					4 to 8 vCPU
				 
			
		
		
			
				
					Memory
				 
			
			
				
					2GB + (# of vDisks * 2GB)
				 
			
		
		
			
				
					Network
				 
			
			
				
					10+ Gbps NIC / SR-IOV support if Citrix Provisioning server runs as a Virtual Machine
				 
			
		
		
			
				
					Host Storage
				 
			
			
				
					40 GB shared storage
				 
			
		
		
			
				
					vDisk Storage
				 
			
			
				
					Depending on the number of images/revisions
				 
			
		
		
			
				
					Operating System
				 
			
			
				
					Windows Server 2022
				 
			
		
	



	 
 


	Model - Citrix Provisioning can be installed on virtual or physical servers:
 


	
		Virtual – Offers rapid server provisioning, snapshots for quick recovery or rollback scenarios, and the ability to adjust server resources on the fly. Virtual provisioning servers allow target devices to be distributed across more servers, helping to reduce the impact of server failure. Virtualization makes more efficient use of system resources.
	
	
		Physical – Offers higher levels of scalability per server than virtual servers. Physical provisioning servers mitigate the risks of virtual machines competing for underlying hypervisor resources.
	



	Virtual provisioning servers are generally preferred when sufficient processor, memory, disk, and networking resources can be made available and guaranteed to be available.
 


	
		Note: 
	 

	
		For high availability, ensure that virtual Provisioning Servers are distributed across multiple virtualization hosts. Distributing the virtual servers across multiple hosts will eliminate a single point of failure and prevent the entire Provisioning Services farm from being brought down in case of a host failure.
	 



	CPU - Citrix Provisioning is not CPU intensive. However, underallocating the number of CPUs does impact the optimization of the network streams. The Streaming Service with default settings and the Citrix Provisioning server can stream up to 4,000 target devices. However, Citrix recommends limiting the streams to 2,000  target devices so that other Provisioning Servers can pick up load if one server is unavailable.
 


	If the provisioning server does not have sufficient cores, the server will show a higher CPU utilization, and target devices waiting for requests to be processed will have a higher read latency.
 


	
		In small environments (up to approximately 500 virtual machines), 4 vCPUs are recommended.
	
	
		In larger environments, 8 vCPUs are recommended.
	



	RAM  -  The Windows operating system hosting Citrix Provisioning partially caches the vDisks in memory (system cache), reducing the number of reads required from storage. Reading from storage is significantly slower than reading from memory. Therefore, Provisioning Servers should be allocated sufficient memory to maximize the benefit of this caching process.
 


	The following formula can be used to determine the optimal amount of memory that should be allocated to a provisioning server: 
 


	2GiB + (vDisk * 2GiB) + 15% (Buffer)
 


	Network—Unlike most Citrix Virtual Apps and Desktops components, Citrix Provisioning does not bottleneck the CPU. Its scalability is based on network throughput.
 


	Determining how much time will be required to boot the target devices can be estimated using the following formula: 
 


	𝑆𝑒𝑐𝑜𝑛𝑑𝑠 𝑡𝑜 𝐵𝑜𝑜𝑡=(𝑁𝑢𝑚𝑏𝑒𝑟 𝑜𝑓 𝑇𝑎𝑟𝑔𝑒𝑡𝑠 ∗𝑀𝐵 𝑈𝑠𝑎𝑔𝑒)Network Throughput
 


	
		Tip: 
	 

	
		Firewalls can add latency and create bandwidth bottlenecks in Provisioning Services environments. If firewalls cannot be avoided, refer to the Citrix Tech Zone article –Communication Ports Used By Citrix Technologies, for the list of ports that should be enabled for full functionality.
	 



	Growth—As the farm grows, administrators must decide whether to add more resources to the provisioning servers or the farm itself.
 


	Many environmental factors need to be considered when determining whether the Provisioning Servers should be scaled up or scaled out:
 


	
		Redundancy – Spreading user load across additional less-powerful servers helps reduce the number of users affected by a single provisioning server failure. If the business cannot accept the loss of a single high-specification server, consider scaling out.
	
	
		Failover times—The more target devices are connected to a single provisioning server, the longer it will take for them to failover if the server fails. Consider scaling out to reduce the time required for target devices to failover to another server.
	
	
		Data center capacity – The datacenter may have limited space, power, and/or cooling. In this situation, consider scaling up.
	
	
		Hardware costs—Scaling up may initially be more cost-effective. However, there will be a point where scaling out actually becomes more cost-effective. A cost analysis should be performed to make that determination.
	



	Hosting costs – There may be hosting and/or maintenance costs based on the number of physical servers used. If so, consider scaling up to reduce the long-term cost of these overheads.
 


	Decision: Network Configuration



	As mentioned before, it is essential that the network is sized correctly to prevent network bottlenecks, which cause high disk access times and directly affect virtual desktop performance. The following diagram outlines a common Provisioning Services network infrastructure:
 


	
 


	The following network configuration is recommended for the network sections outlined within the diagram:
 


	
		PVS Uplink – All disk access from the target devices will be transferred via the PVS network uplink. This means hundreds or even thousands of devices will use this network connection. Therefore, it is vital that this connection is redundant and can failover without any downtime. Furthermore, Citrix recommends a minimum bandwidth of 1Gbps per 500 target devices. A respective QoS quota or a dedicated physical network uplink should be configured for virtual provisioning servers to ensure the best performance.
	
	
		Hypervisor Uplink—This uplink is used by all PVS target devices hosted on a particular hypervisor host. Therefore, redundancy with transparent failover is strongly recommended. Unless the target devices run a very I/O-intensive workload or perform I/O-intensive tasks (e.g., booting) simultaneously, a bandwidth of 1Gbps is sufficient for this uplink.
	
	
		VM Uplink – All network traffic for a virtual machine, including PVS streaming traffic, will traverse this virtual network connection. Unless the workload is extremely I/O intensive, a bandwidth of 100 Mbps can handle even peak loads during I/O intensive tasks, such as booting from vDisk. For example, a Windows 2022 Server will read approximately 232MB from the vDisk for a period of 90 seconds until the Windows Logon Screen is shown. During this period, an average data rate of 20.5 Mbps with peaks up to 90 Mbps can be observed.
	



	The following switch settings are recommended for Citrix Provisioning. They are not required for PVS in Azure or GCP:
 


	
		Storm Control—Storm Control is a feature available on Cisco switches that allows a threshold to be set to suppress multicast, broadcast, or unicast traffic. Its purpose is to prevent malicious or erroneous senders from flooding a LAN and affecting network performance. Provisioning Servers may, by design, send large traffic that falls within a storm control threshold. Therefore, the feature should be configured accordingly.
	
	
		Broadcast Helper – The broadcast helper must direct broadcasts from clients to servers that would otherwise not be routed. In a Provisioning environment, it is necessary to forward PXE boot requests when clients are not on the same subnet as the servers. If possible, the recommended network design is to have Provisioning servers residing on the same subnet as the target devices. This mitigates the risk of any service degradation due to other networking infrastructure components.
	



	Decision: Write Cache



	Because the master image is read-only, each virtual machine has a writable disk to store all
 


	changes. The administrator must decide where to store the write cache disk.
 


	VM-Cache in RAM with Overflow to Disk - A combination of RAM and local storage is used for the write cache. First, writes are stored within the RAM cache, providing high performance. As the RAM cache is consumed, large blocks are removed from the RAM cache and placed onto the local storage write cache disk. This option is recommended as it provides high-performance levels with the low cost of local storage. This is the only option available in Azure.
 


	
 


	VM—Cache in RAM—The RAM associated with the virtual machine holds the write cache drives for each target virtual machine. This option provides high performance due to the RAM's speed. However, the virtual machine will become unusable if the RAM cache runs out of space. To use this option, significant amounts of RAM must be allocated to each virtual machine, increasing the overall cost.
 


	
 


	Decision: Antivirus



	Most antivirus products scan all files and processes by default, significantly impacting Citrix Provisioning performance. For details on how antivirus software can be optimized for Provisioning Services, please refer to CTX124185—Provisioning Services Antivirus Best Practices.
 


	Antivirus software can cause file-locking issues on provisioning servers and VDAs being streamed. To prevent file contention issues, the vDisk Store and write cache should be excluded from antivirus scans. Regular vDisk updates must also be carried out to ensure Antivirus software and Windows updates are up to date and applied.
 


	When a virtual disk runs in standard mode and must be restarted, it downloads the previously loaded virus definitions. This can cause performance degradation when restarting several target devices simultaneously, often causing network congestion while the operation persists. In extreme cases, the target device and provisioning server can become sluggish and consume more resources than necessary. If the antivirus software supports it, definition files should be redirected to the write cache drive to preserve them between reboots.
 


	Machine Creation Services



	Machine Creation Services (MCS) uses disk-cloning technology to simplify the deployment of virtual machines. Computers are provisioned and re-provisioned in real-time from a single shared disk image. In doing so, administrators can eliminate the need to manage and patch individual systems. Instead, administrators perform all image management on the master image.
 


	Decision: Platform



	MCS can be used on various on-premises hypervisors and several hyperscalers. When using different platforms, there are slightly different considerations. 
 


	On-premises hypervisors
 


	Citrix supports XenServer, VMware vSphere, Nutanix Acropolis, and Microsoft SCVMM. A hypervisor administrator account with the appropriate permissions is required when connecting your site to the hypervisor via a hosting connection. The requirements for each vendor are in our product documentation. Additionally, if you are using Nutanix Acropolis, a plug-in is required on your Delivery Controllers or Cloud Connectors. 
 


	Microsoft Azure
 


	Deploying a hosting connection to Azure requires a service principal with the appropriate permissions. Citrix can create the service principal to create the hosting connection, in which case the principal will have Contributor access. Alternatively, you can pre-create a service principal and designate the permissions. A list of minimum permissions can be found in the product documentation.
 


	Resource groups are logical groupings of resources within an Azure subscription. To make them easier to track, it is recommended that you split your machine catalogs into separate delivery groups. You can pre-create resource groups with a desired naming schema, or Citrix can create the resource group while deploying the machine catalog. 
 


	Virtual machine golden images can be created via the Azure Resource Manager when deploying resources via Azure. Once created, these images can be managed via the Azure Compute Gallery, which provides versioning and grouping of resources and global replication across regions. 
 


	Machine profiles can be used within Azure to set various custom properties of VMs. You can view the specific properties that can be set via the machine profile here. A machine profile is required if you are using Entra ID.
 


	Ephemeral disks offer a cost-effective storage option that reuses the local disk of the VMs to host the operating system disk. This functionality is useful for Azure environments that require a higher-performing SSD disk over a standard HDD disk. It is important to note that only certain Azure VMs support ephemeral disks. To learn more about ephemeral disks and their requirements and limitations, please visit our product documentation.
 


	Google Cloud Platform (GCP)
 


	Several APIs need to be enabled when deploying VDAs into Google Cloud. These APIs are the Compute Engine API, Cloud Resource Manager API, Identity and Access Management (IAM) API, and the Cloud Build API.
 


	Deploying machines via MCS requires a hosting connection with appropriate rights to Google Cloud Project resources. The permissions needed depend on when your hosting connection was originally created. View our product documentation for more information.
 


	Similarly to Azure, when you create a catalog to provision machines using Machine Creation Services (MCS), you can use a machine profile to capture the hardware properties from a virtual machine and apply them to newly provisioned VMs in the catalog.
 


	Amazon Web Servies (AWS)
 


	When creating a connection to AWS from the Full Configuration interface, you must provide the API and secret key values, as well as information about your AWS environment, such as region, VPC name, etc. You must also configure appropriate IAM permissions to connect AWS and DaaS.
 


	AWS provides the following options: shared tenancy (the default type) and dedicated tenancy. Shared tenancy means multiple Amazon EC2 instances from different customers might reside on the same physical hardware. Dedicated tenancy means that your EC2 instances run only on hardware with others you have deployed. 
 


	When you create a catalog to provision machines using Machine Creation Services (MCS) in AWS, you select an AMI to represent the master image of that catalog. From that AMI, MCS uses a snapshot of the disk.
 


	Decision: Storage Location



	Machine Creation Services allows administrators to break up a virtual desktop into multiple components and store those pieces on different storage arrays.
 


	 
	Shared Storage - The first option utilizes shared storage for the operating system and the differencing disk.
 


	 
 


	Although this option allows the sharing of the master image across multiple hypervisor hosts, it puts more strain on the storage array because it must also host the differencing disk, which is temporary data.
 


	Hybrid Storage - The second option uses shared storage for the operating system disk and local hypervisor storage for the differencing disk.
 


	 
 


	XenServer IntelliCache Storage - The third option uses shared storage for the operating system disk, local hypervisor storage for the differencing disk, and local XenServer storage for a local cache of the operating system disk.
 


	 
 


	This is only an option for XenServer implementations. It provides the same value as the hybrid storage approach while reducing read IOPS from shared storage. IntelliCache can coexist with the XenServer RAM-based read cache if XenServer RAM is limited.
 


	Public Cloud Storage—Public cloud vendors offer a variety of vendor-managed and customer-managed storage options. Please review our reference architectures for considerations regarding public cloud storage for Azure, AWS, and GCP.
 


	Decision: Cloning Type



	Machine Creation Services incorporates two types of cloning techniques.
 


	
		Thin - Every VM within the catalog utilizes a single, read-only virtual disk for all reads. A
	
	
		second virtual disk, unique for each VM, captures all write IO activity.
	
	
		Full - Every VM within the catalog receives a full copy of the master disk image. Each VM fully owns the disk, allowing for read/write activity. Full cloning technology is only available for personal virtual desktops, where a dedicated virtual machine saves all changes to a local disk.
	



	
		Note:
	 

	
		In the public cloud, there is only full clone mode.
	 



	Consider the following when deciding between thin and full cloning technologies:
 


	
		
			
				 
			
			
				
					Thin Clone
				 
			
			
				
					Full Clone
				 
			
		
		
			
				
					Storage Space Requirements
				 
			
			
				
					Have the greatest storage space savings. A single master disk image is shared across multiple VMs. Only the differencing disk (writes) consumes space, which continues to grow until the VM reboots
				 
			
			
				
					High storage space requirements: Each VM receives a full copy of the master image. The size continues to grow as changes are made to the VM.
				 
			
		
		
			
				
					Backup/Restore
				 
			
			
				
					Difficult—Many third-party Backup/DR solutions do not support snapshot/delta disks, making thin provisioned VMs hard/impossible to backup or move to other storage arrays.
				 
			
			
				
					Easy  - The VM exists within a single virtual disk, making it easy to backup and restore.
				 
			
		
		
			
				
					Provisioning Speed
				 
			
			
				
					Fast - Only requires a single disk image.
				 
			
			
				
					Slow (can be mitigated) - Each VM requires a full copy of the master image. Storage optimization technologies can help mitigate this.
				 
			
		
		
			
				
					Boot Storm
				 
			
			
				
					High Impact - In a boot storm, all differencing disks re-size to hold all writes from Windows startup, placing a high load on the storage as it happens simultaneously.
				 
			
			
				
					Low Impact
				 
			
		
	



	 
 


	Decision: Read Cache



	During boot and logon, virtual desktops incur high storage levels read IOPS, which can strain the underlying storage subsystem. When deployed on XenServer, Shared and Pooled VDI modes utilize a RAM-based read cache hosted on each XenServer.
 


	 
 


	Utilizing this integrated technology reduces read IOPS by 50-80%.
 


	Decision: Write Cache



	During steady state, virtual desktops incur high storage levels of write IOPS, which can strain the underlying storage subsystem. Shared and Pooled VDI modes can utilize a RAM-based write cache using non-paged pool RAM from the virtual machine operating system.
 


	 
 


	Utilizing this integrated technology reduces write IOPS by 95%.
 


	Layer 5: The Compute Layer



	Hardware Sizing



	This section covers hardware sizing for virtual infrastructure servers, virtual desktops, and application hosts. Two methods are typically used to size these servers.
 


	
		The first and preferred way is to plan and purchase hardware based on the workload requirements.
	
	
		The second way is to use existing hardware in the best configuration to support the different workload requirements.
	



	This section will discuss decisions related to both methods.
 


	Decision: Workload Separation



	When implementing a Citrix Virtual Apps and Desktops or Citrix DaaS deployment, the infrastructure, multi-session, or single-session workloads can be separated into dedicated resource clusters or mixed on the same physical hosts. Citrix recommends using resource clusters to separate the workloads, especially in an enterprise deployment. This allows for more targeted host sizing as each workload has unique requirements, such as overcommit ratios and memory usage.
 


	In smaller environments where resource clusters are cost-prohibitive, the workloads may be mixed in a manner that still allows for a highly available environment. Citrix's leading practice is to separate the workloads; however, mixed workloads are a cost-based business decision.
 


	Decision: Physical Process (pCPU)



	The following tables guide the number of virtual desktops supported for light, medium, and heavy workloads per physical core. 
 


	In the first table, single-session desktops are represented and each desktop correlates to a single concurrent user, assuming that the operating system underwent optimization.
 


	
		
			
				
					User Workload
				 
			
			
				
					Operating System
				 
			
			
				
					Desktops per Physical Core
				 
			
			
				
					VM Specs
				 
			
			
				
					VCPU to CPU overcommit
				 
			
		
		
			
				
					Light
				 
			
			
				
					Windows 10
				 
			
			
				
					6
				 
			
			
				
					2 vCPUs/4 GB RAM
				 
			
			
				
					12 vCPUs to 1 CPU
				 
			
		
		
			
				
					Windows 11
				 
			
			
				
					5
				 
			
			
				
					2 vCPUs/4 GB RAM
				 
			
			
				
					10 vCPUs to 1 CPU
				 
			
		
		
			
				
					Medium
				 
			
			
				
					Windows 10
				 
			
			
				
					5
				 
			
			
				
					4 VCPUs /4 GB RAM
				 
			
			
				
					10 vCPUs to 1 CPU
				 
			
		
		
			
				
					Windows 11
				 
			
			
				
					4
				 
			
			
				
					4 vCPUs/4 GB RAM
				 
			
			
				
					8 vCPUs to 1 CPU
				 
			
		
		
			
				
					Heavy
				 
			
			
				
					Windows 10
				 
			
			
				
					2
				 
			
			
				
					6 vCPUs/8 GB RAM
				 
			
			
				
					6 vCPUs to 1 CPU
				 
			
		
		
			
				
					Windows 11
				 
			
			
				
					2
				 
			
			
				
					6 vCPUs/8 GB RAM
				 
			
			
				
					6 vCPUs to 1 CPU
				 
			
		
	



	The second table represents multi-session desktops and the number of users per physical core.
 


	
		
			
				
					User Workload
				 
			
			
				
					Operating System
				 
			
			
				
					Users per Physical Core
				 
			
			
				
					VM Specs
				 
			
			
				
					vCPU to CPU overcommit
				 
			
		
		
			
				
					Light
				 
			
			
				
					Windows Server 2022
				 
			
			
				
					18
				 
			
			
				
					8 vCPUs/ 32 GB RAM
				 
			
			
				
					2 vCPUs to 1 CPU
				 
			
		
		
			
				
					Medium
				 
			
			
				
					Windows Server 2022
				 
			
			
				
					10
				 
			
			
				
					8 vCPUs/ 32 GB RAM
				 
			
			
				
					2 vCPUs to 1 CPU
				 
			
		
		
			
				
					Heavy
				 
			
			
				
					Windows Server 2022
				 
			
			
				
					6
				 
			
			
				
					8 vCPUs/ 32 GB RAM
				 
			
			
				
					2 vCPUs to 1 CPU
				 
			
		
	



	
		Note:
	 

	
		If using Windows Multisession, comparing Windows Server to Windows Multisession workload resulted in 19% fewer task workers and 32% fewer Knowledge workers. This performance decrease is expected because Windows Multisession is a full client version and is not optimized for server-based computing like Windows Server.
	 



	The “Users per Physical Core” estimate is a baseline number running Microsoft Office 365. The baseline number must be adjusted based on specific infrastructure requirements. As a general guideline, the following characteristics are baseline changes to server density.
 


	
		
			
				
					Characteristic
				 
			
			
				
					Server Density Impact
				 
			
		
		
			
				
					Antivirus (not optimized)
				 
			
			
				
					25% decrease
				 
			
		
		
			
				
					Real-time Monitoring
				 
			
			
				
					15% decrease
				 
			
		
		
			
				
					Hyper-threading
				 
			
			
				
					20% decrease
				 
			
		
		
			
				
					Microsoft Office 365
				 
			
			
				
					25% decrease
				 
			
		
	



	To estimate the total number of physical cores required for the Citrix Virtual Apps and Desktops workload, use the following formula for each user group:
 


	Total Virtual Desktops pCPU = ⅀i Usersi/UsersperCorei*(1+AV+ Mon + Off - HT))
 


	Total Virtual Apps pCPU = ⅀i Usersi/UsersperCorei*(1+Av+Mon+Off-HT))
 


	Σ represents the sum of all user group combinations “i.” 
 


	Usersi = Number of concurrent users per user group 
 


	UsersPerCorei = Number of users per physical core 
 


	AV = Antivirus impact (default = 0.25) 
 


	Mon = Monitoring tools impact (default = 0.15)
 


	HT = Hyper-Threading impact (default = .2)
 


	Off = Microsoft Office impact (default = .25)
 


	If workloads will be separated, the formula should be calculated twice, once for all single-session users and the second for all multi-session users in order.
 


	Decision: Physical Memory (pRAM)



	The recommended method for sizing memory to a physical host is to size based on the total memory required to support the virtual machines and the host's CPU capacity. To calculate the total memory required for Citrix Virtual Apps and Desktops, simply multiply the number of virtual machines by the amount of memory allocated to them. The sum of the machine catalogs will be the total RAM required for Citrix Virtual Apps and Desktops hosts. This is shown in the formula below.
 


	𝑇𝑜𝑡𝑎𝑙 Virtual Desktops 𝑝𝑅𝐴𝑀=Σ𝑉𝑀𝑖∗𝑖𝑣𝑅𝐴𝑀𝑖
 


	𝑇𝑜𝑡𝑎𝑙 Virtual Apps 𝑝𝑅𝐴𝑀=Σ𝑉𝑀𝑖∗𝑖𝑣𝑅𝐴𝑀𝑖
 


	Σ represents the sum of all user group combinations “i.” 
 


	VMi = Number of concurrent users per user group 
 


	vRAMi = Amount of RAM assigned to each virtual machine
 


	If workloads will be separated into different hosts (multi-session and single-session workloads), the formula should be calculated for each workload.
 


	Decision: Physical Host (pHost)



	In most situations, the number of physical hosts (pHost) to support the Citrix Virtual App and Desktop workloads will be limited by the number of processor cores available.
 


	The following formula estimates the number of hosts required for the user workloads. The formula is based on the best practice of separating the Virtual App and Virtual Desktop workloads due to the different recommended CPU overcommit ratios for each.
 


	Single Session𝑝𝐻𝑜𝑠𝑡𝑠 =(𝑇𝑜𝑡𝑎𝑙 𝐷𝑒𝑠𝑘𝑡𝑜𝑝 𝑝𝐶𝑃𝑈 𝐶𝑜𝑟𝑒𝑠 𝑝𝑒𝑟 𝑝𝐻𝑜𝑠𝑡 +1) 
 


	Multi Session𝑝𝐻𝑜𝑠𝑡𝑠 =(𝑇𝑜𝑡𝑎𝑙 𝐴𝑝𝑝 𝑝𝐶𝑃𝑈 𝐶𝑜𝑟𝑒𝑠 𝑝𝑒𝑟 𝑝𝐻𝑜𝑠𝑡 +1) 
	 
	The amount of RAM in each host is calculated once the number of physical hosts has been determined based on processor cores. 
 


	Single Session𝑝𝑅𝐴𝑀 𝑝𝑒𝑟 𝑝𝐻𝑜𝑠𝑡 =𝐻𝑦𝑝𝑒𝑟𝑣𝑖𝑠𝑜𝑟𝑅𝐴𝑀+ (𝑇𝑜𝑡𝑎𝑙 𝐷𝑒𝑠𝑘𝑡𝑜𝑝 𝑝𝑅𝐴𝑀 𝐷𝑒𝑠𝑘𝑡𝑜𝑝 𝑝𝐻𝑜𝑠𝑡𝑠−1)
 


	Multi Session𝑝𝑅𝐴𝑀 𝑝𝑒𝑟 𝑝𝐻𝑜𝑠𝑡 =𝐻𝑦𝑝𝑒𝑟𝑣𝑖𝑠𝑜𝑟𝑅𝐴𝑀+ (𝑇𝑜𝑡𝑎𝑙 𝐴𝑝𝑝 𝑝𝑅𝐴𝑀 𝐴𝑝𝑝 𝑝𝐻𝑜𝑠𝑡𝑠−1)
 


	Decision: GPU



	Hosts that deliver graphical workloads require graphics processors to deliver a high-end user experience. Specific hardware hosts and graphics cards are required to support high-end graphics using HDX 3D Pro. An updated list of tested hardware is available in a knowledge base article. Sizing the desktop and application hosts of high-end graphics users should be based on the GPU requirements, ensuring that the host has adequate CPU and memory resources to support the workload.
 


	NVIDIA, AMD, or Intel cards can be leveraged with vGPU profiles to support multiple users. This table provides the GPUs from each that are supported and have been validated, along with sizing guidelines for each.
 


	Storage Sizing



	Decision: Storage Architecture



	The primary storage architectures are as follows:
 


	
		NAS - Provides file-level storage to computer systems through network file shares. The NAS operates as a file server, and NAS systems are networked appliances that contain one or more hard drives, often arranged into logical, redundant storage containers or RAID arrays. Access is typically provided using standard Ethernet and network file-sharing protocols such as NFS, SMB/CIFS, or AFP.
	



	
		Note: 
	 

	
		NAS can become a single point of failure. If the network share becomes unavailable, all target devices streamed from the disk will also be unavailable.
	 



	
		SAN - Dedicated storage network that provides consolidated, block-level storage access. SANs allow computers to connect to different storage devices, so no server has ownership of the storage subsystem, enabling data to be shared among multiple computers. A SAN typically has its own dedicated network of storage devices that are generally not accessible through the network by standard means. A specialized adapter called the Host Bus Adapter (HBA) is required to connect a device to the SAN network. SANs are highly scalable with no noticeable change in performance as more storage and devices are connected. SANs can be a costly investment in capital and the time required to learn, deploy, and manage the technology
	
	
		Hyper-converged - Hyper-converged storage is a part of hyper-converged infrastructure (HCI) which integrates compute, storage, and networking resources in a single appliance. Storage resources are combined and managed through software rather than traditional hardware-based storage systems. These systems are easily scalable and managed alongside other compute and networking resources.
	



	 
 


	Return to Citrix VDI Handbook Home                                                                                                                                                        Next Section &gt; Monitor]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_06/image.png.b6b23f3bcd7a27525ab9d322c0854afb.png" length="45649" type="image/png"/><pubDate>Mon, 01 Jul 2024 13:01:00 +0000</pubDate></item><item><title>Citrix VDI Handbook - Monitor</title><link>https://community.stage.citrix.com/tech-zone/build/tech-papers/citrix-vdi-handbook-monitor/</link><description><![CDATA[Like any integrated system, monitoring and maintenance are critical to the solution's health. Without proper support, operations, and health monitoring systems, the user experience will slowly degrade.
	 

	
		Process 1: Support
	

	
		When problems arise, technical support is the first point of contact. This section addresses the proper staffing, organization, training, delegated administration, and tools for maintaining the Citrix deployment.
	 

	
		Decision: Support Structure
	

	
		Multiple levels of support are the most effective ways of addressing support issues. Low criticality, low complexity, or frequently occurring issues should be managed and resolved at the lower support levels. High criticality and complex issues are escalated to more experienced architects or infrastructure owners. The diagram below outlines a common multi-level support structure.
	 

	
		
	 

	
		If a user encounters an issue, Level-1 support (help desk) is the entry point to the support system. Level-1 should resolve 75% of all issues encountered, of which a majority will be routine problems requiring a limited knowledge of the Citrix environment. At this level, issues are quickly resolved, and some may be automated (self-service), such as password and profile resets.
	 

	
		Non-routine problems that exceed Level-1’s abilities are escalated to Level-2 (Operators). This support level is generally composed of administrators supporting the production Citrix environment. Information on the end user’s problem and attempted troubleshooting steps are documented at the first level, allowing Level-2 technicians to address the problem immediately. Level-2 technicians should handle only about 20% of the support tickets and be highly knowledgeable about the Citrix environment.
	 

	
		Complex issues that exceed Level-2’s abilities should be escalated to Level-3 (Implementers). Level-2 and Level-3 support may often be members of the Citrix Support Team, with Level-3 comprising the senior staff maintaining the Citrix environment. Level-3 issues are complicated and often mission-critical, requiring expert knowledge of the virtual desktop and application environment. Level-3 support tickets should be at least 5% of all support issues.
	 

	
		The final level, Level-4 (Architects), is focused on strategic improvements for the solution, testing new technologies, planning migrations, and other high-level changes. Generally, Level-4 is not involved in active support of a production environment. The Citrix architect should regularly contact architects on other teams, such as security, Active Directory, etc. 
	 

	
		Should support discover an issue related to an application or underlying infrastructure, the ticket is handed to the appropriate team for troubleshooting. The issue is re-escalated if a program bug is discovered and a ticket is established with the appropriate vendor.
	 



	
		Decision: Support Responsibilities and Skill Set
	

	
		This table highlights the recommended characteristics of each support level.
	 

	
		
			
				
					
						Support Level
					 
				
				
					
						Description
					 
				
				
					
						Responsibilities
					 
				
				
					
						Skill Set
					 
				
			
		
		
			
				
					
						Level 1 (Help Desk)
					 
				
				
					
						Provide first-line support for reported issues. Initially, this involves servicing support messages and phone calls. This level must perform initial issue analysis, problem definition, ticket routing, and simple issue resolution. It often processes requests for application access or support with configuring plugins.
					 
				
				
					
						Perform issue definition, initial analysis, and basic issue resolution.
					 

					
						Perform initial troubleshooting to determine the nature of the issue.
					 

					
						Create a ticket, collect user information, and log all troubleshooting steps performed.
					 

					
						Resolve basic Citrix-related, connectivity, and application-related issues using existing knowledge base articles.
					 

					
						Escalate the issue to Level 2 if advanced skills or elevated permissions are required.
					 

					
						Ability to isolate the issue as Citrix-related, Microsoft-related, or third-party Application-related. 
					 

					
						If it affects the production environment or is potentially causing a system-level outage, escalate directly to Level 3.
					 

					
						Generate requests for additional issue resolution guides as necessary.
					 

					
						Follow up with end users when a support ticket is closed to ensure the problem has been addressed.
					 
				
				
					
						
							General Citrix knowledge
						
						
							General Windows client OS/server OS knowledge 
						
						
							General Active Directory knowledge  
						
						
							General Networking Knowledge
						
						
							General troubleshooting and security knowledge
						
					
				
			
			
				
					
						Level 2 (Operators)
					 
				
				
					
						This role primarily supports the day-to-day operations of the Citrix environment, which may include proactive monitoring and management. It should also perform intermediate-level troubleshooting and utilize available monitoring or troubleshooting tools. It will assist with resolving issues escalated by Level-1 support.
					 
				
				
					
						
							Perform intermediate issue analysis and resolution.
						
						
							Identify the root cause of issues. 
						
						
							Respond to server alerts and system outages.
						
						
							Create a weekly report on the number of issues, close rate, open issues, etc.
						
						
							Review vendor knowledge base articles.
						
						
							Respond to out-of-hours helpdesk calls.
						
						
							Respond to critical monitoring alerts.
						
						
							Generate internal knowledge base articles and issue resolution scripts and maintain Level-1 troubleshooting workflows. 
						
						
							Perform basic server maintenance and operational procedures.
						
						
							Manage user profiles and data.
						
						
							Escalate the ticket to Level 3 or the appropriate technology owner if advanced skills or elevated permissions are required.
						
						
							Generate requests for additional issue resolution scripts and knowledge base articles as necessary.
						
						
							Able to read built-in event logs for Windows and Citrix to do basic troubleshooting following public information via Google/Bing.
						
					
				
				
					
						
							Experience with Microsoft Windows Server including but not limited to:
							
								
									Configuring operating system options
								
								
									Understanding Remote Desktop Services policies and profiles
								
								
									Using Active Directory 
								
								
									Creating users/managing permissions and administrator rights
								
								
									Creating and modifying Active Directory group policies
								
							
						
						
							Basic administration skills, including:
							
								
									An understanding of protocols (TCP) o An understanding of firewall concepts
								
								
									 An understanding of email administration and account creation 
								
								
									An understanding of Remote Desktop Services policies and profiles
								
								
									The ability to create shares and give access to shared folders/files
								
							
						
						
							Experience performing the following:
							
								
									Managing, maintaining, monitoring, and troubleshooting Citrix solutions
								
								
									Backing up components in Citrix environments
								
								
									Updating components in Citrix environments
								
								
									Creating reports for trend analysis
								
							
						
					
				
			
			
				
					
						Level 3 (Implementer)
					 
				
				
					
						Central point for implementing, administering, and maintaining Citrix desktop and application virtualization infrastructure. This role focuses on deploying new use cases and leading lifecycle management initiatives. Generally, one Implementer could focus on one use case at a time. For example, three new concurrent use cases would require three Implementers. Escalates issues to software vendor-specific technical support and notifies Level-4 about this issue.
					 
				
				
					
						
							Perform advanced issue analysis and resolution.
						
						
							Perform maintenance and environment upgrades.
						
						
							Addresses high-severity issues and service outages.
						
						
							Manage the Citrix environment.
						
						
							Oversee and lead administrative tasks performed by Level 2.
						
						
							Manage network and storage infrastructure related to the Citrix environment (depending on the company's size or Citrix environment).
						
						
							Review periodic server health reports, resource usage, user experience, and overall environment performance.
						
						
							Review vendor knowledge base articles and newly released updates.
						
						
							Perform policy-level changes and make Active Directory updates.  Review change control requests that impact the Citrix environment.
						
						
							Perform advanced server and infrastructure maintenance.
						
						
							Review knowledge-base articles and issue-resolution scripts for accuracy, compliance, and feasibility
						
						
							 Create knowledge-base articles and issue-resolution scripts to address Level-2 requests.
						
						
							Escalate the ticket to vendor-specific technical support when necessary and notify Level 4 of the issue.
						
					
				
				
					
						
							Knowledge of how the following Windows components integrate with Citrix technologies:
							
								
									Active Directory Domain Services
								
								
									Active Directory Certificate Services
								
								
									Policies
								
								
									Domain Name System (DNS)
								
								
									Dynamic Host Configuration Protocol (DHCP)
								
								
									Group Policy Objects (GPOs)
								
								
									NTFS Permissions
								
								
									Authentication and Authorization
								
								
									Knowledge of IIS
								
								
									Microsoft Windows Operating Systems
								
								
									Roles and features of Windows Server
								
							
						
						
							Knowledge of SQL Clustering and AlwaysOn Availability Groups.
						
						
							General networking skills (i.e., routing, switching)
						
						
							Knowledge of hypervisors
						
						
							Knowledge of public clouds.
						
						
							Knowledge of shared storage configuration and management.
						
					
				
			
			
				
					
						Level 4 (Architect)
					 
				
				
					
						The Level-4 team has minimal exposure to administrative tasks but focuses on scoping, planning, and executing Citrix-specific service and project requests. An architect translates business requirements into a technical design.
					 
				
				
					
						
							Provide technical leadership for upcoming projects.
						
						
							Lead design updates and architecture revisions.
						
						
							Address high-severity issues and service outages.
						
						
							Review periodic server health reports, resource usage, user experience, and overall environment performance to determine the next steps and upgrade paths.  
						
						
							Initiate load testing to determine the capacity of the environment.  
						
						
							Review frequently recurring helpdesk issues.  
						
						
							Ensure technical specifications continue to meet business needs.
						
						
							Update design documentation.
						
					
				
				
					
						
							Advanced architectural assessment and design skills for:
							
								
									Citrix Virtual Apps and Desktops
								
								
									XenServer, VMWare, Hyper-V
								
								
									Azure, AWS, GCP
								
								
									Citrix Provisioning
								
								
									NetScaler
								
								
									Citrix StoreFront
								
								
									Active Directory
								
								
									Storage solutions
								
								
									Networking
								
								
									Application delivery
								
								
									Disaster recovery
								
								
									Policies/policy structures and security restrictions
								
								
									Licensing
								
								
									Methodology
								
							
						
						
							Intermediate knowledge of:
							
								
									General networking skills
								
								
									Change control process
								
								
									Project management
								
								
									Risk assessment
								
							
						
					
				
			
			
				
					
						Vendor Support
					 
				
				
					
						If defects in a program are discovered, vendor assistance may be necessary. At this stage, Level-3 engineers must establish a support ticket with the appropriate vendor to find a solution.
					 
				
			
			
				
					
						Self-Service
					 
				
				
					
						A self-service portal should be utilized for non-critical tasks such as application access, permissions, password resets, etc. The portal can range from a simple FAQ page to a fully automated process requiring no human interaction. The purpose of the self-service portal is to add an additional touch point for end users to address basic issues, preventing the creation of new support tickets.
					 
				
			
		
	

	
		Decision: Certs and Training
	 



	
		The following table details each support level's recommended training, certifications, and experience.
	 

	
		
			
				
					
						Role
					 
				
				
					
						Recommended Training
					 
				
				
					
						Recommended Course(s)
					 
				
				
					
						Recommended Certifications
					 
				
				
					
						Relevant Experience
					 
				
			
		
		
			
				
					
						Help Desk (Level 1)
					 
				
				
					
						 Level-1 support personnel should be provided with basic training on Citrix Virtual Apps and Desktops and/or Citrix DaaS. This can include internal training from subject matter experts or an authorized Citrix Learning Center. The training provided should focus on the following topics:
					 

					
						High-level overview of the Citrix implementation
					 

					
						Using Citrix Director to manage user sessions
					 

					
						Troubleshooting Citrix sessions
					 

					
						Troubleshooting Methodology
					 
				
				
					
						CWS-250: Citrix DaaS Deployment and Administration
					 

					
						 
					 

					
						Course Description
					 
				
				
					 
				
				
					
						1+ years (Entry level also acceptable)
					 
				
			
			
				
					
						Operator (Level-2)
					 
				
				
					
						Level-2 personnel should conduct regular team training sessions to refine administrative skills and ensure a baseline knowledge level across the team. Formalized training is also essential when there are architectural updates to the environment, and the Level-2 team is working with unfamiliar technologies. All members of the Level-2 team should achieve the Citrix Certified Associate (CCA) certification. Advanced training on Windows concepts will also be essential for Level-2 team members who do not have desktop or server support experience. Finally, on-the-job training and close integration with Level-3 administrators is essential as the Level-2 roles are formalized and responsibilities are handed over from Level-3 to Level-2.
					 
				
				
					
						CWS-215: Citrix Virtual Apps and Desktops 7 Administration On-Premises and in Citrix Cloud
					 

					
						 
					 

					
						Course Description
					 
				
				
					
						204 - Citrix Virtual Apps and Desktops 7 Administration
					 

					
						 
					 

					
						Exam Preparation Guide
					 
				
				
					
						2-3 years
					 
				
			
			
				
					
						Implementer (Level-3)
					 
				
				
					
						Level-3 support team members hold at least three years of enterprise experience implementing and supporting Citrix Virtual Apps and Desktops and/or Citrix DaaS, Citrix Provisioning, and Windows operating systems. Level-3 staff should also complete the Citrix Certified Professional (CCP) certification track, which will prepare them to proactively manage the user community and implement Citrix solutions according to Citrix's leading practices.
					 
				
				
					
						CWS-322: Citrix Virtual Apps and Desktops 7 Advanced
					 

					
						 
					 

					
						Course Description
					 
				
				
					
						312 - Citrix Virtual Apps and Desktops 7 Advanced Administration
					 

					
						 
					 

					
						Exam Preparation Guide
					 
				
				
					
						3-4 years
					 
				
			
			
				
					
						Architect (Level 4)
					 
				
				
					
						Experience is essential for Level-4 staff. A qualified Level-4 resource should have a minimum of five years of experience implementing, supporting, and serving in a technology architect role for a Citrix Virtual Apps and Desktops or Citrix DaaS environment, as well as additional administrative experience with integrated technologies such as application and profile management solutions. The ideal candidate will have served in such a capacity in two or more environments for purposes of product exposure and in at least one environment of over 1,200 concurrent users. A Citrix Certified Expert (CCE) certification or comparable training and experience should be a prerequisite for the role.
					 
				
				
					
						CWS-415: Citrix Virtual Apps and Desktops 7 Architect Design Solutions
					 

					
						 
					 

					
						Course Description
					 
				
				
					
						403 - Citrix Virtual Apps and Desktops 7 Assessment, Design, and Advanced Configurations
					 

					
						 
					 

					
						Exam Preparation Guide
					 
				
				
					
						5+ years
					 
				
			
		
	

	
		Decision: Support Staffing
	 



	The following table guides the recommended number of support staff.
 


	
		
			
				
					Role
				 
			
			
				
					Small Environment
				 

				
					Sites: 1
				 

				
					Users: &lt;500
				 

				
					Images: 1-2
				 
			
			
				
					Mid-size Environment
				 

				
					Sites: 1-2
				 

				
					Users: 1000-5000
				 

				
					Images: 3-5
				 
			
			
				
					Large Environment
				 

				
					Sites: 2+
				 

				
					Users: &gt;5000
				 

				
					Images: 5+
				 
			
			
				
					Enterprise Environment
				 

				
					Sites: 2+
				 

				
					Users: &gt;10K
				 

				
					Images: 5+
				 
			
		
		
			
				
					Help Desk (L1)
				 
			
			
				
					3
				 
			
			
				
					5 -10
				 
			
			
				
					15 – 20
				 
			
			
				
					20+
				 
			
		
		
			
				
					Operator (L2)
				 
			
			
				
					1 - 2
				 
			
			
				
					2 - 3
				 
			
			
				
					4 - 5
				 
			
			
				
					5+
				 
			
		
		
			
				
					Implementer (L3)
				 
			
			
				
					1
				 
			
			
				
					1 -2
				 
			
			
				
					2 - 3
				 
			
			
				
					3+
				 
			
		
		
			
				
					Architect (L4)
				 
			
			
				
					1
				 
			
			
				
					1
				 
			
			
				
					1 - 2
				 
			
			
				
					2+
				 
			
		
	



	
		Note: 
	 

	
		This table should only be used as a baseline. Support staffing decisions should be evaluated against an organization's defined requirements, projected workloads, and operational procedures. Multiple levels can be combined. For example, there may be insufficient design projects to have a dedicated architect role, or a more senior member of the Citrix team can act as an Operator and Implementer.
	 



	Decision: Job Aids
 


	The following table details tools that should be made available to all support levels.
 


	
		
			
				
					Tools
				 
			
			
				
					Details
				 
			
		
		
			
				
					Ticket Management System
				 
			
			
				
					Used to document customer information and issues. A typical ticket management system provides the following functionality:  
				 

				
					
						Monitoring the queue of tickets.
					
					
						Setting a limit on the number of open tickets.  
					
					
						Establish thresholds such as how long a certain ticket type should be answered. 
					
					
						Identifying a group of users or individuals who require higher-priority assistance.  
					
					
						Informing the user when their ticket is open, updated, or closed.  
					
					
						Provide an internal knowledge base for the support professionals to search for known resolved issues.
					
				
			
		
		
			
				
					Help Desk Call Scripts
				 
			
			
				
					The first contact help desk personnel should have documented scripts to capture all relevant data while the user is on the phone. This practice also assists in proper triage and allows the next support level to perform research before customer contact. A sample call script is provided for reference in the appendix.
				 
			
		
		
			
				
					Remote Assistance Tools
				 
			
			
				
					Remote assistance tools are useful when troubleshooting user issues. Support technicians and administrators can remotely observe a user’s actions.
				 
			
		
		
			
				
					Knowledge Base Articles
				 
			
			
				
					Documentation should be created and maintained in a knowledge base or library of known issues. Articles should be searchable for quick recovery. Knowledge bases help support staff quickly resolve known issues and reduce the need to perform time-consuming research.
				 
			
		
	



	 
 


	Citrix Support Tools



	The following provides recommendations on the Citrix support tools that should be available to each support level.
 


	Tool: Activity Manager / Support Levels: Self Service
 


	The Activity Manager is a Citrix Workspace and StoreFront feature that allows users to manage their resources by providing quick actions (Disconnect, Log out, Shut Down, Force Quit, Restart) on active applications and desktops from any device within the Citrix Workspace app.
 


	Tool: Citrix Director / Support Levels: L1, L2, L3, L4
 


	Citrix Director is a Citrix Virtual Apps and Desktops tool that overviews hosted desktops and application sessions. It enables support teams to perform basic maintenance tasks and to monitor and troubleshoot system issues.
 


	Tool: Citrix Studio / Support Levels: L1, L2, L3, L4
 


	Citrix Studio enables administrators to perform configuration and maintenance tasks for Citrix Virtual Apps and Desktops site-associated virtual desktops or hosted applications.
 


	Tool: WEM Tool Hub / Support Levels: L2, L3, L4
 


	The Citrix WEM Tool Hub is a collection of tools that aims to simplify the configuration experience for Workspace Environment Management (WEM). It includes the Windows Logon analysis tool, which can be used to view logon duration reports and get tips for optimizing login duration and troubleshooting.
 


	Tool: Citrix Audio Diagnostic Tool: L2, L3, L4
 


	The Citrix Audio Diagnostic Tool is a troubleshooting and monitoring tool for Citrix HDX Audio and can help identify root causes in the Citrix environment and surrounding infrastructure. 
 


	Tool: Citrix Connection Quality Indicator: L2, L3., L4
 


	The Connection Quality Indicator (CQI) is a tool that notifies users of changes to user experience. It can assist users by pointing out issues that degrade the user experience, providing real-time data to find causes for lags in screen refreshes, and reducing the number of calls to help desks related to user experience issues.
 


	Tool: Citrix Insight Services / Support Level: L3, L4
 


	Citrix Insight Services (CIS) is an instrumentation, telemetry, and business insight generation platform. Its instrumentation and telemetry capabilities enable technical users (customers, partners, and engineers) to self-diagnose and fix problems and optimize their environments.
 


	Tool: Citrix Analytics / Support Level: L3, L4
 


	Citrix Analytics solutions allow organizations to detect and deflect potential threats and quickly address performance issues—long before security incidents occur or employees submit help desk tickets. Machine learning and artificial intelligence are used to provide real-time insights into user behavior and automate the process of preventing cybersecurity breaches, all while maintaining a reliable digital workspace experience for employees.
 


	Tool: Citrix Provisioning Console / Support Levels: L3, L4
 


	The Citrix Provisioning Console enables administrators to perform configuration and maintenance tasks for a Citrix Provisioning farm.
 


	Citrix Insight Services



	Administrators can utilize Citrix Insight Services to simplify the support and troubleshooting of the Citrix environment. Citrix Insight Services is run locally to collect environmental information. Online analysis capabilities analyze that information and provide administrators recommendations based on their Citrix environment and configuration. Additional information regarding Citrix Insight Services can be referenced in the Citrix Support article: CTX131233 - FAQ: Citrix Insight  Services.
 


	A full list of the tools Citrix Support provides for troubleshooting can be referenced in the Citrix Supportability Pack.
 


	Citrix AlwaysOn Tracing



	Citrix helps identify connection failures and reduces the need to reproduce a problem. As the name implies, it is “always on,” so traces are constantly captured. When issues occur, they are automatically captured. AlwaysOn Tracing has minimal impact on deployments, and the trace information is compressed as it is collected. The Citrix Telemetry Service retains a maximum of 10 MB of compressed recent trace information, with a maximum time limit of eight days.
 


	Decision: Delegate Administration



	Each support level must have sufficient rights to perform its role effectively. The following tables guide the recommended privileges per support level.
 


	Citrix Virtual Apps and Desktops Delegated Rights



	
		
			
				
					Admin Role
				 
			
			
				
					Description
				 
			
			
				
					Support Level
				 
			
		
		
			
				
					Help Desk Administrator
				 
			
			
				
					Can view Delivery Groups and manage the sessions and machines associated with those groups. Can see the Machine Catalog and host information for the Delivery Groups being monitored. Can also perform session and machine power management operations for the machines in those Delivery Groups.
				 
			
			
				
					L1
				 
			
		
		
			
				
					Full Administrator
				 
			
			
				
					Can perform all tasks and operations. A Full Administrator is always combined with the Entire scope.
				 
			
			
				
					L3, L4
				 
			
		
		
			
				
					Read-Only Administrator
				 
			
			
				
					It can see all objects in specified scopes in addition to global information but cannot change anything. For example, a Read Only Administrator with Scope=London can see all global objects (such as Configuration Logging) and any London-scoped objects (for example, London Delivery Groups). However, that administrator cannot see objects in the New York scope (assuming that the London and New York scopes do not overlap).
				 
			
			
				
					L1, L2
				 
			
		
		
			
				
					Machine Catalog Administrator
				 
			
			
				
					Can create and manage machine catalogs and provision the machines for them. Can build Machine Catalogs from the virtualization infrastructure, Provisioning Services, and physical machines. This role can manage base images and install software but cannot assign applications or desktops to users.
				 
			
			
				
					L2, L3, L4
				 
			
		
		
			
				
					Delivery Group Administrator
				 
			
			
				
					Can deliver applications, desktops, and machines; can also manage the associated sessions. Can also manage application and desktop configurations such as policies and power management settings.
				 
			
			
				
					L2, L3, L4
				 
			
		
		
			
				
					Host Administrator
				 
			
			
				
					Can manage host connections and their associated resource settings. Cannot deliver machines, applications, or desktops to users.
				 
			
			
				
					L2, L3, L4
				 
			
		
	



	Administrators can create or edit custom roles to enable only the necessary permissions for a support level and assign them appropriately. Visit the product documentation for more information on creating and managing custom roles.
 


	Citrix Provisioning Delegated Rights
 


	
		
			
				
					Admin Role
				 
			
			
				
					Description
				 
			
			
				
					Support Level
				 
			
		
		
			
				
					Farm Administrator
				 
			
			
				
					Farm administrators view and manage all objects within a farm, create sites, and manage role memberships throughout the entire farm.
				 
			
			
				
					L3, L4
				 
			
		
		
			
				
					Farm read-only Administrator
				 
			
			
				
					Farm administrators can view all objects within a farm.
				 
			
			
				
					L1
				 
			
		
		
			
				
					Site Administrator
				 
			
			
				
					Site administrators have full management access to all the objects within a site.
				 
			
			
				
					L2
				 
			
		
		
			
				
					Device Administrator
				 
			
			
				
					Device administrators manage device collections to which they have privileges. Management tasks include assigning and removing a virtual disk from a device, editing device properties, and viewing read-only virtual disk properties.
				 
			
			
				
					L2, L3, L4
				 
			
		
		
			
				
					Device operator
				 
			
			
				
					A device operator has administrator privileges to perform target device boot, reboot, and shut down operations.
				 
			
			
				
					L1, L2, L3, L4
				 
			
		
	



	For further information about delegated rights within a Provisioning Site, please refer to Citrix Provisioning Administrative roles.
 


	Citrix StoreFront Delegated Rights



	
		
			
				
					Admin Role
				 
			
			
				
					Support Level
				 
			
		
		
			
				
					N/A
				 
			
			
				
					L1 and L2
				 
			
		
		
			
				
					Local Administrator on StoreFront Server
				 
			
			
				
					L3
				 
			
		
		
			
				
					Full Administrator
				 
			
			
				
					L4
				 
			
		
	



	Users with local administrator rights can view and manage all objects within StoreFront or Web Interface. They can also create new sites and modify existing ones.
 


	Citrix License Server Delegated Rights



	
		
			
				
					Admin Role
				 
			
			
				
					Support Level
				 
			
		
		
			
				
					N/A
				 
			
			
				
					L1
				 
			
		
		
			
				
					N/A
				 
			
			
				
					L2
				 
			
		
		
			
				
					Administrator
				 
			
			
				
					L3 and L4
				 
			
		
	



	The account used during the license server installation becomes the console administrator by default. These accounts are often not intended for regular administration tasks. Please reference this documentation for the steps to change the default administrator.
 


	Process 2: Operations



	This section defines routine operations for the Citrix environment that help to improve stability and performance.
 


	Decision: Administrative Tasks



	The Citrix Support Team should perform regular operations and maintenance tasks to ensure a stable, scalable Citrix environment.
 


	Each operation is categorized by the solution's associated component and the operation's frequency (ongoing, daily, weekly, and yearly). Tasks have been aligned to the roles described within Decision: Support Responsibilities and Skill Set.
 


	If the administrators performing operations are the same the support team, then the designations are linked as follows:
 


	
		Level 2 Support = Operators
	
	
		Level 3 Support = Implementers
	
	
		Level 4 Support = Architect
	



	Daily Periodic Tasks



	The following table outlines the tasks that should be performed by the Citrix Support Team daily.
 


	
		
			
				
					Component
				 
			
			
				
					Task
				 
			
			
				
					Description
				 
			
			
				
					Responsible
				 
			
		
	
	
		
			
				
					Generic
				 
			
			
				
					Review Citrix Director, Windows Performance Monitor, Event Log, and other SIEM alerts
				 
			
			
				
					Check for warnings or alerts within Citrix Director, event logs, or other monitoring software. If an alert occurs, investigate the root cause.
				 
			
			
				
					Operators
				 
			
		
		
			
				
					Verify backups are completed successfully
				 
			
			
				
					Verify all scheduled backups have been completed successfully. This can include but is not limited to:  
				 

				
					
						User data (user profiles/home folders)  
					
					
						Application data  
					
					
						Citrix databases  
					
					
						StoreFront configuration
					
					
						Provisioning Services vDisks (virtual desktops and application servers)
					
					
						XenServer VM/Pool metadata (or equivalent for other hypervisors) 
					
					
						Dedicated virtual desktops
					
					
						License files
					
				
			
			
				
					Operators
				 
			
		
		
			
				
					Test environment access
				 
			
			
				
					Simulate an internal and external connection to ensure desktop and application resources are available before most users log on for the day. This should be tested throughout the day and may even be automated.
				 
			
			
				
					Operators
				 
			
		
		
			
				
					Citrix Virtual Apps and Desktops
				 
			
			
				
					Virtual machine power checking
				 
			
			
				
					Verify that the appropriate number of idle desktops and application servers are powered on and registered with the Delivery Controllers to ensure availability for user workloads.
				 
			
			
				
					Operators
				 
			
		
		
			
				
					Perform incremental backup of Citrix-related databases
				 
			
			
				
					Perform incremental data backups of the following Citrix databases:
				 

				
					
						Site Database
					
					
						Logging Database
					
					
						Monitoring Database
					
					
						WEM Database           
					
				
			
			
				
					Operators, Database team (if Citrix environment is using a shared SQL)
				 
			
		
		
			
				
					Citrix Provisioning
				 
			
			
				
					Check Citrix Provisioning Server utilization
				 
			
			
				
					If necessary, check the number of target devices connected to the Citrix Provisioning Servers and balance the load across the servers.
				 
			
			
				
					Operators
				 
			
		
		
			
				
					Perform incremental backup of Citrix Provisioning database
				 
			
			
				
					Incremental backup of Citrix Provisioning Server database hosted on SQL Server infrastructure.
				 
			
			
				
					Operators, Database team (if Citrix environment is using a shared SQL)
				 
			
		
	



	Weekly Periodic Tasks
 


	
		
			
				
					Component
				 
			
			
				
					Task
				 
			
			
				
					Description
				 
			
			
				
					Responsible
				 
			
		
	
	
		
			
				
					Generic
				 
			
			
				
					Review the latest hotfixes and patches
				 
			
			
				
					Review, test, and deploy the latest Citrix hotfixes and ascertain whether the Cirix Infrastructure and Server-Based OS / Desktop-Based OS virtual machines require them. 
				 

				
					 
				 

				
					Note: Any required hotfixes should be tested using the recommended testing process before implementation in production.
				 
			
			
				
					Operators, Implementers (review process)
				 
			
		
		
			
				
					Create Citrix environmental baselines and a status report
				 
			
			
				
					Create a report on overall environment performance (server health, resource usage, user experience) and the number of Citrix issues (close rate, open issues, and so on).
				 
			
			
				
					Operators
				 
			
		
		
			
				
					Periodically Review the status report
				 
			
			
				
					Review the Citrix status report to identify any trends or common issues.
				 
			
			
				
					Implementers, Architect
				 
			
		
		
			
				
					Maintain internal support knowledge base
				 
			
			
				
					Create knowledge-based articles and issue resolution scripts to address Level-1 and Level-2 support requests. 
				 

				
					 
				 

				
					Review these articles and scripts for accuracy, compliance, and feasibility.
				 
			
			
				
					Operators (Level-2 requests), Implementers(Level-3 requests and review process)
				 
			
		
		
			
				
					Citrix Virtual Apps and Desktops
				 
			
			
				
					Check Configuration Logging reports
				 
			
			
				
					Confirm Citrix site-wide changes implemented during the previous week were approved through change control.
				 
			
			
				
					Auditors
				 
			
		
		
			
				
					Perform a full backup of Citrix-related databases
				 
			
			
				
					Perform full-data backups of the following Citrix databases:  
				 

				
					
						Site Database  
					
					
						Logging Database
					
					
						Monitoring Database
					
					
						WEM Database
					
				
			
			
				
					Operators, Database team (if Citrix environment is using a shared SQL)
				 
			
		
		
			
				
					Citrix Provisioning
				 
			
			
				
					Check storage capacity (only before updating a vDisk)
				 
			
			
				
					Review storage utilization used and free storage space for the vDisk store and each vDisk.
				 

				
					 
				 

				
					Note: Lack of space within the vDisk repository will be an issue only when the vDisks are updated using versioning or when a vDisk is placed in private mode during an update procedure. Storage utilization within vDisk should also be investigated. For example, a 20GB vDisk may only have 200MB of free storage. If the vDisk itself is limited for storage, it must be extended. Citrix does not support resizing a VHD file. Refer to the Microsoft link Resize-VHD for information on resizing a VHD file.
				 
			
			
				
					Operators
				 
			
		
		
			
				
					Check auditing reports
				 
			
			
				
					Review the Citrix Provisioning Services auditing Logs. Note: Provisioning Server auditing is off by default and can be enabled to record configuration actions on components within the Provisioning Services farm. To enable auditing, refer to Enabling Auditing
				 
			
			
				
					Auditors
				 
			
		
		
			
				
					Perform a full backup of the Citrix PVS database
				 
			
			
				
					Backup of Citrix Provisioning Server database hosted on SQL Server infrastructure.
				 
			
			
				
					Operators, Database team (if Citrix environment is using a shared SQL)
				 
			
		
	



	Monthly Periodic Tasks
 


	The following table outlines the tasks that should be performed by the Citrix Support Team monthly.
 


	
		
			
				
					Component
				 
			
			
				
					Task
				 
			
			
				
					Description
				 
			
			
				
					Responsible
				 
			
		
		
			
				
					Generic
				 
			
			
				
					Perform capacity assessment
				 
			
			
				
					Perform capacity assessment of the Citrix environment to determine environment utilization and scalability requirements.
				 
			
			
				
					Architect
				 
			
		
		
			
				
					Review software upgrades
				 
			
			
				
					Review and assess the requirement for new Citrix software releases or versions. (Quarterly)
				 
			
			
				
					Architect
				 
			
		
	



	Yearly Periodic Tasks



	
		
			
				
					Component
				 
			
			
				
					Task
				 
			
			
				
					Description
				 
			
			
				
					Responsible
				 
			
		
		
			
				
					Generic
				 
			
			
				
					Conduct Citrix policy assessment
				 
			
			
				
					Review Citrix policies and determine whether new policies are required and existing policies need to be updated.
				 
			
			
				
					Implementers
				 
			
		
		
			
				
					Perform Business Continuity Plan (BCP)/ Disaster Recovery (DR) test
				 
			
			
				
					Conduct functional BCP/DR test to confirm DR readiness. This plan should include a yearly restore test to validate that the actual restore process from backup data is functioning correctly.
				 
			
			
				
					Implementers
				 
			
		
		
			
				
					Perform application assessment
				 
			
			
				
					Review the usage of applications outside and within the Citrix environment. Assess the validity of adding additional applications to the Citrix site, removing no longer required applications, or upgrading the applications to the latest version.
				 
			
			
				
					Architect
				 
			
		
		
			
				
					Citrix Provisioning
				 
			
			
				
					Archive audit reports
				 
			
			
				
					Perform an archive of the Citrix Provisioning Server Audit Trail Information for compliance requirements.
				 
			
			
				
					Auditors
				 
			
		
	



	Decision: Backup Location
 


	The location of backups directly affects the recovery time and reliability of the Citrix environment. Critical data backups should be stored both on-site and off-site. If off-site backups are not possible due to the costs associated with them or the sensitivity of the data, backups should be placed at separate physical locations within the same datacenter.
 


	Each backup option is discussed further below.
 


	
		Onsite Backups—Onsite backups should be located on a storage device in the datacenter that will allow the data to be recovered quickly in the event of a failure. They are ideal for issues affecting only a small subset of datacenter hardware. Backups can also be stored on a cold storage solution such as tape. While this medium is slower to recover, it provides additional protection since it is only active during the backup process.
	
	
		Offsite Backups—Although the recovery time is much higher, offsite backups provide additional protection during a disaster. Offsite backups may require transferring data over the Internet to a third-party provider or are created onsite and transported to a remote location on storage mediums such as tape. It is typical to put a limited number of backups offsite, for example, one backup a week or month.
	



	Decision: Testing Process



	Regular updates and maintenance are an everyday part of IT operations. Standard processes must be followed to ensure updates do not negatively impact the production environment. This includes maintaining a dedicated testing infrastructure to validate modifications before production implementation.
 


	Since changes to Citrix infrastructure can impact thousands of virtual desktop and application users, multi-phase testing is critical for the reliability and performance of the environment. As such, the process for testing should resemble the following:
 


	
 


	
		Development—The development infrastructure exists outside of the production network. Typically, it consists of short-lived virtual machines whose configuration matches production as closely as possible. The purpose of the development phase is to provide change requestors with a non-production environment to perform proof of concepts, determine integration requirements, and perform iterative testing as part of a discovery phase. Proposed changes should be documented and applied in the test phase.
	
	
		Testing - The test environment is a standalone 1:1 copy of the production infrastructure and confirms that the proposed changes can be easily repeated before the pre-production staging environment. The changes made should follow documentation from the development stage. If testing fails within the testing stage, the architect must determine the severity of failure and determine whether minor updates to documentation is sufficient or a full development cycle is needed.
	
	
		Pre-production—The pre-production environment should mimic the current production environment. Staging aims to implement the proposed changes with little risk or uncertainty. Any changes to the staging infrastructure are expected to be tested and documented for repeatability. No iterations or adjustments should be required during this phase. During this phase and within this environment, User Acceptance Testing (UAT) should be performed.
	
	
		Production—The production environment is a fully redundant and scalable solution designed for normal use by end users. There should be minimal changes to the environment. All approved changes should be rolled out in stages to the production environment if possible. This process is known as a staged rollout and mitigates risk by allowing changes to be rolled back, if necessary, without entirely using a normal environment.
	



	Decision: Change Control



	Standardized processes that manage changes throughout a system’s lifecycle are necessary to ensure consistent and accountable performance. The following change control leading practices should be considered.
 


	
		Use a change control window so that all applicable parties know when downtime might occur.
	
	
		Ensure all teams are represented in the Change Advisory Board (CAB).
	
	
		Every change should have a rollback plan.
	
	
		If a change fails, have a debrief to determine what went wrong.
	
	
		Always use an automated change control system, so support staff can quickly and easily identify changes.
	
	
		When available, ensure configuration logging is enabled to track any changes made to the Citrix environment.
	



	The change control process should be closely followed, starting with a change request. A change request form should be filled out detailing the changes requested, reasons for the change, and intended timeframes for the action. This is then reviewed and edited if required by a Change Manager and advisory board. When the change request has gone through the entire change approval process, it is given to a change implementer who stages the change for testing and finally conducts the implementation in production.
 


	A sample change control process, including detailed steps, is provided in the diagram below:
 


	
 


	The process is as follows:
 


	
		Any person requesting a change completes the Change Request (CR) form.
	
	
		After appropriate manager approvals have been acquired, the CR is forwarded to the appropriate Change Manager(s).
	
	
		The Change Manager validates the CR for completeness and logs the CR information into the Change Control Log for tracking. Incomplete change requests are returned to the requestor for update and re-submission.
	
	
		The Change Manager assesses the change's impact in conjunction with subject matter experts and/or managers of the teams associated with/affected by it.
	
	
		The Change Manager works with the associated/affected teams and the change requester to confirm the change's priority, category, and type, as well as the proposed rollback plan.
	
	
		If the change is approved by the Change Manager, the CR is forwarded to the CAB for approval. If the change is rejected, the Change Control Log is updated with the current status and the reason for the rejection, and the CR is sent back to the requestor.
	
	
		The CAB reviews and validates the change in detail and discusses and evaluates the purpose, reasons, impact, cost, and benefits. Each board member represents their department and provides guidance on the change requests. The CAB reviews multiple requests to coordinate implementations and “package” requests into a single release schedule.
	
	
		Upon approval, the change is sent back to the change manager to schedule for implementation in the staging environment.
	
	
		The change is implemented, and tests are conducted. The results are sent back to the Change Manager.
	
	
		The change is scheduled for production implementation if the staging implementation and testing are successful. If the staging phase is unsuccessful, another staging iteration will be conducted.
	
	
		The change is rolled out in stages to the production environment if possible. This process is known as a staged rollout and mitigates risk by allowing changes to be rolled back, if necessary, without impacting the entire environment. A rollback plan should be in place if there is an issue implementing a change in the production environment.
	
	
		The Change Manager reviews the implementation and finally updates the Change Control Log.
	
	
		The Change Manager periodically reviews the Change Control Log to identify trends in changes' type, frequency, and size and forwards the results to the CAB for review.
	



	The processes may be expedited in an emergency. Should an issue be declared an emergency, a change request form is still filled out and delivered to the appropriate change management representative. The requested change is immediately implemented when approved, and the advisory board is notified.
 


	Decision: Availability Testing



	Availability testing ensures resources are still available during a failure. These tests are essential to ensure users always have access to business-critical resources. The testing should be conducted during nonbusiness hours or during a scheduled maintenance weekend when appropriate notice has been given to end users to inform them if any unforeseen issues arise.
 


	The following is a list of the key components that should be tested regularly.
 


	
		StoreFront—StoreFront should be load-balanced and health-checked by a NetScaler or other load-balancing device. All but one of the StoreFront servers should be shut down to validate its configuration. This will ensure that the load-balancing device detects the failure and directs users to the functioning server.
	
	
		SQL—The SQL Server should be in a high-availability configuration. The primary SQL server should be taken offline to validate the configuration, and then the Citrix Studio console should be opened. Since Citrix Studio will not be accessible without a functioning SQL server, it will validate that the SQL server failover mechanisms are functioning properly.
	
	
		Controllers—The deployed resources should be configured with a list of multiple Controllers. If one is unavailable, desktops and application hosts will automatically establish a connection to another server in the list. To validate this, shut down one of the controller hosts and determine if the resources initially connected to it are automatically registered to another server. This can be determined by viewing the registration status of the resources inside Citrix Studio.
	
	
		Service Continuity - If using Citrix Workspace, Service Continuity should be enabled. To validate Service Continuity is functioning properly, ensure lease files are downloaded, edit the local host file on your endpoint so that 127.0.01 points to your Workspace URL (&lt;storename&gt;.cloud.com), run ipconfig /flushdns, then exit and relaunch the Citrix Workspace app. Once validated, remove the entry in your local host file. For more information on Service Continuity configuration, see this Citrix Tech Brief.
	
	
		Local Host Cache - Local Host Cache is a combination of several services and components that come together to take over the brokering responsibilities until the connection to the Cloud Broker or SQL database can be reestablished. In DaaS, Local Host Cache engages when Cloud Connectors lose connectivity with Citrix Cloud for 60 seconds. For customer-managed Sites, Local Host Cache engages when Delivery Controllers lose connectivity to the Site database for 90 seconds. Review the information in this Citrix Tech Brief to test your Local Host Cache configuration.
	



	Sample Testing Workflow: Citrix Provisioning
 


	Prerequisites and configuration requirements:
 


	
		Hypervisor, Citrix Virtual Apps and Desktops services are up and running.
	
	
		At least two Citrix Provisioning servers are installed and configured to provide the streamed disk image.
	
	
		Resilient networking and storage infrastructure with multiple links to each server.
	
	
		Test users are active on the Citrix Virtual Apps and Desktops machines.
	



	
		
			
				
					Steps
				 
			
			
				
					Expected Results
				 
			
		
		
			
				
					Citrix Provisioning Server Outage  
				 

				
					
						Shutdown one of the Citrix Provisioning Servers.  
					
					
						Validate Citrix Provisioning continues to function.
					
					
						Restart Citrix Provisioning Server.  
					
					
						Validate connections rebalance between Citrix Provisioning Servers.  
					
					
						Try the other(rest) Citrix Provisioning server(s) individually.
					
				
			
			
				
					
						Existing Citrix Virtual Apps and Desktops machines connect to another PVS server.  
					
					
						There is limited to no impact to the users utilizing that server.  
					
					
						New Citrix Virtual Apps and Desktops machines can be booted and start correctly.  
					
					
						SCOM reports that the PVS server is down / unavailable.  
					
					
						Live connections are rebalanced between both PVS servers once both PVS servers are made available again.
					
				
			
		
		
			
				
					SQL Server Citrix Provisioning Database Outage
				 

				
					
						Admin reboots SQL Servers simultaneously
					
					
						Validate Citrix Provisioning continues to function, but that administration is impossible.
					
					
						Wait for the SQL server to come back online.
					
					
						Validate Citrix Provisioning administrative functions are once again possible.
					
				
			
			
				
					
						Citrix Provisioning continues to function.
					
					
						Citrix Provisioning administrative functions are no longer available.
					
					
						Citrix Provisioning administrative functions are available once the SQL service has been restored.
					
				
			
		
	



	Sample Testing Workflow: Citrix Virtual Apps and Desktops
 


	Prerequisites and configuration requirements:
 


	
		Hypervisor, Citrix Virtual Apps and Desktops services are up and running.
	
	
		Network and storage services are available.
	
	
		Citrix Provisioning provides the streamed disk images.
	
	
		Citrix Workspace Environment Management (WEM) services are up and running.
	
	
		Test users are active on the virtual machines.
	
	
		SQL and Citrix Virtual Apps and Desktops servers are up and running.
	
	
		Ensure multiple StoreFront servers are running.
	
	
		NetScaler load balancing services.
	



	
		
			
				
					Steps
				 
			
			
				
					Expected Results
				 
			
		
		
			
				
					Controller Citrix Broker Service Outage
				 

				
					
						Stop the Citrix Broker Service on one of the Controller servers.
					
					
						Validate virtual desktops or applications can still be enumerated and launched.
					
					
						Start the Citrix Broker Service on the Controller server.
					
					
						Shutdown one of the Controllers.
					
					
						Validate virtual desktops or applications can still be enumerated and launched.
					
					
						When a desktop is launched, determine which Controller owns the host connection. Shut the Controller down and verify that another Controller takes over the session.
					
				

				
					Note: This should be done during the maintenance window. Once complete, the VDI resources should be rebooted to distribute the VDAs evenly across all controllers.
				 
			
			
				
					
						StoreFront correctly identifies service as unavailable and redirects connections to the remaining Controller.
					
					
						Desktops continue to be enumerated and launched successfully.  
					
					
						Launched desktop can be supported if a hosting Controller goes down.
					
				
			
		
		
			
				
					SQL Service Outage:
				 

				
					
						Admin restarts servers in the SQL Always on Availability group.  
					
					
						Validate Citrix Virtual Apps and Desktops continue functioning, but that administration is impossible.
					
					
						Wait for the SQL Service to come back online.  
					
					
						Validate administrative functions are once again possible.
					
				
			
			
				
					
						Existing sessions are not impacted
					
					
						The local host cache allows access to recently used applications, hosted shared desktops, and assigned VDI.
					
					
						Administrative functions are not possible
					
					
						Administrative functions are possible once SQL service is available.
					
				
			
		
	



	Sample Testing Workflow: Citrix License Server
 


	Prerequisites and configuration requirements:
 


	
		Citrix Licensing Server up and running (with valid licenses installed).
	
	
		Hypervisor, Citrix Virtual Apps and Desktops, and StoreFront services are up and running.
	
	
		Users are active on the Server OS or Desktop OS machines.
	



	
		
			
				
					Steps
				 
			
			
				
					Expected Results
				 
			
		
		
			
				
					Service continuity during the complete failure of the Citrix Licensing Server:  
				 

				
					
						Shutdown the Citrix Licensing server.
					
					
						Reboot an existing Server OS machine.  
					
					
						Log on to the Citrix StoreFront and launch a published application.
					
					
						Reboot an existing Desktop OS machine.  
					
					
						Log on to the Citrix StoreFront and launch a virtual desktop.
					
				
			
			
				
					
						License Server connectivity error posted in Event Log.  
					
					
						Provisioned Server OS boots successfully.  
					
					
						Users can launch published applications.  
					
					
						Provisioned Desktop OS boots successfully.  
					
					
						Users can launch a virtual desktop.  
					
					
						Administrators will have 30 days' grace to recover the Citrix Licensing Server.
					
				
			
		
	



	Process 3: Monitoring



	By having an in-depth understanding of the Citrix environment and its components' current and expected behavior, administrators are better equipped to discover an issue before it affects the user community. Furthermore, the data tracked during normal operations benefits trending and capacity planning. This section defines the monitoring recommendations for a Citrix environment and some recommended tools.
 


	Decision: Performance Monitor Metrics



	Monitoring the overall environment's performance is crucial to ensuring that all components are available and performing effectively and that users have a high-quality experience.
 


	Different components within the overall solution require monitoring of unique metrics with appropriately set thresholds. The metrics and thresholds presented are based on real-world experience but may not apply to all environments. Organizations must perform their own baseline, validity testing, and validation before implementing them within a production environment.
 


	
		Note: 
	 

	
		Some hypervisors, such as VMware vSphere and Hyper-V, provide specific performance counters for tracking CPU and Memory utilization within virtual machines (i.e., “VM Processor \ % Processor Time”). These performance counters should be used in addition to the general counters listed below.
	 



	General



	These performance counters should be used to monitor the key performance metrics of the Citrix infrastructure, application servers, and virtual desktops.
 


	
		
			
				
					Metric
				 
			
			
				
					Description
				 
			
			
				
					Warning (Yellow)
				 
			
			
				
					Critical (Red)
				 
			
			
				
					Troubleshooting/Remediation
				 
			
		
	
	
		
			
				
					Processor - % Processor Time
				 
			
			
				
					% Processor Time is the percentage of elapsed time the processor spends executing a non-Idle thread. It is calculated by measuring the duration of the idle thread is active in the sample interval and subtracting that time from interval duration. (Each processor has an idle thread that consumes cycles when no other threads are ready to run). This counter is the primary indicator of processor activity and displays the average percentage of busy time observed during the sample interval. It is calculated by monitoring the service's inactive time and subtracting that value from 100%.
				 
			
			
				
					80% for 15 minutes
				 
			
			
				
					95% for 15 minutes
				 
			
			
				
					Identify the processes/services consuming processor time using Task Manager or Resource Monitor. 
				 

				
					 
				 

				
					If all processes/services work within normal parameters and the level of CPU consumption is expected, adding additional CPU resources to this system in the future should be considered. 
				 

				
					 
				 

				
					If a process or service that works outside normal parameters can be identified, it should be killed. Please note that killing a process can cause unsaved data to be lost.
				 
			
		
		
			
				
					System - Processor Queue Length
				 
			
			
				
					Processor queue length is the number of threads in the processor queue. Unlike the disk counters, this counter shows ready threads only, not threads that are running. There is a single queue for processor time, even on computers with multiple processors. Therefore, if a computer has multiple processors, you must divide this value by the number of processors servicing the workload. Depending on the workload, a sustained processor queue of less than ten threads per processor is normally acceptable.
				 
			
			
				
					5 (per core) for 5 minutes or 6 (per core) for 15 minutes
				 
			
			
				
					10 (per Core) for 10 minutes or 12 (per core) for 30 minutes
				 
			
			
				
					A long CPU queue is a clear symptom of a CPU bottleneck. Please follow the steps outlined for the counter “Processor - % Processor Time.”
				 
			
		
		
			
				
					Memory – Available Bytes
				 
			
			
				
					Available memory indicates the amount of memory that is left after nonpaged pool allocations, paged pool allocations, process’ working sets, and the file system cache have all taken their piece.
				 
			
			
				
					&lt;30% of total RAM or 20% of physical memory over 6 minutes
				 
			
			
				
					&lt;15% of total RAM or 5% of physical memory over 6 minutes
				 
			
			
				
					Identify the processes/services consuming memory using Task Manager or Resource Monitor. 
				 

				
					 
				 

				
					If all processes/services work within normal parameters and the level of memory consumption is an expected behavior, it should be considered to add additional memory to this system in the future.
				 

				
					 
				 

				
					If a process/service can be identified that works outside normal parameters, it should be killed. Please note that killing a process can cause unsaved data to be lost.
				 
			
		
		
			
				
					Memory – Pages/sec
				 
			
			
				
					Pages/sec is the rate at which pages are read from or written to disk to resolve hard page faults.
				 
			
			
				
					&gt;10
				 
			
			
				
					&gt;20
				 
			
			
				
					A high value reported for this counter typically indicates a memory bottleneck, except if “Memory – Available Bytes” reports a high value simultaneously. In this case, most likely, an application is sequentially reading a file from memory.
				 
			
		
		
			
				
					Paging File - %Usage
				 
			
			
				
					This is the percentage amount of the Page File instance in use.
				 
			
			
				
					&gt;80% over 60 minutes
				 
			
			
				
					&gt;95% over 60 minutes
				 
			
			
				
					Review this value in conjunction with “Memory - Available Bytes” and “Memory - Pages/sec” to understand paging activity on the affected system.
				 
			
		
		
			
				
					LogicalDisk/PhysicalDisk - % Free Space
				 
			
			
				
					% Free Space is the percentage of total usable space on the selected logical disk drive that is free.
				 
			
			
				
					&lt;20% of physical disk or 20% reported after 2 minutes
				 
			
			
				
					&lt;10% of physical disk or 15% reported after 1 minute
				 
			
			
				
					Identify which files or folders consume disk space and delete obsolete files if possible. In case no files can be deleted, consider increasing the size of the affected partition or add additional disks.
				 
			
		
		
			
				
					LogicalDisk/PhysicalDisk - % Disk Time
				 
			
			
				
					% Disk Time marks how busy the disk is.
				 
			
			
				
					&gt;70% over 15 minutes
				 
			
			
				
					&gt;90% over 15 minutes 
				 
			
			
				
					Identify the processes/services consuming disk time using Task Manager or Resource Monitor. 
				 

				
					 
				 

				
					If all processes/services work within normal parameters and the level of disk consumption is an expected behavior, it should be considered to move the affected partition to a more capable disk subsystem in the future. 
				 

				
					 
				 

				
					If a process/service that works outside normal parameters can be identified, the process should be killed. Please note that killing a process can cause unsaved data to be lost.
				 
			
		
		
			
				
					LogicalDisk/PhysicalDisk – Current Disk Queue Length
				 
			
			
				
					Current disk queue length provides a primary measure of disk congestion. It is an indication of the number of transactions that are waiting to be processed.
				 
			
			
				
					&gt;= 3 over 15 minutes
				 
			
			
				
					&gt;=10 over 30 minutes
				 
			
			
				
					A long disk queue length typically indicates a disk performance bottleneck. This can be caused by either processes/services causing a high number of I/Os or a shortage of physical memory. Please follow the steps outlined for counter “LogicalDisk/PhysicalDisk - % Disk Time” and counter “Memory – Available Bytes”
				 
			
		
		
			
				
					LogicalDisk/PhysicalDisk – Avg. Disk Sec/Read – Avg. Disk Sec/Write – Avg. Disk Sec/Transfer
				 
			
			
				
					The Average Disk Second counters show the average time of a read/write/transfer from or to a disk in seconds.
				 
			
			
				
					&gt;=15ms consistently
				 
			
			
				
					&gt;=20ms consistently
				 
			
			
				
					High disk read or write latency indicates a disk performance bottleneck. The affected systems will become slow and unresponsive, and applications or services may fail. Please follow the steps outlined for counter “LogicalDisk/PhysicalDisk - % Disk Time”
				 
			
		
		
			
				
					Network Interface – Bytes Total/sec
				 
			
			
				
					Bytes Total/sec shows the rate at which the network adaptor processes data bytes. This counter includes all application and file data, as well as protocol information, such as packet headers.
				 
			
			
				
					60% of NIC speed inbound and outbound traffic for 1 min.
				 
			
			
				
					70% of NIC speed inbound and outbound traffic for 1 min.
				 
			
			
				
					Identify the processes/services consuming the network using Task Manager or Resource Monitor.
				 

				
					 
				 

				
					 If all processes/services work within normal parameters and the level of bandwidth consumption is expected, it should be considered to move the respective process/service to a dedicated NIC (or team of NICs). 
				 

				
					 
				 

				
					If a process or service can be identified that works outside normal parameters, it should be killed. Please note that killing a process can cause unsaved data to be lost.
				 
			
		
	



	StoreFront
 


	These performance counters are specific to the StoreFront servers.
 


	
		
			
				
					Metric
				 
			
			
				
					Description
				 
			
			
				
					Warning (Yellow)
				 
			
			
				
					Critical (Red)
				 
			
			
				
					Troubleshooting/Remediation
				 
			
		
		
			
				
					ASP.NET – Request Queued
				 
			
			
				
					The number of requests waiting to be processed by ASP. To establish threshold values accurately, a baseline must be established in the environment.
				 
			
			
				
					Based on baseline values
				 
			
			
				
					Based on baseline values
				 
			
			
				
					Requests may be rejected if the queue length exceeds the critical limit. In this case, it should be considered that additional StoreFront or Web Interface servers be added to the load balancing team to distribute the load across more nodes.
				 
			
		
		
			
				
					ASP.NET – Requests Rejected
				 
			
			
				
					The number of requests rejected because the request queue was full.
				 
			
			
				
					None
				 
			
			
				
					&gt;=1
				 
			
			
				
					When this limit is exceeded, requests will be rejected with a 503 status code and the message "Server is too busy." Please follow the steps outlined for counter “ASP.NET – Request Queued”
				 
			
		
		
			
				
					APP_POOL_WAS\Current Application Pool State\Citrix Receiver for Web
				 
			
			
				
					A value of 5 indicates the Application Pool is stopped and Receiver for Web will display an error
				 
			
			
				
					-
				 
			
			
				
					5
				 
			
			
				
					Investigate the other performance counters, events, and configuration of the failing node on this counter.
				 
			
		
		
			
				
					APP_POOL_WAS\Current Application Pool State\Citrix Delivery Services Authentication
				 
			
			
				
					A value of 5 indicates the Application Pool is stopped and StoreFront Authentication will not be successful.
				 
			
			
				
					-
				 
			
			
				
					5
				 
			
		
		
			
				
					APP_POOL_WAS\Current Application Pool State\Citrix Delivery Services Resource
				 
			
			
				
					A value of 5 indicates the Application Pool is stopped and Gateway or PNAgent access will not be successful.
				 
			
			
				
					-
				 
			
			
				
					5
				 
			
		
		
			
				
					Request response
				 
			
			
				
					Time taken for requests to be processed by StoreFront (Enumeration, Authentication app launch)
				 
			
			
				
					3 seconds
				 
			
			
				
					5 seconds
				 
			
		
	



	Decision: Services Monitoring
 


	Windows services critical to basic server functionality should be automatically monitored to ensure they are running properly. The following table lists the common Windows services that should be monitored. A warning (Yellow) or critical (Red) alert should be assigned when any of these services are restarted or stopped. The recommended recovery actions for the services listed below are as follows:
 


	
		First failure: Restart the Service
	
	
		Second Failure: Restart the Service
	
	
		Subsequent Failures: Put the server in maintenance mode and investigate the root cause.
	



	Citrix Virtual Apps and Desktops



	
		
			
				
					Service
				 
			
			
				
					Functionality
				 
			
			
				
					Administration Risk
				 
			
		
	
	
		
			
				
					Citrix AD Identity Service
				 
			
			
				
					Manages Active Directory computer accounts. Dependencies:  WMI Service
				 
			
			
				
					Machine Creation Service relies on this service to create virtual machines. Administrators will be unable to create new or modify existing Machine Catalogs or establish new connections to Citrix Studio.
				 
			
		
		
			
				
					Citrix Broker Service
				 
			
			
				
					Manages connections to virtual machines and applications.
				 
			
			
				
					Citrix Virtual Apps and Desktops cannot communicate with the Configuration Logging Database if this service is stopped. Administrators cannot change the environment or establish new connections to Citrix Studio.
				 
			
		
		
			
				
					Citrix Configuration Service
				 
			
			
				
					Stores service configuration information. Dependencies:  WMI Service
				 
			
			
				
					If this service is stopped, administrators cannot change the environment or establish new connections to Citrix Studio.
				 
			
		
		
			
				
					Citrix Delegated Administration Service
				 
			
			
				
					Manages configuration of delegated administration permissions.
				 
			
			
				
					If this service is stopped, Citrix cannot assign administrative permissions. Administrators cannot change the environment or establish new connections to Citrix Studio. Administrators will be unable to establish new connections to the Citrix Director, and existing sessions with Citrix Director will be interrupted.
				 
			
		
		
			
				
					Citrix Diagnostic Facility COM Server Service
				 
			
			
				
					Manages and controls Citrix diagnostic trace sessions on the system. Dependencies:  RPC Service
				 
			
			
				
					This service does not impact the production environment. It generates CDF trace files, which aid in troubleshooting issues.
				 
			
		
		
			
				
					Citrix Environment Test Service
				 
			
			
				
					Manages tests for evaluating the state of a Citrix site.
				 
			
			
				
					If this service is stopped, administrators cannot establish new connections to Citrix Studio. Administrators will also be unable to check the status of the Citrix site configuration, machine catalogs, and delivery groups by running the tests under “Common Tasks” in the Citrix Studio administration console.,
				 
			
		
		
			
				
					Citrix Host Services
				 
			
			
				
					Manages host and hypervisor connection. Dependencies:  WMI Service
				 
			
			
				
					Administrators cannot create new Machine Catalogs or control virtual machine power settings via Citrix Studio. Administrators will be unable to establish new connections to Citrix Studio. Users may experience issues connecting to virtual desktops when this service is unavailable. If this service is stopped, existing connections will not be affected.
				 
			
		
		
			
				
					Citrix Machine Creation Service
				 
			
			
				
					Creates new virtual machines. Dependencies:  WMI Service
				 
			
			
				
					Administrators cannot create new or modify existing Machine Catalogs or establish new connections to Citrix Studio. Administrators will be unable to establish new connections to Citrix Studio.
				 
			
		
		
			
				
					Citrix Monitor Service
				 
			
			
				
					Monitors the system
				 
			
			
				
					If this service is stopped, Citrix will be unable to communicate with the Monitoring Database, Citrix Director will be unable to retrieve any data on the environment, and administrators will be unable to establish new connections to Citrix Studio.
				 
			
		
		
			
				
					Citrix StoreFront Service
				 
			
			
				
					Manages deployment of StoreFront.
				 
			
			
				
					Administrators will be unable to establish new connections to Citrix Studio.
				 
			
		
	



	Citrix Cloud Connectors
 


	
		
			
				
					Service Name
				 
			
			
				
					Description
				 
			
			
				
					Startup Type
				 
			
			
				
					Log On As
				 
			
			
				
					Dependencies
				 
			
		
	
	
		
			
				
					Citrix CDF Capture Service
				 
			
			
				
					Captures CDF traces from all configured products and components.
				 
			
			
				
					Automatic
				 
			
			
				
					Network Service
				 
			
			
				
					N/A
				 
			
		
		
			
				
					Citrix Cloud Connector Metrics Service
				 
			
			
				
					This service is responsible for collecting metrics from the NetScaler Gateway Service and generating Synthetics. It forwards all the data from the Cloud Connector to the Citrix Analytics Service platform.
				 
			
			
				
					Automatic (Delayed Start)
				 
			
			
				
					Network Service
				 
			
			
				
					N/A
				 
			
		
		
			
				
					Citrix Cloud Services AD Provider
				 
			
			
				
					Enables Citrix Cloud to facilitate the management of resources associated with the Active Directory domain accounts in which it is installed.
				 
			
			
				
					Automatic (Delayed Start)
				 
			
			
				
					Network Service
				 
			
			
				
					N/A
				 
			
		
		
			
				
					Citrix Cloud Services Agent Logger
				 
			
			
				
					Provides a support logging framework for the Citrix Cloud Connector services.
				 
			
			
				
					Automatic
				 
			
			
				
					Network Service
				 
			
			
				
					Citrix Cloud Agent System
				 
			
		
		
			
				
					Citrix Cloud Agent System
				 
			
			
				
					Handles the system calls necessary for the on-premises agents. Includes installation, reboots, and registry access. Can only be called by Citrix Cloud Services Agent WatchDog.
				 
			
			
				
					Automatic
				 
			
			
				
					NT AUTHORITY\SYSTEM
				 
			
			
				
					N/A
				 
			
		
		
			
				
					Citrix Cloud Services Agent WatchDog
				 
			
			
				
					Monitors and upgrades the on-premises agents (evergreen).
				 
			
			
				
					Automatic
				 
			
			
				
					Network Service
				 
			
			
				
					Citrix Cloud Agent System
				 
			
		
		
			
				
					Citrix Cloud Services Credential Provider
				 
			
			
				
					Handles storage and retrieval of encrypted data.
				 
			
			
				
					Automatic (Delayed Start)
				 
			
			
				
					Network Service
				 
			
			
				
					N/A
				 
			
		
		
			
				
					Citrix Cloud Services WebRelay Provider
				 
			
			
				
					Enables HTTP Requests received from WebRelay Cloud service to be forwarded to On-Premises Web Servers.
				 
			
			
				
					Automatic (Delayed Start)
				 
			
			
				
					Network Service
				 
			
			
				
					N/A
				 
			
		
		
			
				
					Citrix ClxMtp Service
				 
			
			
				
					Citrix ClxMtp Service
				 
			
			
				
					Automatic (Delayed Start)
				 
			
			
				
					Network Service
				 
			
			
				
					N/A
				 
			
		
		
			
				
					Citrix Config Synchronizer Service
				 
			
			
				
					Copies brokering configuration locally for high availability mode.
				 
			
			
				
					Automatic
				 
			
			
				
					Network Service
				 
			
			
				
					N/A
				 
			
		
		
			
				
					Citrix High Availability Service
				 
			
			
				
					Provides continuity of service during an outage of the central site.
				 
			
			
				
					Automatic
				 
			
			
				
					Network Service
				 
			
			
				
					N/A
				 
			
		
		
			
				
					Citrix ITSM Adapter Provider
				 
			
			
				
					Automates provisioning and management of virtual apps and desktops.
				 
			
			
				
					Automatic
				 
			
			
				
					Local System
				 
			
			
				
					N/A
				 
			
		
		
			
				
					Citrix NetScaler Cloud Gateway
				 
			
			
				
					Provides Internet connectivity to on-premises desktops and applications without the need to open in-bound firewall rules or deploying components in the DMZ.
				 
			
			
				
					Automatic
				 
			
			
				
					Network Service
				 
			
			
				
					N/A
				 
			
		
		
			
				
					Citrix Remote Broker Provider
				 
			
			
				
					Enables communication to a remote Broker service from local VDAs and StoreFront servers.
				 
			
			
				
					Automatic
				 
			
			
				
					Network Service
				 
			
			
				
					N/A
				 
			
		
		
			
				
					Citrix RemoteHCLServer Service
				 
			
			
				
					Proxies communications between the Delivery Controller and the Hypervisor(s).
				 
			
			
				
					Automatic
				 
			
			
				
					Network Service
				 
			
			
				
					Workstation
				 
			
		
		
			
				
					Citrix WEM Cloud Authentication Service
				 
			
			
				
					Provides authentication service for Citrix WEM agents to connect to cloud infrastructure servers.
				 
			
			
				
					Automatic (Delayed Start)
				 
			
			
				
					Network Service
				 
			
			
				
					N/A
				 
			
		
		
			
				
					Citrix WEM Cloud Messaging Service
				 
			
			
				
					Provides service for Citrix WEM cloud service to receive messages from cloud infrastructure servers.
				 
			
			
				
					Automatic (Delayed Start)
				 
			
			
				
					Network Service
				 
			
			
				
					N/A
				 
			
		
	



	StoreFront



	
		
			
				
					Service
				 
			
			
				
					Description
				 
			
			
				
					Impact on Failure
				 
			
		
	
	
		
			
				
					Citrix Credential Wallet
				 
			
			
				
					Provides a secure store of credentials.
				 
			
			
				
					Users will be unable to log in to access their desktops or applications. Users logged into StoreFront will also be unable to launch new applications or desktop sessions. Existing applications or desktop sessions are unaffected.
				 
			
		
		
			
				
					 Citrix Default Domain Services
				 
			
			
				
					Provides authentication, password change, and other domain services.
				 
			
			
				
					Users will be unable to login to access their desktops or applications. Users currently logged in will not be affected.
				 
			
		
		
			
				
					Citrix Peer Resolution
				 
			
			
				
					Resolves peer names within peer-to-peer meshes.
				 
			
			
				
					Both the Citrix Credential Wallet and Citrix Subscriptions store have stopped generating the risks associated with those services.
				 
			
		
		
			
				
					Citrix Subscription Store
				 
			
			
				
					Provides a store and replication of user subscriptions.
				 
			
			
				
					Citrix Workspace app cannot add, remove, or reposition applications within StoreFront. Users will need to re-add applications, and all changes made to their selection of applications within the StoreFront store will not be saved or replicated in other sessions. The original user configuration will be restored once the service is restarted.
				 
			
		
		
			
				
					 World Wide Web Publishing Service
				 
			
			
				
					Provides web connectivity and administration through the Internet Information Services Manager.
				 
			
			
				
					Access to published applications or desktops will not be available through StoreFront. Users will be unable to resolve the Receiver for Web login page. Users logged into StoreFront will be unable to launch new applications or desktop sessions and will need to reenter credentials when the service is restarted. Existing applications or desktop sessions are unaffected.
				 
			
		
	



	Citrix Provisioning



	
		
			
				
					Service
				 
			
			
				
					Functionality
				 
			
			
				
					Administration Risk
				 
			
		
		
			
				
					Citrix PXE Service
				 
			
			
				
					Provides the PXE Boot Server functionality. 
				 

				
					 
				 

				
					Note: Only applicable when PXE boot is used.
				 
			
			
				
					Target devices may not boot successfully if PXE booting is leveraged due to the failure of this service.
				 
			
		
		
			
				
					Citrix Stream Service
				 
			
			
				
					Streams contents of the vDisk to the target device on demand.
				 
			
			
				
					If this service is stopped, it will not be possible to stream vDisk images.
				 
			
		
		
			
				
					Citrix SOAP Service
				 
			
			
				
					Provides a framework for external or existing solutions to interface with Provisioning services. 
				 

				
					 
				 

				
					Note: Only impacts console operations. User is unaffected
				 
			
			
				
					If this service fails, Provisioning Server to Provisioning Server communication and Provisioning Console to Provisioning Server communication is not possible.
				 
			
		
		
			
				
					Citrix TFTP Service
				 
			
			
				
					Provides the TFTP Server functionality. 
				 

				
					 
				 

				
					Note: Only applicable when TFTP is used.
				 
			
			
				
					If this service fails, target devices may be unable to boot if this server is used as a TFTP server for the bootstrap.
				 
			
		
		
			
				
					Citrix Two-Stage Boot Service
				 
			
			
				
					Provides the bootstrap functionality for booting devices using a BDM ISO file. 
				 

				
					Note: Only when BDM boot partitions are used.
				 
			
			
				
					If this service fails, target devices may be unable to boot if a BDM ISO file is used.
				 
			
		
	



	Citrix License Server
 


	
		
			
				
					Service
				 
			
			
				
					Functionality
				 
			
			
				
					Administration Risk
				 
			
		
		
			
				
					Citrix Licensing Service
				 
			
			
				
					Provides licensing services for Citrix products.
				 
			
			
				
					Licensing mode changes to a grace period when service is stopped or the License Server cannot be contacted. If not monitored, the functionality of Citrix products will cease after the grace period expires.
				 
			
		
		
			
				
					Citrix Licensing Support Service
				 
			
			
				
					This account controls reading the license files and updating strings with license trailers (data dictionary functionality).
				 
			
			
				
					None
				 
			
		
		
			
				
					Citrix Licensing WMI
				 
			
			
				
					The Citrix License Management Console collects license data information using the WMI service.
				 
			
			
				
					None
				 
			
		
	



	 



	Citrix Virtual Delivery Agent (VDA)



	
		
			
				
					Service Name
				 
			
			
				
					Description
				 
			
			
				
					Startup Type
				 
			
			
				
					Log On As
				 
			
			
				
					Dependencies
				 
			
		
	
	
		
			
				
					Citrix Audio Redirection Service
				 
			
			
				
					Provides audio redirection between the endpoint device and the virtual desktop.
				 
			
			
				
					Automatic
				 
			
			
				
					Local Service
				 
			
			
				
					N/A
				 
			
		
		
			
				
					Citrix CDF Capture Service
				 
			
			
				
					Captures CDF traces from all configured products and components.
				 
			
			
				
					Manual
				 
			
			
				
					Network Service
				 
			
			
				
					N/A
				 
			
		
		
			
				
					Citrix CEIP Service for VDA
				 
			
			
				
					Citrix CEIP Service for VDA.
				 
			
			
				
					Automatic
				 
			
			
				
					Local Service
				 
			
			
				
					N/A
				 
			
		
		
			
				
					Citrix Clipboard Service
				 
			
			
				
					Provides Clipboard virtual channel.
				 
			
			
				
					Automatic
				 
			
			
				
					Local Service
				 
			
			
				
					N/A
				 
			
		
		
			
				
					Citrix ClxMtp Service
				 
			
			
				
					Provides ClxMtp Protocol
				 
			
			
				
					Manual
				 
			
			
				
					Network Service
				 
			
			
				
					Remote Desktop Service
				 
			
		
		
			
				
					Citrix Control Channel Service
				 
			
			
				
					Provides Control Virtual Channel.
				 
			
			
				
					Automatic
				 
			
			
				
					Local Service
				 
			
			
				
					N/A
				 
			
		
		
			
				
					Citrix Desktop Service
				 
			
			
				
					The Citrix Desktop Service manages communication between the delivery controller and virtual desktops. It handles initial brokering of connections settings for connections and interaction with sessions.
				 
			
			
				
					Automatic
				 
			
			
				
					Network Service
				 
			
			
				
					Workstation
				 
			
		
		
			
				
					Citrix Device Redirector Service
				 
			
			
				
					Service to manage Citrix remote devices.
				 
			
			
				
					Automatic
				 
			
			
				
					Local Service
				 
			
			
				
					N/A
				 
			
		
		
			
				
					Citrix Diagnostic Facility COM Server
				 
			
			
				
					Manages and controls Citrix diagnostic trace sessions on the system.
				 
			
			
				
					Automatic
				 
			
			
				
					Network Service
				 
			
			
				
					Remote Procedure Call (RPC)
				 
			
		
		
			
				
					Citrix DND Service
				 
			
			
				
					Provides Drag and Drop command virtual channel.
				 
			
			
				
					Automatic
				 
			
			
				
					Local Service
				 
			
			
				
					N/A
				 
			
		
		
			
				
					Citrix Encryption Service
				 
			
			
				
					Secure ICA encryption.
				 
			
			
				
					Automatic
				 
			
			
				
					Local Service
				 
			
			
				
					Windows Management Instrumentation
				 
			
		
		
			
				
					Citrix End User Experience Monitoring Service
				 
			
			
				
					Service to collect and collate end-user experience measurements.
				 
			
			
				
					Automatic
				 
			
			
				
					Local Service
				 
			
			
				
					Citrix SMC Support Driver
				 
			
		
		
			
				
					Citrix Enterprise Browser Elevation Service (CitrixEnterpriseBrowserElevationService)
				 
			
			
				
					N/A
				 
			
			
				
					Manual
				 
			
			
				
					Local System
				 
			
			
				
					Remote Procedure Call (RPC)
				 
			
		
		
			
				
					Citrix GDT Service
				 
			
			
				
					Provides data transfer services to clipboard and drag and drop virtual channels
				 
			
			
				
					Automatic
				 
			
			
				
					Local Service
				 
			
			
				
					N/A
				 
			
		
		
			
				
					Citrix Group Policy Engine
				 
			
			
				
					Responsible for applying settings configured by Citrix administrators for the computer and users through the Group Policy component.
				 
			
			
				
					Automatic
				 
			
			
				
					Local System
				 
			
			
				
					Remote Procedure Call (RPC)
				 
			
		
		
			
				
					Citrix HDX Browser Redirection Service
				 
			
			
				
					Provides browser redirection between the endpoint device and the virtual desktop.
				 
			
			
				
					Automatic
				 
			
			
				
					NT AUTHORITY\SYSTEM
				 
			
			
				
					N/A
				 
			
		
		
			
				
					Citrix HDX HTML5 Video Redirection Service
				 
			
			
				
					Provides HTML5 video redirection between the endpoint device and the virtual desktop.
				 
			
			
				
					Automatic
				 
			
			
				
					Local System
				 
			
			
				
					N/A
				 
			
		
		
			
				
					Citrix HDX MediaStream Service
				 
			
			
				
					Provides multimedia acceleration between the endpoint device and the virtual desktop.
				 
			
			
				
					Automatic
				 
			
			
				
					Local Service
				 
			
			
				
					N/A
				 
			
		
		
			
				
					Citrix HDX Port Forwarding Service
				 
			
			
				
					Provides port forwarding between the endpoint device and the virtual desktop.
				 
			
			
				
					Automatic
				 
			
			
				
					Local Service
				 
			
			
				
					N/A
				 
			
		
		
			
				
					Citrix HDX Teams Redirection Service
				 
			
			
				
					Provides teams redirection between the endpoint device and the virtual desktop.
				 
			
			
				
					Automatic
				 
			
			
				
					NT AUTHORITY\SYSTEM
				 
			
			
				
					N/A
				 
			
		
		
			
				
					Citrix Local User Service Manager
				 
			
			
				
					Citrix Local User Management Service.
				 
			
			
				
					Automatic
				 
			
			
				
					NT AUTHORITY\SYSTEM
				 
			
			
				
					N/A
				 
			
		
		
			
				
					Citrix Location and Sensor Virtual Channel Service
				 
			
			
				
					Enables a server side application to leverage Location and Sensor capabilities.
				 
			
			
				
					Automatic
				 
			
			
				
					Local Service
				 
			
			
				
					N/A
				 
			
		
		
			
				
					Citrix Mobile Receiver Virtual Channel Service
				 
			
			
				
					Enables a server side application to use mobile device capabilities.
				 
			
			
				
					Automatic
				 
			
			
				
					Local Service
				 
			
			
				
					N/A
				 
			
		
		
			
				
					Citrix MultiTouch Redirection Service
				 
			
			
				
					Provides MultiTouch redirection between the endpoint device and the virtual desktop.
				 
			
			
				
					Automatic
				 
			
			
				
					Local Service
				 
			
			
				
					N/A
				 
			
		
		
			
				
					Citrix NetScaler AppFlow Service
				 
			
			
				
					Provides AppFlow events to Netscaler.
				 
			
			
				
					Automatic
				 
			
			
				
					Local Service
				 
			
			
				
					N/A
				 
			
		
		
			
				
					Citrix Print Manager Service
				 
			
			
				
					This service supports the Citrix Advanced Universal Printing Architecture.
				 
			
			
				
					Automatic
				 
			
			
				
					Local Service
				 
			
			
				
					Print Spooler
				 

				
					Remote Procedure Call (RPC)
				 
			
		
		
			
				
					Citrix Profile Management
				 
			
			
				
					Manages user personalization settings.
				 
			
			
				
					Automatic
				 
			
			
				
					Local System
				 
			
			
				
					N/A
				 
			
		
		
			
				
					Citrix Pvs for VMs agent
				 
			
			
				
					Pvs for VMs agent machine password update service.
				 
			
			
				
					Automatic
				 
			
			
				
					Local System
				 
			
			
				
					N/A
				 
			
		
		
			
				
					Citrix Services Manager
				 
			
			
				
					Citrix Services Manager.
				 
			
			
				
					Automatic
				 
			
			
				
					NT AUTHORITY\SYSTEM
				 
			
			
				
					Remote Desktop Service
				 
			
		
		
			
				
					Citrix Smart Card Service
				 
			
			
				
					Provides Smart Card redirection between the endpoint device and the virtual desktop.
				 
			
			
				
					Automatic
				 
			
			
				
					Local Service
				 
			
			
				
					N/A
				 
			
		
		
			
				
					Citrix Stack Control Service
				 
			
			
				
					RPC/Com Translation Service.
				 
			
			
				
					Automatic
				 
			
			
				
					Network Service
				 
			
			
				
					N/A
				 
			
		
		
			
				
					Citrix Telemetry Service
				 
			
			
				
					Citrix Telemetry Service.
				 
			
			
				
					Automatic (Delayed Start)
				 
			
			
				
					Network Service
				 
			
			
				
					N/A
				 
			
		
		
			
				
					Citrix UWA Cache Service
				 
			
			
				
					The Citrix UWA Cache Service manages a private cache for Universal Windows Platform apps and their icons for Citrix use. The Broker Agent sends information in this cache to the Delivery Controller.
				 
			
			
				
					Automatic
				 
			
			
				
					Local System
				 
			
			
				
					N/A
				 
			
		
		
			
				
					Citrix WebAuthn Redirection Service
				 
			
			
				
					Provides Virtual Authentication FIDO2 Redirection Services.
				 
			
			
				
					Automatic
				 
			
			
				
					Local Service
				 
			
			
				
					N/A
				 
			
		
		
			
				
					Citrix WIA Service
				 
			
			
				
					Allows Redirection of WIA imaging devices over virtual channel.
				 
			
			
				
					Automatic
				 
			
			
				
					Local Service
				 
			
			
				
					N/A
				 
			
		
	



	Decision: Events Monitoring



	Monitoring the Windows Event Log for unknown or critical events can help to discover issues and allow administrators to understand event patterns proactively:
 


	
		Licensing—Errors in the Event Log related to Remote Desktop licensing should be investigated. This might result from the installed Citrix product being unable to contact the Remote Desktop Licensing Server or the Citrix Licensing Server. If errors in the Event Log are not reviewed, users might eventually be denied access because they cannot acquire a valid license.
	
	
		Hardware Failure—Any event notification relating to a hardware failure should be looked at immediately. Any device that has failed will impact the system's performance. At a minimum, a hardware failure will remove the component's redundancy.
	
	
		Security Warnings—Customers should investigate security warnings or audit failure events regarding failed logins in the security log. This could indicate that someone is attempting to compromise the servers.
	
	
		Disk Capacity—When the drives of a Windows system reach 90% capacity, an event error message will be generated. To ensure continuous service, customers should poll these event errors. When the system runs out of hard disk space, it is put at severe risk. The server might not have enough space to service users' requests for temporary file storage.
	
	
		Application / Service errors - Any notification related to application or service errors should be investigated.
	
	
		Citrix errors - All Citrix software components will leverage the Windows Event Log for error logging. A list of the known Event Log warnings and errors issued by Citrix components can be found at the following links:
	



	
		Citrix Event Logs
	



	
		Citrix Provisioning

		
			
				Citrix Workspace Environment Management
			
			
				Citrix App Layering
			
		
	



	It is important to check the Event Viewer periodically for Citrix-related warnings or errors. Warnings or errors that repeatedly appear in the logs should be investigated immediately because it may indicate a problem that could severely impact the Citrix environment if not properly resolved.
 


	Decision: Citrix Director Alerts



	Baseline alerts within Director for Delivery Groups to alert Citrix administrators should be configured and adjusted as required.
 


	These alerts can be configured in Citrix Director and should be monitored.
 


	
		
			
				
					Alert Policy
				 
			
			
				
					Threshold and Alert Intervals
				 
			
			
				
					Description
				 
			
		
		
			
				
					CPU
				 
			
			
				
					All Delivery Groups:
				 

				
					
						Warning: 80% (30 min)
					
					
						Critical: 90% (30 min)
					
				
			
			
				
					Percentage of CPU usage.
				 

				
					   -   Identify the processes or resources consuming CPU.
				 

				
					   -   End the process if necessary. Ending the process causes unsaved data to be lost.
				 

				
					   -   If all is working as expected, add additional CPU resources in the future.
				 
			
		
		
			
				
					Memory
				 
			
			
				
					All Delivery Groups:
				 

				
					
						Warning: 80% (30 min)
					
					
						Critical: 90% (30 min)
					
				
			
			
				
					Percentage of Memory usage.
				 

				
					   -   Identify the processes or resources consuming memory.
				 

				
					   -   End the process if necessary. Ending the process causes unsaved data to be lost.
				 

				
					   -   If all is working as expected, add additional memory in the future.
				 
			
		
		
			
				
					Load Evaluator Index
				 
			
			
				
					All Delivery Groups:
				 

				
					
						Warning: 80% (30 min)
					
					
						Critical: 90% (30 min)
					
				
			
			
				
					Value of the Load Evaluator Index over the last 5 minutes.
				 

				
					   -   Check Director for Server OS Machines that might have a peak load (Max load).
				 

				
					   -   View both Dashboard (failures) and Trends Load Evaluator Index report.
				 
			
		
		
			
				
					Connection Failure Count
				 
			
			
				
					All Delivery Groups:
				 

				
					
						Warning: 5% of total desktops
					
					
						Critical: 10%+ of total desktops
					
				
			
			
				
					Number of connection failures over the last hour.
				 

				
					   -   Check Director Connection Failures Trends view for events logged from the Configuration log.
				 

				
					   -   Determine if applications or desktops are reachable.
				 
			
		
		
			
				
					Failed Machines (Server OS)
				 
			
			
				
					All Delivery Groups:
				 

				
					
						Warning: 10
					
					
						Critical: 20
					
				
			
			
				
					Number of failed Server OS machines.
				 

				
					Failures can occur for various reasons, as shown in the Director Dashboard and Filter views. Run Citrix Scout diagnostics to determine the root cause.
				 
			
		
		
			
				
					Average Logon Duration
				 
			
			
				
					
						Warning: 60 seconds (30 min)
					
					
						Critical: 120 seconds (30 min)
					
				
			
			
				
					Average logon duration for logons that occurred over the last hour.
				 

				
					   -   Check the Director Dashboard to get up-to-date metrics regarding the logon duration. Many users logging in during a short timeframe can increase the logon duration.
				 

				
					   -   Check the baseline and breakdown of the logons to narrow down the cause.
				 
			
		
	



	Decision: Capacity Management
 


	In addition to monitoring system-level metrics daily, performance metrics should be tracked from a historical perspective to help plan for future growth as more users access the environment.
 


	A baseline of the environment's performance should be taken so that it can be compared against performance over time. For example, if a user complains of poor performance, this baseline can be used to identify whether the issues are related to the user load exceeding the environment's capacity.
 


	Historical CPU, memory, and network utilization data on the controller and application servers or desktops would be an example of baseline performance metrics for capacity management.
 


	Citrix Director



	Administrators can utilize the Trends view within Citrix Director to track different parameters of the Citrix deployment over time. These parameters can be leveraged for capacity planning of the Citrix environment.
 


	From the Trends view, administrators can see historical data that is broken up into several categories, including:
 


	
		Sessions - Provides concurrent session usage over time, enabling the ability to size the environment appropriately.
	
	
		Connection Failures - Gives an overview of the different types of connection failures that have occurred across different Delivery Groups.
	
	
		Failed Desktop OS Machines – Gives an overview of the different problems associated with failures in desktop machines.
	
	
		Failed Server OS Machines - Gives an overview of the different problems associated with failures in server machines.
	
	
		Logon Performance – This shows how long users can log on to their applications and desktops.
	
	
		Load Evaluator Index – Provides various performance counter-based metrics, including CPU, Memory, and Disk Usage for Server OS machines.
	
	
		Capacity Management – Shows utilization of published applications and desktops
	
	
		Resource Utilization – Provides information on CPU, Memory, and storage resource utilization
	
	
		Custom Reports – Allows administrators to create custom historical reports on numerous metrics captured by the system.
	
	
		Hosted Application Usage—This section Details all applications published on the site and can provide detailed usage information about each individual application (concurrent instances, launches, usage duration, and so on).
	



	Citrix Analytics for Performance
 


	The Citrix Analytics for Performance infrastructure dashboard gives administrators an overview of their environment’s infrastructure health. The dashboard provides the VDA information across all sites. For multi-session OS VDAs, administrators can see which VDAs are unusable based on the load evaluator index. For single-session OS VDAs, administrators can see the number of VDAs in use and available.
 


	From the Infrastructure Analytics page, administrators can gain insights into the key components of their Apps and Desktops sites to see:
 


	
		Available Machines - Shows the percentage of machines that were available in the last 15 minutes and the states they are in, either Ready or Active.
	
	
		Unavailable Machines - Shows the percentage of machines that were unavailable in the last 15 minutes, either in Unregistered, Failed, or Maintenance states. This information can be used to optimize machine utilization in the environment.
	
	
		Machine Availability Trends - Shows the Aggregate State of machines plotted across the selected period. The machine state is aggregated to consider the least favorable state from among Ready for use, Active, Maintenance, Unregistered, and Failed, in that order.
	
	
		Machine Performance—Available for Multi-session OS desktops only, this shows the number of machines in usable state categorized based on the load evaluator index, such as high, medium, and low, for the selected time period, Site, and Delivery Groups.
	



	 
 


	Return to VDI Handbook Home]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_06/image.png.144e65e6ace643754c0285ab4b8eada8.png" length="53961" type="image/png"/><pubDate>Mon, 01 Jul 2024 13:01:00 +0000</pubDate></item><item><title>Deployment Guide: Using Terraform to deploy a Citrix Virtual Apps and Desktops 2402 LTSR site on vSphere 8</title><link>https://community.stage.citrix.com/tech-zone/automation/terraform-cvad-vsphere8/</link><description><![CDATA[Using Terraform to deploy a Citrix Virtual Apps and Desktops (CVAD) LTSR 2402 site on vSphere 8



	Overview



	This guide will showcase the possibility of deploying a Citrix Virtual Apps and Desktops (CVAD) LTSR 2402 site on vSphere 8 using Terraform. We want to reduce manual interventions to the absolute minimum. 
	 
	In the end, you will have created:
 


	
		Two Virtual Machines registered with the Domain for the deployment of the Cloud Connectors
	
	
		One Virtual Machine registered with the Domain for deploying all Terraform scripts (explanations follow later in this guide)
	
	
		Two Virtual Machines running NetScaler VPX
	
	
		A complete CVAD 2402 deployment containing 2 Delivery Controllers, StoreFront, WebStudio, and a License Server
	
	
		A Hosting Connection to vSphere
	
	
		A Machine Catalog containing 2 VDI machines based on a Master Image
	
	
		A Delivery Group with full AutoScale support      
		 
	



	
		NOTE:
	 

	
		The configuration of the 2 NetScaler VPX machines is out-of-scope of this guide. 
		Detailed instructions for configuring the VPX machines are in the NetScaler documentation.
	 



	 
 


	
		IMPORTANT:
	 

	
		You can find the related guide "Using Terraform to deploy a Citrix DaaS Resource Location on vSphere 8" on Citrix Tech Zone: https://community.citrix.com/tech-zone/build/deployment-guides/terraform-daas-vsphere8
	 



	 
 


	Installation and Configuration of Terraform



	Please find a description of how to install Terraform and initial configurations in our Tech Zone Deployment Guide https://community.citrix.com/tech-zone/build/deployment-guides/terraform-install-and-config.
 


	You can find all Terraform configuration files later on GitHub.
 


	 
	Deployment overview 
	 
	In this guide, we use an existing domain. We will not deploy a new domain. For further instructions on deploying a new domain, refer to the guide Citrix DaaS and Terraform—Automatic Deployment of a Resource Location on Microsoft Azure. Note that we will rework this guide 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 primary Domain Controller using IPSec-based Site-to-Site VPNs. Each Resource Location has its own subdomain. 
	 
 


	NOTE:  
	 
	We did not use loop-constructs or count keywords —each resource is created explicitly—for easier reading and understanding. 
	 
 


	 
 


	The Terraform flow consists of different parts:
 


	
		Module One - you can run this part can be run on any computer where Terraform is installed:

		
			
				Creating the initially needed Resources on vSphere 8:

				
					
						Importing 2 NetScaler VPXes and using unattended configuration using userdata.iso-files for an initial setup
					
					
						Creating a Windows 11-based Master Image VM 
					
					
						Creating two Windows Server 2022-based VMs used as Delivery Controller/Storefront Server VMs in Module 2
					
					
						Creating a Windows Server 2022-based VM acting as an Administrative workstation for running Terraform Modules 2 and higher is necessary because the Modules use WinRM for further configuration and deployment
					
					
						Creating all necessary scripts for joining the VMs to the existing sub-domain
					
					
						Putting the VMs into the existing sub-domain
					
				
			
		
	



	IMPORTANT:  
	 
	The Master Image needs to be configured before deployment - Terraform will configure a blank, Domain-joined VM!  
	 
	The Administrative VM must have Terraform installed - use the Chocolately Guidance provided in our Tech Zone Deployment Guide „XXXX“! 
	 
 


	
		Module Two - this part can only be run on the previously created Administrative VM as the deployment of Modules 2 and 3 relies heavily on WinRM:

		
			
				Configuring the three previously created Virtual Machines on vSphere 8:

				
					
						Installing the needed software on the Delivery Controllers
					
					
						Installing the required software on the Admin-VM
					
				
			
			
				Creating and configuring the necessary base entities:
				
					
						Creating the databases
					
					
						Installing the License Server, the core DDC components, the WebStudio, and Director
					
					
						Creating the CVAD site 
					
					
						Configuring Storefront
					
				
			
		
	
	
		Module Three:
		
			
				Running necessary configuration scripts after installation of the baseline entities: 
				
					
						Importing and changing the SSL certificate to a public-signed certificate 
					
					
						Uploading a valid License file
					
				
			
		
	
	
		Module Four:
		
			
				Creating the Hypervisor Connection, the Hypervisor Resource Pool, the Machine Catalog, and the Delivery Group
			
		
	



	CAUTION: 
	 
	Due to current limitations In PowerShell-Invoking, we cannot automatically add the second Delivery Controller/Storefront Server to the configured StoreFront Cluster. 
	 
	You have to add the second Delivery Controller/Storefront Server MANUALLY to the configured StoreFront Cluster! 
	 
	We will provide mitigation using Ansible in an extra guide published soon! 
	 
 


	 
 


	IMPORTANT: 
	 
	Make sure that all Terraform-related VMs can communicate using WinRM. 
	The deployment fails if the Admin-VM cannot connect to the CCs using WinRM. 
	You can find various configuration guides for WinRM on the Internet. 
	 
	We rely on GPOs related to WinRM to configure all Domain Members accordingly! 
	 
 


	 
	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 td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:"Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\TACG&gt; test-wsman -ComputerName &lt;IP-Address of the computer you want to reach&gt; -Credential &lt;IP-Address of the computer you want to reach&gt;\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 &lt;IP-Address of the computer you want to reach&gt; -Credential &lt;IP-Address of the computer you wish to reach&gt;\administrator  
					 

					
						The response should look like:   
						[172.31.22.104]: PS C:\Users\Administrator\Documents&gt;
					 
				
			
		
	



	 
	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 #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}	
	
		
			
				
					
						
							locals {
						 

						
							  #### Test the WinRM communication
						 

						
							  #### Need to invoke PowerShell as Domain User as the provisioner does not allow it to be run in a Domain Users-context
						 

						
							  TerraformTestWinRMScript     = &lt;&lt;-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 td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:"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!  
					 

					
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
						null_resource.CreateTestScriptOnCC1 (remote-exec): C:\Users\Administrator&gt;powershell -File c:/temp/xdinst/DATA/Terraform-Test-WinRM.ps1
					 

					
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;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



	You must set all needed configuration settings in the corresponding variables. Some Configuration settings are propagated throughout the whole Terraform configuration...
 


	You must start each module 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.
 


	IMPORTANT: 
	 
	You must complete each module/step successfully before starting the next module! 
	 
 


	 
 


	File System structure



	Root-Directory
 


	Module 1: _CVADonvSphere-Creation:
 


	
		
			
			
		
		
			
				
					
						Filename                                                                                                        
					 
				
				
					
						Purpose                                               
					 
				
			
		
		
			
				
					
						_CVADonvSphere-Creation-Create.tf
					 
				
				
					
						Resource configuration and primary flow definition
					 
				
			
			
				
					
						_CVADonvSphere-Creation-Create-variables.tf
					 
				
				
					
						Definition of Variables
					 
				
			
			
				
					
						_CVADonvSphere-Creation-Create.auto.tfvars.json
					 
				
				
					
						Setting the values of the Variables
					 
				
			
			
				
					
						_CVADonvSphere-Creation-Provider.tf
					 
				
				
					
						Provider definition and configuration
					 
				
			
			
				
					
						_CVADonvSphere-Creation-Provider-variables.tf
					 
				
				
					
						Definition of Variables
					 
				
			
			
				
					
						_CVADonvSphere-Creation-Provider.auto.tfvars.json
					 
				
				
					
						Setting the values of the Variables
					 
				
			
			
				
					
						DATA Directory
					 
				
				
					
						empty
					 
				
			
			
				
					 
				
				
					 
				
			
		
	



	
		 
	 

	
		Module 2: _CVADonvSphere-Configuration:
	 

	
		
			
				
				
			
			
				
					
						
							Filename                                                                                                        
						 
					
					
						
							Purpose                                               
						 
					
				
			
			
				
					
						
							_CVADonvSphere-InstallCVADandSF-CreatePreReqs.tf
						 
					
					
						
							Resource configuration and primary flow definition
						 
					
				
				
					
						
							_CVADonvSphere-InstallCVADandSF-variables.tf
						 
					
					
						
							Definition of Variables
						 
					
				
				
					
						
							_CVADonvSphere-InstallCVADandSF.auto.tfvars.json
						 
					
					
						
							Setting the values of the Variables
						 
					
				
				
					
						
							_CVADonvSphere-Provider.tf
						 
					
					
						
							Provider definition and configuration
						 
					
				
				
					
						
							_CVADonvSphere-Provider-variables.tf
						 
					
					
						
							Definition of Variables
						 
					
				
				
					
						
							_CVADonvSphere-Provider.auto.tfvars.json
						 
					
					
						
							Setting the values of the Variables
						 
					
				
				
					
						
							DATA Directory
						 
					
					
						
							Contains dynamically created scripts which will be uploaded and executed on the Delivery Controller and Admin VMs
						 
					
				
				
					
						 
					
					
						 
					
				
			
		
	

	
		 
		Module 3: _CVADonvSphere-CVADEntities:
	 

	
		
			
				
				
			
			
				
					
						
							Filename                                                                                                        
						 
					
					
						
							Purpose                                               
						 
					
				
			
			
				
					
						
							_CVADonvSphere-CreateEntities.tf
						 
					
					
						
							Resource configuration and primary flow definition
						 
					
				
				
					
						
							_CVADonvSphere-CreateEntities-variables.tf
						 
					
					
						
							Definition of Variables
						 
					
				
				
					
						
							_CVADonvSphere- CreateEntities.auto.tfvars.json
						 
					
					
						
							Setting the values of the Variables
						 
					
				
				
					
						
							_CVADonvSphere-Provider.tf
						 
					
					
						
							Provider definition and configuration
						 
					
				
				
					
						
							_CVADonvSphere-Provider-variables.tf
						 
					
					
						
							Definition of Variables
						 
					
				
				
					
						
							_CVADonvSphere-Provider.auto.tfvars.json
						 
					
					
						
							Setting the values of the Variables
						 
					
				
				
					
						
							DATA Directory
						 
					
					
						
							Contains dynamically created scripts which will be uploaded and executed on the Delivery Controller and Admin VMs
						 
					
				
				
					
						
							 
						 
					
					
						 
					
				
			
		
	



	Module 4: _CVADonvSphere-ConfigurationAfterStoreFront:
 


	
		
			
		
		
			
				
					
						Filename                                                                                                        
					 
				
				
					
						Purpose                                               
					 
				
			
		
		
			
				
					
						_CVADonvSphere-scripts.tf
					 
				
				
					
						Resource configuration and primary flow definition
					 
				
			
			
				
					
						_CVADonvSphere-scripts-variables.tf
					 
				
				
					
						Definition of Variables
					 
				
			
			
				
					
						_CVADonvSphere-scripts.auto.tfvars.json
					 
				
				
					
						Setting the values of the Variables
					 
				
			
			
				
					
						_CVADonvSphere-Provider.tf
					 
				
				
					
						Provider definition and configuration
					 
				
			
			
				
					
						_CVADonvSphere-Provider-variables.tf
					 
				
				
					
						Definition of Variables
					 
				
			
			
				
					
						_CVADonvSphere-Provider.auto.tfvars.json
					 
				
				
					
						Setting the values of the Variables
					 
				
			
			
				
					
						DATA Directory
					 
				
				
					
						Contains dynamically created scripts which will be uploaded and executed on the Delivery Controller and Admin VMs
					 
				
			
			
				
					 
				
				
					 
				
			
		
	

	
		CAUTION: 
		 
		All Terraform-related directories and files (.terraform, -terraform.lock.hcl, terraform.tfstate, terraform.tfstate) must not be changed or deleted - doing so might break the deployment! 
		 
	 

	
		Change the settings in the .json files to match your needs.
	 

	
		To ensure a smooth and error-free build, the following prerequisites must be met before setting the corresponding settings or running the Terraform workflow.
	 

	
		 
	 

	
		 Software Components for Configuration and Deployment
	
	 

	
		IMPORTANT: 
		 
		The CVAD Installer ISO, the NetScaler OVA templates, and the Userdata-Configuration ISOs for the initial unattended configuration of the VPXes must be manually uploaded to a Storage location on vSphere where Terraform can use them during the configuration run. 
		 
	 

	
		The Terraform deployment needs actual versions of the following software components:
	 

	
		
			Citrix CVAD 2402 Install ISO 
		
		
			NetScaler OVA template
		
	

	
		The Terraform engine requires these components and looks for these files during the workflow.  
		 
		You can set the URIs of the Storage Repository in the corresponding variables:
	 

	
		
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}		
		
			
				
					
						
							
								... 
								 "vsphere_nsvpx_ovf_path":"/data/NSVPX.ovf", 
								 "vsphere_cvad_iso_source_path":"/data/Citrix_Virtual_Apps_and_Desktops_7_2402_LTSR.iso", 
								 "vsphere_cvad_iso_destination_path":"TF-CVAD-ISO/Citrix_Virtual_Apps_and_Desktops_7_2402_LTSR.iso" 
								...
							 
						
					
				
			
		
	

	
		 
	 

	
		Module 1: Create the initially needed Resources on vSphere 8
	

	
		This module is split into the following configuration parts:
	 

	
		
			Importing 2 NetScaler VPXes and using unattended configuration using userdata.iso-files for an initial setup
		
		
			Creating a Windows 11-based Master Image VM 
		
		
			Creating two Windows Server 2022-based VMs used as Delivery Controller/Storefront Server VMs in Module 2
		
		
			Creating a Windows Server 2022-based VM acting as an Administrative workstation for running the Terraform Modules 2 and higher is necessary because WinRM is used for further configuration and deployment in Modules 2 and higher!
		
		
			Creating all necessary scripts for joining the VMs to the existing sub-domain
		
		
			Putting the VMs into the existing sub-domain 
			 
		
	

	
		IMPORTANT:  
		 
		The Master Image needs to be configured before deployment - Terraform will configure a blank, Domain-joined VM!  
		 
		The Administrative VM must have Terraform installed - use the Chocolately Guidance provided previously for installation! 
		 
	 

	
		 
	 

	
		IMPORTANT: 
		 
		Before starting the Terraform workflow, please ensure you have configured the variables according to your needs. 
		 
	 

	
		 
		The configuration can be started by following the standard Terraform workflow:
	 

	
		terraform init, 
		terraform plan 
		 
	 

	
		and if no errors occur
	 

	
		terraform apply
	 

	
		 
	 

	
		
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:"Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}		
	 

	
		
			
				
					
						PS C:\_TACG\_CVADOnvSphere&gt; terraform init
					 

					
						Initializing the backend...
					 

					
						Initializing provider plugins... 
						- Finding citrix/citrix versions matching "0.6.0"... 
						- Finding latest version of citrix/citrixadc... 
						- Finding hashicorp/vsphere versions matching "&gt;= 2.7.0"... 
						- Finding mastercard/restapi versions matching "1.18.2"... 
						- Installing citrix/citrix v0.6.0... 
						- Installed citrix/citrix v0.6.0 (self-signed, key ID BD4BD0E690CB7D88) 
						- Installing citrix/citrixadc v1.39.0... 
						- Installed citrix/citrixadc v1.39.0 (signed by a HashiCorp partner, key ID 25D62DD8407EA386) 
						- Installing hashicorp/vsphere v2.8.1... 
						- Installed hashicorp/vsphere v2.8.1 (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\_CVADOnvSphere&gt; terraform plan 
						data.vsphere_datacenter.datacenter: Reading... 
						data.vsphere_datacenter.datacenter: Read complete after 0s [id=datacenter-3] 
						data.vsphere_host.host: Reading... 
						data.vsphere_virtual_machine.W2K22_Template: Reading... 
						data.vsphere_network.network: Reading... 
						data.vsphere_datastore.datastore: Reading... 
						data.vsphere_network.network: Read complete after 0s [id=network-12] 
						data.vsphere_datastore.datastore: Read complete after 0s [id=datastore-28] 
						data.vsphere_host.host: Read complete after 0s [id=host-25] 
						data.vsphere_resource_pool.pool: Reading... 
						data.vsphere_virtual_machine.W2K22_Template: Read complete after 0s [id=42071eb9-f13b-74d8-7467-2209b53bd240] 
						data.vsphere_resource_pool.pool: Read complete after 0s [id=resgroup-5049] 
						data.vsphere_ovf_vm_template.NSVPX_Template: Reading... 
						data.vsphere_ovf_vm_template.NSVPX_Template: Read complete after 0s [id=NSVPX]
					 

					
						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:
					 

					
						  # vsphere_file.CVAD_Upload_Iso will be created 
						  + resource "vsphere_file" "CVAD_Upload_Iso" { 
						      + datacenter       = (sensitive value) 
						      + datastore        = (sensitive value) 
						      + destination_file = "TF-CVAD-ISO/Citrix_Virtual_Apps_and_Desktops_7_2402_LTSR.iso" 
						      + id               = (known after apply) 
						      + source_file      = "./data/Citrix_Virtual_Apps_and_Desktops_7_2402_LTSR.iso" 
						    }
					 

					
						  # vsphere_file.NSVPX1_Upload_Iso will be created 
						  + resource "vsphere_file" "NSVPX1_Upload_Iso" { 
						      + datacenter       = (sensitive value) 
						      + datastore        = (sensitive value) 
						      + destination_file = "TF-CVAD-NSVPX1-ISO/userdata.iso" 
						      + id               = (known after apply) 
						      + source_file      = "./data/userdata1.iso" 
						    }
					 

					
						  # vsphere_file.NSVPX2_Upload_Iso will be created 
						  + resource "vsphere_file" "NSVPX2_Upload_Iso" { 
						      + datacenter       = (sensitive value) 
						      + datastore        = (sensitive value) 
						      + destination_file = "TF-CVAD-NSVPX2-ISO/userdata.iso" 
						      + id               = (known after apply) 
						      + source_file      = "./data/userdata2.iso" 
						    }
					 

					
						  # vsphere_virtual_machine.ddc1-vm will be created 
						  + resource "vsphere_virtual_machine" "ddc1-vm" { 
						      + annotation                              = (known after apply) 
						      + boot_retry_delay                        = 10000 
						      + change_version                          = (known after apply) 
						      + cpu_limit                               = -1 
						      + cpu_share_count                         = (known after apply) 
						      + cpu_share_level                         = "normal" 
						      + datastore_id                            = "datastore-28" 
						      + default_ip_address                      = (known after apply) 
						      + ept_rvi_mode                            = "automatic" 
						      + extra_config_reboot_required            = true 
						      + firmware                                = "efi" 
						      + force_power_off                         = true 
						      + guest_id                                = "windows2019srvNext_64Guest" 
						      + guest_ip_addresses                      = (known after apply) 
						      + hardware_version                        = (known after apply) 
						      + host_system_id                          = (known after apply) 
						      + hv_mode                                 = "hvAuto" 
						      + id                                      = (known after apply) 
						      + ide_controller_count                    = 2 
						      + imported                                = (known after apply) 
						      + latency_sensitivity                     = "normal" 
						      + memory                                  = 8192 
						      + memory_limit                            = -1 
						      + memory_share_count                      = (known after apply) 
						      + memory_share_level                      = "normal" 
						      + migrate_wait_timeout                    = 30 
						      + moid                                    = (known after apply) 
						      + name                                    = "TF-CVAD-DDCSF1" 
						      + num_cores_per_socket                    = 1 
						      + num_cpus                                = 2 
						      + power_state                             = (known after apply) 
						      + poweron_timeout                         = 300 
						      + reboot_required                         = (known after apply) 
						      + resource_pool_id                        = "resgroup-5049" 
						      + run_tools_scripts_after_power_on        = true 
						      + run_tools_scripts_after_resume          = true 
						      + run_tools_scripts_before_guest_shutdown = true 
						      + run_tools_scripts_before_guest_standby  = true 
						      + sata_controller_count                   = 0 
						      + scsi_bus_sharing                        = "noSharing" 
						      + scsi_controller_count                   = 1 
						      + scsi_type                               = "pvscsi" 
						      + shutdown_wait_timeout                   = 3 
						      + storage_policy_id                       = (known after apply) 
						      + swap_placement_policy                   = "inherit" 
						      + sync_time_with_host                     = true 
						      + tools_upgrade_policy                    = "manual" 
						      + uuid                                    = (known after apply) 
						      + vapp_transport                          = (known after apply) 
						      + vmware_tools_status                     = (known after apply) 
						      + vmx_path                                = (known after apply) 
						      + wait_for_guest_ip_timeout               = 0 
						      + wait_for_guest_net_routable             = true 
						      + wait_for_guest_net_timeout              = 5
					 

					
						      + cdrom { 
						          + datastore_id   = "datastore-28" 
						          + device_address = (known after apply) 
						          + key            = (known after apply) 
						          + path           = "TF-CVAD-ISO/Citrix_Virtual_Apps_and_Desktops_7_2402_LTSR.iso" 
						        }
					 

					
						      + clone { 
						          + template_uuid = "42071eb9-f13b-74d8-7467-2209b53bd240" 
						          + timeout       = 30
					 

					
						          + customize { 
						              + ipv4_gateway = "10.10.0.1" 
						              + timeout      = 60
					 

					
						              + network_interface { 
						                  + dns_server_list = [ 
						                      + "10.10.100.1", 
						                    ] 
						                  + ipv4_address    = "10.10.170.11" 
						                  + ipv4_netmask    = 16 
						                }
					 

					
						              + windows_options { 
						                  + admin_password        = (sensitive value) 
						                  + auto_logon            = true 
						                  + auto_logon_count      = 2 
						                  + computer_name         = "TACG-TF-CVDDDC1" 
						                  + domain_admin_password = (sensitive value) 
						                  + domain_admin_user     = (sensitive value) 
						                  + full_name             = (sensitive value) 
						                  + join_domain           = (sensitive value) 
						                  + organization_name     = (sensitive value) 
						                  + time_zone             = 85 
						                } 
						            } 
						        }
					 

					
						      + disk { 
						          + attach            = false 
						          + controller_type   = "scsi" 
						          + datastore_id      = "&lt;computed&gt;" 
						          + device_address    = (known after apply) 
						          + disk_mode         = "persistent" 
						          + disk_sharing      = "sharingNone" 
						          + eagerly_scrub     = false 
						          + io_limit          = -1 
						          + io_reservation    = 0 
						          + io_share_count    = 0 
						          + io_share_level    = "normal" 
						          + keep_on_remove    = false 
						          + key               = 0 
						          + label             = "0" 
						          + path              = (known after apply) 
						          + size              = 70 
						          + storage_policy_id = (known after apply) 
						          + thin_provisioned  = true 
						          + unit_number       = 0 
						          + uuid              = (known after apply) 
						          + write_through     = false 
						        }
					 

					
						      + network_interface { 
						          + adapter_type          = "vmxnet3" 
						          + bandwidth_limit       = -1 
						          + bandwidth_reservation = 0 
						          + bandwidth_share_count = (known after apply) 
						          + bandwidth_share_level = "normal" 
						          + device_address        = (known after apply) 
						          + key                   = (known after apply) 
						          + mac_address           = (known after apply) 
						          + network_id            = "network-12" 
						        } 
						    }
					 

					
						  # vsphere_virtual_machine.ddc2-vm will be created 
						  + resource "vsphere_virtual_machine" "ddc2-vm" { 
						      + annotation                              = (known after apply) 
						      + boot_retry_delay                        = 10000 
						      + change_version                          = (known after apply) 
						      + cpu_limit                               = -1 
						      + cpu_share_count                         = (known after apply) 
						      + cpu_share_level                         = "normal" 
						      + datastore_id                            = "datastore-28" 
						      + default_ip_address                      = (known after apply) 
						      + ept_rvi_mode                            = "automatic" 
						      + extra_config_reboot_required            = true 
						      + firmware                                = "efi" 
						      + force_power_off                         = true 
						      + guest_id                                = "windows2019srvNext_64Guest" 
						      + guest_ip_addresses                      = (known after apply) 
						      + hardware_version                        = (known after apply) 
						      + host_system_id                          = (known after apply) 
						      + hv_mode                                 = "hvAuto" 
						      + id                                      = (known after apply) 
						      + ide_controller_count                    = 2 
						      + imported                                = (known after apply) 
						      + latency_sensitivity                     = "normal" 
						      + memory                                  = 8192 
						      + memory_limit                            = -1 
						      + memory_share_count                      = (known after apply) 
						      + memory_share_level                      = "normal" 
						      + migrate_wait_timeout                    = 30 
						      + moid                                    = (known after apply) 
						      + name                                    = "TF-CVAD-DDCSF2" 
						      + num_cores_per_socket                    = 1 
						      + num_cpus                                = 2 
						      + power_state                             = (known after apply) 
						      + poweron_timeout                         = 300 
						      + reboot_required                         = (known after apply) 
						      + resource_pool_id                        = "resgroup-5049" 
						      + run_tools_scripts_after_power_on        = true 
						      + run_tools_scripts_after_resume          = true 
						      + run_tools_scripts_before_guest_shutdown = true 
						      + run_tools_scripts_before_guest_standby  = true 
						      + sata_controller_count                   = 0 
						      + scsi_bus_sharing                        = "noSharing" 
						      + scsi_controller_count                   = 1 
						      + scsi_type                               = "pvscsi" 
						      + shutdown_wait_timeout                   = 3 
						      + storage_policy_id                       = (known after apply) 
						      + swap_placement_policy                   = "inherit" 
						      + sync_time_with_host                     = true 
						      + tools_upgrade_policy                    = "manual" 
						      + uuid                                    = (known after apply) 
						      + vapp_transport                          = (known after apply) 
						      + vmware_tools_status                     = (known after apply) 
						      + vmx_path                                = (known after apply) 
						      + wait_for_guest_ip_timeout               = 0 
						      + wait_for_guest_net_routable             = true 
						      + wait_for_guest_net_timeout              = 5
					 

					
						      + cdrom { 
						          + datastore_id   = "datastore-28" 
						          + device_address = (known after apply) 
						          + key            = (known after apply) 
						          + path           = "TF-CVAD-ISO/Citrix_Virtual_Apps_and_Desktops_7_2402_LTSR.iso" 
						        }
					 

					
						      + clone { 
						          + template_uuid = "42071eb9-f13b-74d8-7467-2209b53bd240" 
						          + timeout       = 30
					 

					
						          + customize { 
						              + ipv4_gateway = "10.10.0.1" 
						              + timeout      = 60
					 

					
						              + network_interface { 
						                  + dns_server_list = [ 
						                      + "10.10.100.1", 
						                    ] 
						                  + ipv4_address    = "10.10.170.12" 
						                  + ipv4_netmask    = 16 
						                }
					 

					
						              + windows_options { 
						                  + admin_password        = (sensitive value) 
						                  + auto_logon            = true 
						                  + auto_logon_count      = 2 
						                  + computer_name         = "TACG-TF-CVDDDC2" 
						                  + domain_admin_password = (sensitive value) 
						                  + domain_admin_user     = (sensitive value) 
						                  + full_name             = (sensitive value) 
						                  + join_domain           = (sensitive value) 
						                  + organization_name     = (sensitive value) 
						                  + time_zone             = 85 
						                } 
						            } 
						        }
					 

					
						      + disk { 
						          + attach            = false 
						          + controller_type   = "scsi" 
						          + datastore_id      = "&lt;computed&gt;" 
						          + device_address    = (known after apply) 
						          + disk_mode         = "persistent" 
						          + disk_sharing      = "sharingNone" 
						          + eagerly_scrub     = false 
						          + io_limit          = -1 
						          + io_reservation    = 0 
						          + io_share_count    = 0 
						          + io_share_level    = "normal" 
						          + keep_on_remove    = false 
						          + key               = 0 
						          + label             = "0" 
						          + path              = (known after apply) 
						          + size              = 70 
						          + storage_policy_id = (known after apply) 
						          + thin_provisioned  = true 
						          + unit_number       = 0 
						          + uuid              = (known after apply) 
						          + write_through     = false 
						        }
					 

					
						      + network_interface { 
						          + adapter_type          = "vmxnet3" 
						          + bandwidth_limit       = -1 
						          + bandwidth_reservation = 0 
						          + bandwidth_share_count = (known after apply) 
						          + bandwidth_share_level = "normal" 
						          + device_address        = (known after apply) 
						          + key                   = (known after apply) 
						          + mac_address           = (known after apply) 
						          + network_id            = "network-12" 
						        } 
						    }
					 

					
						 
						  # vsphere_virtual_machine.NSVPX1 will be created 
						  + resource "vsphere_virtual_machine" "NSVPX1" { 
						      + annotation                              = (known after apply) 
						      + boot_delay                              = 10000 
						      + boot_retry_delay                        = 10000 
						      + change_version                          = (known after apply) 
						      + cpu_limit                               = -1 
						      + cpu_share_count                         = (known after apply) 
						      + cpu_share_level                         = "normal" 
						      + datacenter_id                           = "datacenter-3" 
						      + datastore_id                            = "datastore-28" 
						      + default_ip_address                      = (known after apply) 
						      + ept_rvi_mode                            = "automatic" 
						      + extra_config_reboot_required            = true 
						      + firmware                                = "bios" 
						      + force_power_off                         = true 
						      + guest_id                                = "freebsd64Guest" 
						      + guest_ip_addresses                      = (known after apply) 
						      + hardware_version                        = (known after apply) 
						      + host_system_id                          = "host-25" 
						      + hv_mode                                 = "hvAuto" 
						      + id                                      = (known after apply) 
						      + ide_controller_count                    = 2 
						      + imported                                = (known after apply) 
						      + latency_sensitivity                     = "normal" 
						      + memory                                  = 2048 
						      + memory_limit                            = -1 
						      + memory_share_count                      = (known after apply) 
						      + memory_share_level                      = "normal" 
						      + migrate_wait_timeout                    = 30 
						      + moid                                    = (known after apply) 
						      + name                                    = "TF-CVAD-NSVPX1" 
						      + nested_hv_enabled                       = false 
						      + num_cores_per_socket                    = 0 
						      + num_cpus                                = 2 
						      + power_state                             = (known after apply) 
						      + poweron_timeout                         = 300 
						      + reboot_required                         = (known after apply) 
						      + resource_pool_id                        = "resgroup-5049" 
						      + run_tools_scripts_after_power_on        = true 
						      + run_tools_scripts_after_resume          = true 
						      + run_tools_scripts_before_guest_shutdown = true 
						      + run_tools_scripts_before_guest_standby  = true 
						      + sata_controller_count                   = 0 
						      + scsi_bus_sharing                        = "noSharing" 
						      + scsi_controller_count                   = 1 
						      + scsi_type                               = "lsilogic" 
						      + shutdown_wait_timeout                   = 3 
						      + storage_policy_id                       = (known after apply) 
						      + swap_placement_policy                   = "inherit" 
						      + sync_time_with_host                     = true 
						      + tools_upgrade_policy                    = "manual" 
						      + uuid                                    = (known after apply) 
						      + vapp_transport                          = (known after apply) 
						      + vmware_tools_status                     = (known after apply) 
						      + vmx_path                                = (known after apply) 
						      + wait_for_guest_ip_timeout               = 0 
						      + wait_for_guest_net_routable             = true 
						      + wait_for_guest_net_timeout              = 0
					 

					
						      + cdrom { 
						          + datastore_id   = "datastore-28" 
						          + device_address = (known after apply) 
						          + key            = (known after apply) 
						          + path           = "TF-CVAD-NSVPX1-ISO/userdata.iso" 
						        }
					 

					
						      + network_interface { 
						          + adapter_type          = "vmxnet3" 
						          + bandwidth_limit       = -1 
						          + bandwidth_reservation = 0 
						          + bandwidth_share_count = (known after apply) 
						          + bandwidth_share_level = "normal" 
						          + device_address        = (known after apply) 
						          + key                   = (known after apply) 
						          + mac_address           = (known after apply) 
						          + network_id            = "network-12" 
						        }
					 

					
						      + ovf_deploy { 
						          + allow_unverified_ssl_cert = false 
						          + disk_provisioning         = "thin" 
						          + enable_hidden_properties  = false 
						          + local_ovf_path            = ".//data/NSVPX-ESX-14.1-17.38_nc_64.ovf" 
						          + ovf_network_map           = { 
						              + "VM Network" = "network-12" 
						            } 
						        } 
						    }
					 

					
						  # vsphere_virtual_machine.NSVPX2 will be created 
						  + resource "vsphere_virtual_machine" "NSVPX2" { 
						      + annotation                              = (known after apply) 
						      + boot_delay                              = 10000 
						      + boot_retry_delay                        = 10000 
						      + change_version                          = (known after apply) 
						      + cpu_limit                               = -1 
						      + cpu_share_count                         = (known after apply) 
						      + cpu_share_level                         = "normal" 
						      + datacenter_id                           = "datacenter-3" 
						      + datastore_id                            = "datastore-28" 
						      + default_ip_address                      = (known after apply) 
						      + ept_rvi_mode                            = "automatic" 
						      + extra_config_reboot_required            = true 
						      + firmware                                = "bios" 
						      + force_power_off                         = true 
						      + guest_id                                = "freebsd64Guest" 
						      + guest_ip_addresses                      = (known after apply) 
						      + hardware_version                        = (known after apply) 
						      + host_system_id                          = "host-25" 
						      + hv_mode                                 = "hvAuto" 
						      + id                                      = (known after apply) 
						      + ide_controller_count                    = 2 
						      + imported                                = (known after apply) 
						      + latency_sensitivity                     = "normal" 
						      + memory                                  = 2048 
						      + memory_limit                            = -1 
						      + memory_share_count                      = (known after apply) 
						      + memory_share_level                      = "normal" 
						      + migrate_wait_timeout                    = 30 
						      + moid                                    = (known after apply) 
						      + name                                    = "TF-CVAD-NSVPX2" 
						      + nested_hv_enabled                       = false 
						      + num_cores_per_socket                    = 0 
						      + num_cpus                                = 2 
						      + power_state                             = (known after apply) 
						      + poweron_timeout                         = 300 
						      + reboot_required                         = (known after apply) 
						      + resource_pool_id                        = "resgroup-5049" 
						      + run_tools_scripts_after_power_on        = true 
						      + run_tools_scripts_after_resume          = true 
						      + run_tools_scripts_before_guest_shutdown = true 
						      + run_tools_scripts_before_guest_standby  = true 
						      + sata_controller_count                   = 0 
						      + scsi_bus_sharing                        = "noSharing" 
						      + scsi_controller_count                   = 1 
						      + scsi_type                               = "lsilogic" 
						      + shutdown_wait_timeout                   = 3 
						      + storage_policy_id                       = (known after apply) 
						      + swap_placement_policy                   = "inherit" 
						      + sync_time_with_host                     = true 
						      + tools_upgrade_policy                    = "manual" 
						      + uuid                                    = (known after apply) 
						      + vapp_transport                          = (known after apply) 
						      + vmware_tools_status                     = (known after apply) 
						      + vmx_path                                = (known after apply) 
						      + wait_for_guest_ip_timeout               = 0 
						      + wait_for_guest_net_routable             = true 
						      + wait_for_guest_net_timeout              = 0
					 

					
						      + cdrom { 
						          + datastore_id   = "datastore-28" 
						          + device_address = (known after apply) 
						          + key            = (known after apply) 
						          + path           = "TF-CVAD-NSVPX2-ISO/userdata.iso" 
						        }
					 

					
						      + network_interface { 
						          + adapter_type          = "vmxnet3" 
						          + bandwidth_limit       = -1 
						          + bandwidth_reservation = 0 
						          + bandwidth_share_count = (known after apply) 
						          + bandwidth_share_level = "normal" 
						          + device_address        = (known after apply) 
						          + key                   = (known after apply) 
						          + mac_address           = (known after apply) 
						          + network_id            = "network-12" 
						        }
					 

					
						      + ovf_deploy { 
						          + allow_unverified_ssl_cert = false 
						          + disk_provisioning         = "thin" 
						          + enable_hidden_properties  = false 
						          + local_ovf_path            = ".//data/NSVPX-ESX-14.1-17.38_nc_64.ovf" 
						          + ovf_network_map           = { 
						              + "VM Network" = "network-12" 
						            } 
						        } 
						    }
					 

					
						Plan: 7 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\_CVADOnvSphere&gt; terraform apply 
						data.vsphere_datacenter.datacenter: Reading... 
						data.vsphere_datacenter.datacenter: Read complete after 0s [id=datacenter-3] 
						data.vsphere_host.host: Reading... 
						data.vsphere_virtual_machine.W2K22_Template: Reading... 
						data.vsphere_network.network: Reading... 
						data.vsphere_datastore.datastore: Reading... 
						data.vsphere_network.network: Read complete after 0s [id=network-12] 
						data.vsphere_datastore.datastore: Read complete after 0s [id=datastore-28] 
						data.vsphere_host.host: Read complete after 0s [id=host-25] 
						data.vsphere_virtual_machine.W2K22_Template: Read complete after 0s [id=42071eb9-f13b-74d8-7467-2209b53bd240] 
						data.vsphere_resource_pool.pool: Reading... 
						data.vsphere_resource_pool.pool: Read complete after 0s [id=resgroup-5049] 
						data.vsphere_ovf_vm_template.NSVPX_Template: Reading... 
						data.vsphere_ovf_vm_template.NSVPX_Template: Read complete after 0s [id=NSVPX]
					 

					
						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:
					 

					
						  # vsphere_file.CVAD_Upload_Iso will be created 
						  + resource "vsphere_file" "CVAD_Upload_Iso" { 
						      + datacenter       = (sensitive value) 
						      + datastore        = (sensitive value) 
						      + destination_file = "TF-CVAD-ISO/Citrix_Virtual_Apps_and_Desktops_7_2402_LTSR.iso" 
						      + id               = (known after apply) 
						      + source_file      = "./data/Citrix_Virtual_Apps_and_Desktops_7_2402_LTSR.iso" 
						    }
					 

					
						  # vsphere_file.NSVPX1_Upload_Iso will be created 
						  + resource "vsphere_file" "NSVPX1_Upload_Iso" { 
						      + datacenter       = (sensitive value) 
						      + datastore        = (sensitive value) 
						      + destination_file = "TF-CVAD-NSVPX1-ISO/userdata.iso" 
						      + id               = (known after apply) 
						      + source_file      = "./data/userdata1.iso" 
						    }
					 

					
						  # vsphere_file.NSVPX2_Upload_Iso will be created 
						  + resource "vsphere_file" "NSVPX2_Upload_Iso" { 
						      + datacenter       = (sensitive value) 
						      + datastore        = (sensitive value) 
						      + destination_file = "TF-CVAD-NSVPX2-ISO/userdata.iso" 
						      + id               = (known after apply) 
						      + source_file      = "./data/userdata2.iso" 
						    }
					 

					
						  # vsphere_virtual_machine.ddc1-vm will be created 
						  + resource "vsphere_virtual_machine" "ddc1-vm" { 
						      + annotation                              = (known after apply) 
						      + boot_retry_delay                        = 10000 
						      + change_version                          = (known after apply) 
						      + cpu_limit                               = -1 
						      + cpu_share_count                         = (known after apply) 
						      + cpu_share_level                         = "normal" 
						      + datastore_id                            = "datastore-28" 
						      + default_ip_address                      = (known after apply) 
						      + ept_rvi_mode                            = "automatic" 
						      + extra_config_reboot_required            = true 
						      + firmware                                = "efi" 
						      + force_power_off                         = true 
						      + guest_id                                = "windows2019srvNext_64Guest" 
						      + guest_ip_addresses                      = (known after apply) 
						      + hardware_version                        = (known after apply) 
						      + host_system_id                          = (known after apply) 
						      + hv_mode                                 = "hvAuto" 
						      + id                                      = (known after apply) 
						      + ide_controller_count                    = 2 
						      + imported                                = (known after apply) 
						      + latency_sensitivity                     = "normal" 
						      + memory                                  = 8192 
						      + memory_limit                            = -1 
						      + memory_share_count                      = (known after apply) 
						      + memory_share_level                      = "normal" 
						      + migrate_wait_timeout                    = 30 
						      + moid                                    = (known after apply) 
						      + name                                    = "TF-CVAD-DDCSF1" 
						      + num_cores_per_socket                    = 1 
						      + num_cpus                                = 2 
						      + power_state                             = (known after apply) 
						      + poweron_timeout                         = 300 
						      + reboot_required                         = (known after apply) 
						      + resource_pool_id                        = "resgroup-5049" 
						      + run_tools_scripts_after_power_on        = true 
						      + run_tools_scripts_after_resume          = true 
						      + run_tools_scripts_before_guest_shutdown = true 
						      + run_tools_scripts_before_guest_standby  = true 
						      + sata_controller_count                   = 0 
						      + scsi_bus_sharing                        = "noSharing" 
						      + scsi_controller_count                   = 1 
						      + scsi_type                               = "pvscsi" 
						      + shutdown_wait_timeout                   = 3 
						      + storage_policy_id                       = (known after apply) 
						      + swap_placement_policy                   = "inherit" 
						      + sync_time_with_host                     = true 
						      + tools_upgrade_policy                    = "manual" 
						      + uuid                                    = (known after apply) 
						      + vapp_transport                          = (known after apply) 
						      + vmware_tools_status                     = (known after apply) 
						      + vmx_path                                = (known after apply) 
						      + wait_for_guest_ip_timeout               = 0 
						      + wait_for_guest_net_routable             = true 
						      + wait_for_guest_net_timeout              = 5
					 

					
						      + cdrom { 
						          + datastore_id   = "datastore-28" 
						          + device_address = (known after apply) 
						          + key            = (known after apply) 
						          + path           = "TF-CVAD-ISO/Citrix_Virtual_Apps_and_Desktops_7_2402_LTSR.iso" 
						        }
					 

					
						      + clone { 
						          + template_uuid = "42071eb9-f13b-74d8-7467-2209b53bd240" 
						          + timeout       = 30
					 

					
						          + customize { 
						              + ipv4_gateway = "10.10.0.1" 
						              + timeout      = 60
					 

					
						              + network_interface { 
						                  + dns_server_list = [ 
						                      + "10.10.100.1", 
						                    ] 
						                  + ipv4_address    = "10.10.170.11" 
						                  + ipv4_netmask    = 16 
						                }
					 

					
						              + windows_options { 
						                  + admin_password        = (sensitive value) 
						                  + auto_logon            = true 
						                  + auto_logon_count      = 2 
						                  + computer_name         = "TACG-TF-CVDDDC1" 
						                  + domain_admin_password = (sensitive value) 
						                  + domain_admin_user     = (sensitive value) 
						                  + full_name             = (sensitive value) 
						                  + join_domain           = (sensitive value) 
						                  + organization_name     = (sensitive value) 
						                  + time_zone             = 85 
						                } 
						            } 
						        }
					 

					
						      + disk { 
						          + attach            = false 
						          + controller_type   = "scsi" 
						          + datastore_id      = "&lt;computed&gt;" 
						          + device_address    = (known after apply) 
						          + disk_mode         = "persistent" 
						          + disk_sharing      = "sharingNone" 
						          + eagerly_scrub     = false 
						          + io_limit          = -1 
						          + io_reservation    = 0 
						          + io_share_count    = 0 
						          + io_share_level    = "normal" 
						          + keep_on_remove    = false 
						          + key               = 0 
						          + label             = "0" 
						          + path              = (known after apply) 
						          + size              = 70 
						          + storage_policy_id = (known after apply) 
						          + thin_provisioned  = true 
						          + unit_number       = 0 
						          + uuid              = (known after apply) 
						          + write_through     = false 
						        }
					 

					
						      + network_interface { 
						          + adapter_type          = "vmxnet3" 
						          + bandwidth_limit       = -1 
						          + bandwidth_reservation = 0 
						          + bandwidth_share_count = (known after apply) 
						          + bandwidth_share_level = "normal" 
						          + device_address        = (known after apply) 
						          + key                   = (known after apply) 
						          + mac_address           = (known after apply) 
						          + network_id            = "network-12" 
						        } 
						    }
					 

					
						  # vsphere_virtual_machine.ddc2-vm will be created 
						  + resource "vsphere_virtual_machine" "ddc2-vm" { 
						      + annotation                              = (known after apply) 
						      + boot_retry_delay                        = 10000 
						      + change_version                          = (known after apply) 
						      + cpu_limit                               = -1 
						      + cpu_share_count                         = (known after apply) 
						      + cpu_share_level                         = "normal" 
						      + datastore_id                            = "datastore-28" 
						      + default_ip_address                      = (known after apply) 
						      + ept_rvi_mode                            = "automatic" 
						      + extra_config_reboot_required            = true 
						      + firmware                                = "efi" 
						      + force_power_off                         = true 
						      + guest_id                                = "windows2019srvNext_64Guest" 
						      + guest_ip_addresses                      = (known after apply) 
						      + hardware_version                        = (known after apply) 
						      + host_system_id                          = (known after apply) 
						      + hv_mode                                 = "hvAuto" 
						      + id                                      = (known after apply) 
						      + ide_controller_count                    = 2 
						      + imported                                = (known after apply) 
						      + latency_sensitivity                     = "normal" 
						      + memory                                  = 8192 
						      + memory_limit                            = -1 
						      + memory_share_count                      = (known after apply) 
						      + memory_share_level                      = "normal" 
						      + migrate_wait_timeout                    = 30 
						      + moid                                    = (known after apply) 
						      + name                                    = "TF-CVAD-DDCSF2" 
						      + num_cores_per_socket                    = 1 
						      + num_cpus                                = 2 
						      + power_state                             = (known after apply) 
						      + poweron_timeout                         = 300 
						      + reboot_required                         = (known after apply) 
						      + resource_pool_id                        = "resgroup-5049" 
						      + run_tools_scripts_after_power_on        = true 
						      + run_tools_scripts_after_resume          = true 
						      + run_tools_scripts_before_guest_shutdown = true 
						      + run_tools_scripts_before_guest_standby  = true 
						      + sata_controller_count                   = 0 
						      + scsi_bus_sharing                        = "noSharing" 
						      + scsi_controller_count                   = 1 
						      + scsi_type                               = "pvscsi" 
						      + shutdown_wait_timeout                   = 3 
						      + storage_policy_id                       = (known after apply) 
						      + swap_placement_policy                   = "inherit" 
						      + sync_time_with_host                     = true 
						      + tools_upgrade_policy                    = "manual" 
						      + uuid                                    = (known after apply) 
						      + vapp_transport                          = (known after apply) 
						      + vmware_tools_status                     = (known after apply) 
						      + vmx_path                                = (known after apply) 
						      + wait_for_guest_ip_timeout               = 0 
						      + wait_for_guest_net_routable             = true 
						      + wait_for_guest_net_timeout              = 5
					 

					
						      + cdrom { 
						          + datastore_id   = "datastore-28" 
						          + device_address = (known after apply) 
						          + key            = (known after apply) 
						          + path           = "TF-CVAD-ISO/Citrix_Virtual_Apps_and_Desktops_7_2402_LTSR.iso" 
						        }
					 

					
						      + clone { 
						          + template_uuid = "42071eb9-f13b-74d8-7467-2209b53bd240" 
						          + timeout       = 30
					 

					
						          + customize { 
						              + ipv4_gateway = "10.10.0.1" 
						              + timeout      = 60
					 

					
						              + network_interface { 
						                  + dns_server_list = [ 
						                      + "10.10.100.1", 
						                    ] 
						                  + ipv4_address    = "10.10.170.12" 
						                  + ipv4_netmask    = 16 
						                }
					 

					
						              + windows_options { 
						                  + admin_password        = (sensitive value) 
						                  + auto_logon            = true 
						                  + auto_logon_count      = 2 
						                  + computer_name         = "TACG-TF-CVDDDC2" 
						                  + domain_admin_password = (sensitive value) 
						                  + domain_admin_user     = (sensitive value) 
						                  + full_name             = (sensitive value) 
						                  + join_domain           = (sensitive value) 
						                  + organization_name     = (sensitive value) 
						                  + time_zone             = 85 
						                } 
						            } 
						        }
					 

					
						      + disk { 
						          + attach            = false 
						          + controller_type   = "scsi" 
						          + datastore_id      = "&lt;computed&gt;" 
						          + device_address    = (known after apply) 
						          + disk_mode         = "persistent" 
						          + disk_sharing      = "sharingNone" 
						          + eagerly_scrub     = false 
						          + io_limit          = -1 
						          + io_reservation    = 0 
						          + io_share_count    = 0 
						          + io_share_level    = "normal" 
						          + keep_on_remove    = false 
						          + key               = 0 
						          + label             = "0" 
						          + path              = (known after apply) 
						          + size              = 70 
						          + storage_policy_id = (known after apply) 
						          + thin_provisioned  = true 
						          + unit_number       = 0 
						          + uuid              = (known after apply) 
						          + write_through     = false 
						        }
					 

					
						      + network_interface { 
						          + adapter_type          = "vmxnet3" 
						          + bandwidth_limit       = -1 
						          + bandwidth_reservation = 0 
						          + bandwidth_share_count = (known after apply) 
						          + bandwidth_share_level = "normal" 
						          + device_address        = (known after apply) 
						          + key                   = (known after apply) 
						          + mac_address           = (known after apply) 
						          + network_id            = "network-12" 
						        } 
						    }
					 

					
						 
						  # vsphere_virtual_machine.NSVPX1 will be created 
						  + resource "vsphere_virtual_machine" "NSVPX1" { 
						      + annotation                              = (known after apply) 
						      + boot_delay                              = 10000 
						      + boot_retry_delay                        = 10000 
						      + change_version                          = (known after apply) 
						      + cpu_limit                               = -1 
						      + cpu_share_count                         = (known after apply) 
						      + cpu_share_level                         = "normal" 
						      + datacenter_id                           = "datacenter-3" 
						      + datastore_id                            = "datastore-28" 
						      + default_ip_address                      = (known after apply) 
						      + ept_rvi_mode                            = "automatic" 
						      + extra_config_reboot_required            = true 
						      + firmware                                = "bios" 
						      + force_power_off                         = true 
						      + guest_id                                = "freebsd64Guest" 
						      + guest_ip_addresses                      = (known after apply) 
						      + hardware_version                        = (known after apply) 
						      + host_system_id                          = "host-25" 
						      + hv_mode                                 = "hvAuto" 
						      + id                                      = (known after apply) 
						      + ide_controller_count                    = 2 
						      + imported                                = (known after apply) 
						      + latency_sensitivity                     = "normal" 
						      + memory                                  = 2048 
						      + memory_limit                            = -1 
						      + memory_share_count                      = (known after apply) 
						      + memory_share_level                      = "normal" 
						      + migrate_wait_timeout                    = 30 
						      + moid                                    = (known after apply) 
						      + name                                    = "TF-CVAD-NSVPX1" 
						      + nested_hv_enabled                       = false 
						      + num_cores_per_socket                    = 0 
						      + num_cpus                                = 2 
						      + power_state                             = (known after apply) 
						      + poweron_timeout                         = 300 
						      + reboot_required                         = (known after apply) 
						      + resource_pool_id                        = "resgroup-5049" 
						      + run_tools_scripts_after_power_on        = true 
						      + run_tools_scripts_after_resume          = true 
						      + run_tools_scripts_before_guest_shutdown = true 
						      + run_tools_scripts_before_guest_standby  = true 
						      + sata_controller_count                   = 0 
						      + scsi_bus_sharing                        = "noSharing" 
						      + scsi_controller_count                   = 1 
						      + scsi_type                               = "lsilogic" 
						      + shutdown_wait_timeout                   = 3 
						      + storage_policy_id                       = (known after apply) 
						      + swap_placement_policy                   = "inherit" 
						      + sync_time_with_host                     = true 
						      + tools_upgrade_policy                    = "manual" 
						      + uuid                                    = (known after apply) 
						      + vapp_transport                          = (known after apply) 
						      + vmware_tools_status                     = (known after apply) 
						      + vmx_path                                = (known after apply) 
						      + wait_for_guest_ip_timeout               = 0 
						      + wait_for_guest_net_routable             = true 
						      + wait_for_guest_net_timeout              = 0
					 

					
						      + cdrom { 
						          + datastore_id   = "datastore-28" 
						          + device_address = (known after apply) 
						          + key            = (known after apply) 
						          + path           = "TF-CVAD-NSVPX1-ISO/userdata.iso" 
						        }
					 

					
						      + network_interface { 
						          + adapter_type          = "vmxnet3" 
						          + bandwidth_limit       = -1 
						          + bandwidth_reservation = 0 
						          + bandwidth_share_count = (known after apply) 
						          + bandwidth_share_level = "normal" 
						          + device_address        = (known after apply) 
						          + key                   = (known after apply) 
						          + mac_address           = (known after apply) 
						          + network_id            = "network-12" 
						        }
					 

					
						      + ovf_deploy { 
						          + allow_unverified_ssl_cert = false 
						          + disk_provisioning         = "thin" 
						          + enable_hidden_properties  = false 
						          + local_ovf_path            = ".//data/NSVPX-ESX-14.1-17.38_nc_64.ovf" 
						          + ovf_network_map           = { 
						              + "VM Network" = "network-12" 
						            } 
						        } 
						    }
					 

					
						  # vsphere_virtual_machine.NSVPX2 will be created 
						  + resource "vsphere_virtual_machine" "NSVPX2" { 
						      + annotation                              = (known after apply) 
						      + boot_delay                              = 10000 
						      + boot_retry_delay                        = 10000 
						      + change_version                          = (known after apply) 
						      + cpu_limit                               = -1 
						      + cpu_share_count                         = (known after apply) 
						      + cpu_share_level                         = "normal" 
						      + datacenter_id                           = "datacenter-3" 
						      + datastore_id                            = "datastore-28" 
						      + default_ip_address                      = (known after apply) 
						      + ept_rvi_mode                            = "automatic" 
						      + extra_config_reboot_required            = true 
						      + firmware                                = "bios" 
						      + force_power_off                         = true 
						      + guest_id                                = "freebsd64Guest" 
						      + guest_ip_addresses                      = (known after apply) 
						      + hardware_version                        = (known after apply) 
						      + host_system_id                          = "host-25" 
						      + hv_mode                                 = "hvAuto" 
						      + id                                      = (known after apply) 
						      + ide_controller_count                    = 2 
						      + imported                                = (known after apply) 
						      + latency_sensitivity                     = "normal" 
						      + memory                                  = 2048 
						      + memory_limit                            = -1 
						      + memory_share_count                      = (known after apply) 
						      + memory_share_level                      = "normal" 
						      + migrate_wait_timeout                    = 30 
						      + moid                                    = (known after apply) 
						      + name                                    = "TF-CVAD-NSVPX2" 
						      + nested_hv_enabled                       = false 
						      + num_cores_per_socket                    = 0 
						      + num_cpus                                = 2 
						      + power_state                             = (known after apply) 
						      + poweron_timeout                         = 300 
						      + reboot_required                         = (known after apply) 
						      + resource_pool_id                        = "resgroup-5049" 
						      + run_tools_scripts_after_power_on        = true 
						      + run_tools_scripts_after_resume          = true 
						      + run_tools_scripts_before_guest_shutdown = true 
						      + run_tools_scripts_before_guest_standby  = true 
						      + sata_controller_count                   = 0 
						      + scsi_bus_sharing                        = "noSharing" 
						      + scsi_controller_count                   = 1 
						      + scsi_type                               = "lsilogic" 
						      + shutdown_wait_timeout                   = 3 
						      + storage_policy_id                       = (known after apply) 
						      + swap_placement_policy                   = "inherit" 
						      + sync_time_with_host                     = true 
						      + tools_upgrade_policy                    = "manual" 
						      + uuid                                    = (known after apply) 
						      + vapp_transport                          = (known after apply) 
						      + vmware_tools_status                     = (known after apply) 
						      + vmx_path                                = (known after apply) 
						      + wait_for_guest_ip_timeout               = 0 
						      + wait_for_guest_net_routable             = true 
						      + wait_for_guest_net_timeout              = 0
					 

					
						      + cdrom { 
						          + datastore_id   = "datastore-28" 
						          + device_address = (known after apply) 
						          + key            = (known after apply) 
						          + path           = "TF-CVAD-NSVPX2-ISO/userdata.iso" 
						        }
					 

					
						      + network_interface { 
						          + adapter_type          = "vmxnet3" 
						          + bandwidth_limit       = -1 
						          + bandwidth_reservation = 0 
						          + bandwidth_share_count = (known after apply) 
						          + bandwidth_share_level = "normal" 
						          + device_address        = (known after apply) 
						          + key                   = (known after apply) 
						          + mac_address           = (known after apply) 
						          + network_id            = "network-12" 
						        }
					 

					
						      + ovf_deploy { 
						          + allow_unverified_ssl_cert = false 
						          + disk_provisioning         = "thin" 
						          + enable_hidden_properties  = false 
						          + local_ovf_path            = ".//data/NSVPX-ESX-14.1-17.38_nc_64.ovf" 
						          + ovf_network_map           = { 
						              + "VM Network" = "network-12" 
						            } 
						        } 
						    }
					 

					
						Plan: 7 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
					 

					
						vsphere_file.NSVPX1_Upload_Iso: Creating... 
						vsphere_file.CVAD_Upload_Iso: Creating... 
						vsphere_file.NSVPX2_Upload_Iso: Creating... 
						vsphere_virtual_machine.ddc2-vm: Creating... 
						vsphere_virtual_machine.ddc1-vm: Creating... 
						vsphere_virtual_machine.NSVPX1: Creating... 
						vsphere_virtual_machine.NSVPX2: Creating... 
						vsphere_file.NSVPX2_Upload_Iso: Creation complete after 1s [id=[datastore1 (1)] TACG/TF-CVAD-NSVPX2-ISO/userdata.iso] 
						vsphere_file.NSVPX1_Upload_Iso: Creation complete after 1s [id=[datastore1 (1)] TACG/TF-CVAD-NSVPX1-ISO/userdata.iso] 
						vsphere_file.CVAD_Upload_Iso: Still creating... [10s elapsed] 
						vsphere_virtual_machine.ddc2-vm: Still creating... [10s elapsed] 
						vsphere_virtual_machine.ddc1-vm: Still creating... [10s elapsed] 
						vsphere_file.CVAD_Upload_Iso: Still creating... [20s elapsed]
					 

					
						 
						... 
						(Output shortened) 
						...
					 

					
						vsphere_file.CVAD_Upload_Iso: Creation complete after 6m58s [id=[datastore1 (1)] TACG/TF-CVAD-ISO/Citrix_Virtual_Apps_and_Desktops_7_2402_LTSR.iso] 
						vsphere_virtual_machine.ddc1-vm: Still creating... [7m1s elapsed]
					 

					
						 
						... 
						(Output shortened) 
						...
					 

					
						vsphere_virtual_machine.NSVPX1: Creation complete after 9m33s [id=4207ea57-ed5w-2765-8ab1-28dc390a3128] 
						vsphere_virtual_machine.NSVPX2: Creation complete after 9m33s [id=4207ed11-2e45-ac54-9433-90de34f1ac12] 
						vsphere_virtual_machine.ddc1-vm: Creation complete after 9m33s [id=4207e802-eae5-43a2-6273-a2afa349aba0] 
						vsphere_virtual_machine.ddc2-vm: Creation complete after 9m33s [id=4207f9d7-6201-c9ab-0fc5-2178a75a7eef]
					 

					
						Apply complete! Resources: 7 added, 0 changed, 0 destroyed. 
						PS C:\_TACG\_CVADOnvSphere&gt;
					 
				
			
		
	

	
		 
		Terraform successfully created all needed VMs on vSphere 8:
	 

	
		 
		 
	 

	
		 
		 
	 

	
		
	 

	
		The VMs were successfully registered in AD:
	 

	
		
	 

	
		
	 

	
		The NetScaler VPXes were successfully installed and got their initial configuration:
	 

	
		
	 

	
		The Terraform flow completed successfully, and the next module can begin. 
		 
	 

	
		Module 2: Install and Configure the Delivery Controller- and StoreFront-Servers on vSphere 8 
		 
	

	
		
			IMPORTANT: 
			 
			This module can only be run on the previously created Administrative VM, as Modules 2 and higher deployments rely heavily on WinRM! Please install Terraform on the Administrative VM before trying to run the scripts! 
			 
		 
	

	
		This module is split into the following configuration parts:
	 

	
		
			Configuring the three previously created Virtual Machines on vSphere 8:

			
				
					Installing the needed software on the Delivery Controllers
				
				
					Installing the required software on the Admin-VM
				
			
		
		
			Creating and configuring the necessary base entities:
			
				
					Creating the databases
				
				
					Installing the License Server, the core DDC components, the WebStudio, and Director
				
				
					Creating the CVAD site 
				
				
					Configuring Storefront 
					 
				
			
		
	

	
		
			CAUTION: 
			 
			Due to current limitations In PowerShell-Invoking, we cannot automatically add the second Delivery Controller/Storefront Server to the configured StoreFront Cluster. 
			 
			You have to add the second Delivery Controller/Storefront Server MANUALLY to the configured StoreFront Cluster! 
			We will provide mitigation using Ansible in an extra guide published soon! 
			 
		 
	

	
		 
	 

	
		
			IMPORTANT: 
			 
			Before starting the Terraform workflow, please ensure you have configured the variables according to your needs. 
			 
		 
	

	
		 
		The configuration can be started by following the standard Terraform workflow:
	 

	
		
			terraform init, 
			terraform plan
		 
	

	
		and if no errors occur
	 

	
		
			terraform apply
		 
	

	
		 
	 

	
		
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:"Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}		
		
			
				
					
						
							PS C:\_TACG\_CVADOnvSphere\_Configuration&gt; terraform init
						 

						
							Initializing the backend...
						 

						
							Initializing provider plugins... 
							- Finding citrix/citrix versions matching "0.6.0"... 
							- Finding latest version of citrix/citrixadc... 
							- Finding latest version of hashicorp/local... 
							- Finding latest version of hashicorp/null... 
							- Finding hashicorp/vsphere versions matching "&gt;= 2.7.0"... 
							- Finding mastercard/restapi versions matching "1.18.2"... 
							- Installing hashicorp/null v3.2.2... 
							- Installed hashicorp/null v3.2.2 (signed by HashiCorp) 
							- Installing hashicorp/vsphere v2.8.1... 
							- Installed hashicorp/vsphere v2.8.1 (signed by HashiCorp) 
							- Installing mastercard/restapi v1.18.2... 
							- Installed mastercard/restapi v1.18.2 (self-signed, key ID DCB8C431D71C30AB) 
							- Installing citrix/citrix v0.6.0... 
							- Installed citrix/citrix v0.6.0 (self-signed, key ID BD4BD0E690CB7D88) 
							- Installing citrix/citrixadc v1.39.0... 
							- Installed citrix/citrixadc v1.39.0 (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 required changes 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\_CVADOnvSphere\_Configuration&gt; terraform plan
						 

						
							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.StoreFrontConfigurationScriptIntoDataDirectory will be created 
							  + resource "local_file" "StoreFrontConfigurationScriptIntoDataDirectory" { 
							      + 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             = "./data/SF-Config.ps1" 
							      + id                   = (known after apply) 
							    }
						 

						
							  # local_file.WriteCVADDBScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteCVADDBScriptIntoDataDirectory" { 
							      + 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             = "./data/CVAD-DBs.ps1" 
							      + id                   = (known after apply) 
							    }
						 

						
							  # local_file.WriteCVADScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteCVADScriptIntoDataDirectory" { 
							      + content              = &lt;&lt;-EOT 
							            New-Item -Path 'C:/TEMP/XDINST' -ItemType Directory 
							            D:/x64/"XenDesktop Setup"/XenDesktopServerSetup.exe /components controller,webstudio,licenseserver,desktopdirector /noreboot /configure_firewall /quiet /nosql /Logpath C:/TEMP/XDINST 
							            #Restart-Computer -Force 
							        EOT 
							      + 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             = "./data/CVAD-Install.ps1" 
							      + id                   = (known after apply) 
							    }
						 

						
							  # local_file.WriteDDC1SFTaskRunScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteDDC1SFTaskRunScriptIntoDataDirectory" { 
							      + content              = &lt;&lt;-EOT 
							            Add-PSSnapin Citrix.Storefront.* 
							            Start-Service -Name CitrixClusterService 
							            $JoinObject = Start-STFServerGroupJoin -IsAuthorizingServer -Confirm:$false 
							            $JoinPassCode = ($JoinObject | Select -ExpandProperty 'Passcode') 
							            $JoinPassCode | Out-File -Force -FilePath 'C:/TEMP/XDINST/pc.txt' 
							            Copy-Item -Path C:/TEMP/XDINST/pc.txt -Destination \\\\10.10.170.12\c$\temp\XDINST\pc.txt 
							            Write-Output 'Replication enabled.' 
							              } 
							        EOT 
							      + 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             = "./data/SF-CMD-EnableReplication.ps1" 
							      + id                   = (known after apply) 
							    }
						 

						
							  # local_file.WriteDDC1SFTaskScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteDDC1SFTaskScriptIntoDataDirectory" { 
							      + 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             = "./data/SF-Task-StartReplication-DDC1.ps1" 
							      + id                   = (known after apply) 
							    }
						 

						
							  # local_file.WriteDDC2SFTaskRunScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteDDC2SFTaskRunScriptIntoDataDirectory" { 
							      + content              = &lt;&lt;-EOT 
							            Add-PSSnapin Citrix.Storefront.* 
							            Start-Service -Name CitrixClusterService 
							            $Passcode = Get-Content -Path 'C:/TEMP/XDINST/pc.txt' 
							            Start-STFServerGroupJoin -AuthorizerHostName TACG-TF-CVDDDC1.the-austrian-citrix-guy.at -Passcode $Passcode -Confirm:$false 
							            Wait-STFServerGroupJoin -WaitTimeout 20 
							            Write-Output 'Replication complete.' 
							              } 
							        EOT 
							      + 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             = "./data/SF-CMD-StartReplication.ps1" 
							      + id                   = (known after apply) 
							    }
						 

						
							  # local_file.WriteDDC2SFTaskScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteDDC2SFTaskScriptIntoDataDirectory" { 
							      + 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             = "./data/SF-Task-StartReplication-DDC2.ps1" 
							      + id                   = (known after apply) 
							    }
						 

						
							  # local_file.WriteLicScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteLicScriptIntoDataDirectory" { 
							      + 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             = "./data/CVAD-CreateDNSARecordOnDC.ps1" 
							      + id                   = (known after apply) 
							    }
						 

						
							  # local_file.WriteRebootScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteRebootScriptIntoDataDirectory" { 
							      + content              = &lt;&lt;-EOT 
							            Restart-Computer -Force 
							        EOT 
							      + 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             = "./data/Reboot.ps1" 
							      + id                   = (known after apply) 
							    }
						 

						
							  # local_file.WriteSFScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteSFScriptIntoDataDirectory" { 
							      + content              = &lt;&lt;-EOT 
							            D:x64/StoreFront/CitrixStoreFront-x64.exe -silent 
							        EOT 
							      + 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             = "./data/SF-Install.ps1" 
							      + id                   = (known after apply) 
							    }
						 

						
							  # null_resource.InstallCVADFromCDROMOnDDC1 will be created 
							  + resource "null_resource" "InstallCVADFromCDROMOnDDC1" { 
							      + id = (known after apply) 
							    }
						 

						
							Plan: 11 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\_CVADOnvSphere\_Configuration&gt; terraform apply
						 

						
							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.StoreFrontConfigurationScriptIntoDataDirectory will be created 
							  + resource "local_file" "StoreFrontConfigurationScriptIntoDataDirectory" { 
							      + 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             = "./data/SF-Config.ps1" 
							      + id                   = (known after apply) 
							    }
						 

						
							  # local_file.WriteCVADDBScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteCVADDBScriptIntoDataDirectory" { 
							      + 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             = "./data/CVAD-DBs.ps1" 
							      + id                   = (known after apply) 
							    }
						 

						
							  # local_file.WriteCVADScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteCVADScriptIntoDataDirectory" { 
							      + content              = &lt;&lt;-EOT 
							            New-Item -Path 'C:/TEMP/XDINST' -ItemType Directory 
							            D:/x64/"XenDesktop Setup"/XenDesktopServerSetup.exe /components controller,webstudio,licenseserver,desktopdirector /noreboot /configure_firewall /quiet /nosql /Logpath C:/TEMP/XDINST 
							            #Restart-Computer -Force 
							        EOT 
							      + 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             = "./data/CVAD-Install.ps1" 
							      + id                   = (known after apply) 
							    }
						 

						
							  # local_file.WriteDDC1SFTaskRunScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteDDC1SFTaskRunScriptIntoDataDirectory" { 
							      + content              = &lt;&lt;-EOT 
							            Add-PSSnapin Citrix.Storefront.* 
							            Start-Service -Name CitrixClusterService 
							            $JoinObject = Start-STFServerGroupJoin -IsAuthorizingServer -Confirm:$false 
							            $JoinPassCode = ($JoinObject | Select -ExpandProperty 'Passcode') 
							            $JoinPassCode | Out-File -Force -FilePath 'C:/TEMP/XDINST/pc.txt' 
							            Copy-Item -Path C:/TEMP/XDINST/pc.txt -Destination \\\\10.10.170.12\c$\temp\XDINST\pc.txt 
							            Write-Output 'Replication enabled.' 
							              } 
							        EOT 
							      + 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             = "./data/SF-CMD-EnableReplication.ps1" 
							      + id                   = (known after apply) 
							    }
						 

						
							  # local_file.WriteDDC1SFTaskScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteDDC1SFTaskScriptIntoDataDirectory" { 
							      + 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             = "./data/SF-Task-StartReplication-DDC1.ps1" 
							      + id                   = (known after apply) 
							    }
						 

						
							  # local_file.WriteDDC2SFTaskRunScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteDDC2SFTaskRunScriptIntoDataDirectory" { 
							      + content              = &lt;&lt;-EOT 
							            Add-PSSnapin Citrix.Storefront.* 
							            Start-Service -Name CitrixClusterService 
							            $Passcode = Get-Content -Path 'C:/TEMP/XDINST/pc.txt' 
							            Start-STFServerGroupJoin -AuthorizerHostName TACG-TF-CVDDDC1.the-austrian-citrix-guy.at -Passcode $Passcode -Confirm:$false 
							            Wait-STFServerGroupJoin -WaitTimeout 20 
							            Write-Output 'Replication complete.' 
							              } 
							        EOT 
							      + 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             = "./data/SF-CMD-StartReplication.ps1" 
							      + id                   = (known after apply) 
							    }
						 

						
							  # local_file.WriteDDC2SFTaskScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteDDC2SFTaskScriptIntoDataDirectory" { 
							      + 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             = "./data/SF-Task-StartReplication-DDC2.ps1" 
							      + id                   = (known after apply) 
							    }
						 

						
							  # local_file.WriteLicScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteLicScriptIntoDataDirectory" { 
							      + 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             = "./data/CVAD-CreateDNSARecordOnDC.ps1" 
							      + id                   = (known after apply) 
							    }
						 

						
							  # local_file.WriteRebootScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteRebootScriptIntoDataDirectory" { 
							      + content              = &lt;&lt;-EOT 
							            Restart-Computer -Force 
							        EOT 
							      + 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             = "./data/Reboot.ps1" 
							      + id                   = (known after apply) 
							    }
						 

						
							  # local_file.WriteSFScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteSFScriptIntoDataDirectory" { 
							      + content              = &lt;&lt;-EOT 
							            D:x64/StoreFront/CitrixStoreFront-x64.exe -silent 
							        EOT 
							      + 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             = "./data/SF-Install.ps1" 
							      + id                   = (known after apply) 
							    }
						 

						
							  # null_resource.InstallCVADFromCDROMOnDDC1 will be created 
							  + resource "null_resource" "InstallCVADFromCDROMOnDDC1" { 
							      + id = (known after apply) 
							    }
						 

						
							Plan: 11 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
						 

						
							local_file.WriteDDC1SFTaskScriptIntoDataDirectory: Creating... 
							local_file.WriteRebootScriptIntoDataDirectory: Creating... 
							local_file.WriteLicScriptIntoDataDirectory: Creating... 
							local_file.WriteSFScriptIntoDataDirectory: Creating... 
							local_file.WriteDDC1SFTaskRunScriptIntoDataDirectory: Creating... 
							local_file.WriteCVADDBScriptIntoDataDirectory: Creating... 
							local_file.WriteLicScriptIntoDataDirectory: Creation complete after 0s [id=d6b87207cf1d0d94db1b1c024c9db91297f690fe] 
							local_file.WriteDDC1SFTaskRunScriptIntoDataDirectory: Creation complete after 0s [id=411c151a411a5031212e7d4f0042274ca402441a] 
							local_file.WriteDDC2SFTaskScriptIntoDataDirectory: Creating... 
							local_file.WriteCVADDBScriptIntoDataDirectory: Creation complete after 0s [id=ee3d362db0ed3450003739d858c39d27fefe23d6] 
							local_file.WriteSFScriptIntoDataDirectory: Creation complete after 0s [id=43e0764879f69aef387665571bc9fdc861e76795] 
							local_file.WriteRebootScriptIntoDataDirectory: Creation complete after 0s [id=2baf7fa209ad203f96017f90f5727642e5f44a03] 
							local_file.WriteDDC1SFTaskScriptIntoDataDirectory: Creation complete after 0s [id=6229451f12025d35a2e569add9ff899780513b24] 
							local_file.StoreFrontConfigurationScriptIntoDataDirectory: Creating... 
							local_file.WriteCVADScriptIntoDataDirectory: Creating... 
							local_file.WriteDDC2SFTaskScriptIntoDataDirectory: Creation complete after 0s [id=aa984fd3ab00ed8f3a239cc7647b3f5688460bfc] 
							local_file.WriteDDC2SFTaskRunScriptIntoDataDirectory: Creating... 
							local_file.StoreFrontConfigurationScriptIntoDataDirectory: Creation complete after 0s [id=d17a9ebeeb667525a1faeb180608fc89324f0a0f] 
							local_file.WriteCVADScriptIntoDataDirectory: Creation complete after 0s [id=95c755ab1fe36ee9cca4d7955c04d7c553fe3664] 
							local_file.WriteDDC2SFTaskRunScriptIntoDataDirectory: Creation complete after 0s [id=f14aed048ec9494e52cbeddd7adff1c14e336c8c] 
							null_resource.InstallCVADFromCDROMOnDDC1: Creating... 
							null_resource.InstallCVADFromCDROMOnDDC1: Provisioning with 'file'... 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.InstallCVADFromCDROMOnDDC1: Provisioning with 'remote-exec'... 
							null_resource.InstallCVADFromCDROMOnDDC1 (remote-exec): Connecting to remote host via WinRM... 
							null_resource.InstallCVADFromCDROMOnDDC1 (remote-exec):   Host: 10.10.170.11 
							null_resource.InstallCVADFromCDROMOnDDC1 (remote-exec):   Port: 5985 
							null_resource.InstallCVADFromCDROMOnDDC1 (remote-exec):   User: administrator 
							null_resource.InstallCVADFromCDROMOnDDC1 (remote-exec):   Password: true 
							null_resource.InstallCVADFromCDROMOnDDC1 (remote-exec):   HTTPS: false 
							null_resource.InstallCVADFromCDROMOnDDC1 (remote-exec):   Insecure: false 
							null_resource.InstallCVADFromCDROMOnDDC1 (remote-exec):   NTLM: false 
							null_resource.InstallCVADFromCDROMOnDDC1 (remote-exec):   CACert: false 
							null_resource.InstallCVADFromCDROMOnDDC1 (remote-exec): Connected! 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
							null_resource.InstallCVADFromCDROMOnDDC1: Still creating... [10s elapsed] 
							null_resource.InstallCVADFromCDROMOnDDC1: Still creating... [4m0s elapsed] 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.InstallCVADFromCDROMOnDDC1: Creation complete after 4m7s [id=4583907025399882491] 
							...
						 

						
							(Output shortened)
						 

						
							...
						 

						
							Apply complete! Resources: 11 added, 0 changed, 0 destroyed.
						 
					
				
			
		
	

	
		.Terraform successfully created all needed databases on the SQL Server:
	 

	
		
	 

	
		
	 

	
		The CVAD Installer was running silently and installed all needed services:
	 

	
		
	 

	
		
	 

	
		The Citrix Delivery Controller was successfully installed:
	 

	
		
	 

	
		The StoreFront cluster was successfully created:
	 

	
		
	 

	
		
	 

	
		Accessing the StoreFront cluster works:
	 

	
		
	 

	
		The Terraform flow was completed successfully, and you can start the next module.
	 

	
		Module 3: Run necessary scripts after initial deployment of Delivery Controller- and Storefront-Servers on vSphere 8
	

	
		
			IMPORTANT:
		 

		
			The /DATA directory must contain a valid License file and a public-signed certificate in .p12 format without Password Protection. 
			 
		 
	

	
		The Terraform engine requires these components and looks for these files during the workflow.  
		You can set the URIs of the Storage Repository in the corresponding variables:
	 

	
		
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}		
		
			
				
					
						
							
								... 
								    "CVAD-Lic-Name":"FID_c22d92a1_7e59_4820_a3a9_68e8b5ecdad9.lic", 
								    "CVAD-PFX-Name":"tacg.2024.pfx", 
								...
							 
						
					
				
			
		
	

	
		 
	 

	
		
			IMPORTANT: 
		 

		
			This module can only be run on the previously created Administrative VM, as the deployment relies heavily on WinRM! 
			 
		 
	

	
		This module is split into the following configuration parts:
	 

	
		
			Changing the self-signed SSL certificate on both DDCs/SFs to a public-signed certificate:

			
				
					Uploading the PFX
				
				
					Changing the IIS bindings to the uploaded certificate and resetting IIS
				
			
		
		
			Uploading and registering a valid License file to the License server:
			
				
					Uploading the License file
				
				
					Registering the License file on the License Server
				
			
		
	

	
		
			IMPORTANT: 
		 

		
			Before starting the Terraform workflow, please ensure you have configured the variables according to your needs. 
			 
		 
	

	
		The configuration can be started by following the standard Terraform workflow:
	 

	
		
			terraform init, 
			terraform plan
		 
	

	
		and if no errors occur
	 

	
		
			terraform apply
		 
	

	
		 
	 

	
		
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:"Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}		
		
			
				
					
						
							PS C:\_TACG\_CVADOnvSphere\_ConfigurationAfterStoreFront&gt; terraform init
						 

						
							Initializing the backend...
						 

						
							Initializing provider plugins... 
							- Finding latest version of hashicorp/null... 
							- Finding latest version of hashicorp/time... 
							- Finding hashicorp/vsphere versions matching "&gt;= 2.7.0"... 
							- Finding mastercard/restapi versions matching "1.18.2"... 
							- Finding citrix/citrix versions matching "0.6.0"... 
							- Finding latest version of citrix/citrixadc... 
							- Finding latest version of hashicorp/local... 
							- Installing citrix/citrix v0.6.0... 
							- Installed citrix/citrix v0.6.0 (self-signed, key ID BD4BD0E690CB7D88) 
							- Installing citrix/citrixadc v1.39.0... 
							- Installed citrix/citrixadc v1.39.0 (signed by a HashiCorp partner, key ID 25D62DD8407EA386) 
							- 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/time v0.11.2... 
							- Installed hashicorp/time v0.11.2 (signed by HashiCorp) 
							- Installing hashicorp/vsphere v2.8.1... 
							- Installed hashicorp/vsphere v2.8.1 (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\_CVADOnvSphere\_ConfigurationAfterStoreFront&gt; terraform plan 
							╷ 
							╵ 
							PS C:\_TACG\_CVADOnvSphere\_ConfigurationAfterStoreFront&gt; terraform plan
						 

						
							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.WriteIISSSLScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteIISSSLScriptIntoDataDirectory" { 
							      + 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             = "./data/CVAD-IIS.ps1" 
							      + id                   = (known after apply) 
							    }
						 

						
							  # local_file.WriteLicScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteLicScriptIntoDataDirectory" { 
							      + 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             = "./data/CVAD-Lic.ps1" 
							      + id                   = (known after apply) 
							    }
						 

						
							  # local_file.WritePFXScriptIntoDataDirectory will be created 
							  + resource "local_file" "WritePFXScriptIntoDataDirectory" { 
							      + 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             = "./data/CVAD-PFX.ps1" 
							      + id                   = (known after apply) 
							    }
						 

						
							  # null_resource.ChangeSSLOnDDC1 will be created 
							  + resource "null_resource" "ChangeSSLOnDDC1" { 
							      + id = (known after apply) 
							    }
						 

						
							  # null_resource.ChangeSSLOnDDC2 will be created 
							  + resource "null_resource" "ChangeSSLOnDDC2" { 
							      + id = (known after apply) 
							    }
						 

						
							  # null_resource.CreateCVADLicPFXOnDDC1 will be created 
							  + resource "null_resource" "CreateCVADLicPFXOnDDC1" { 
							      + id = (known after apply) 
							    }
						 

						
							  # null_resource.CreateCVADPFXOnDDC2 will be created 
							  + resource "null_resource" "CreateCVADPFXOnDDC2" { 
							      + id = (known after apply) 
							    }
						 

						
							  # null_resource.RebootDDC1-2 will be created 
							  + resource "null_resource" "RebootDDC1-2" { 
							      + id = (known after apply) 
							    }
						 

						
							  # null_resource.RebootDDC2-2 will be created 
							  + resource "null_resource" "RebootDDC2-2" { 
							      + id = (known after apply) 
							    }
						 

						
							  # time_sleep.WaitForRebootofDDC1AfterStoreFrontReplication will be created 
							  + resource "time_sleep" "WaitForRebootofDDC1AfterStoreFrontReplication" { 
							      + create_duration = "120s" 
							      + id              = (known after apply) 
							    }
						 

						
							  # time_sleep.WaitForRebootofDDC2AfterStoreFrontReplication will be created 
							  + resource "time_sleep" "WaitForRebootofDDC2AfterStoreFrontReplication" { 
							      + create_duration = "120s" 
							      + id              = (known after apply) 
							    }
						 

						
							Plan: 11 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\_CVADOnvSphere\_ConfigurationAfterStoreFront&gt; terraform apply
						 

						
							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.WriteIISSSLScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteIISSSLScriptIntoDataDirectory" { 
							      + 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             = "./data/CVAD-IIS.ps1" 
							      + id                   = (known after apply) 
							    }
						 

						
							  # local_file.WriteLicScriptIntoDataDirectory will be created 
							  + resource "local_file" "WriteLicScriptIntoDataDirectory" { 
							      + 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             = "./data/CVAD-Lic.ps1" 
							      + id                   = (known after apply) 
							    }
						 

						
							  # local_file.WritePFXScriptIntoDataDirectory will be created 
							  + resource "local_file" "WritePFXScriptIntoDataDirectory" { 
							      + 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             = "./data/CVAD-PFX.ps1" 
							      + id                   = (known after apply) 
							    }
						 

						
							  # null_resource.ChangeSSLOnDDC1 will be created 
							  + resource "null_resource" "ChangeSSLOnDDC1" { 
							      + id = (known after apply) 
							    }
						 

						
							  # null_resource.ChangeSSLOnDDC2 will be created 
							  + resource "null_resource" "ChangeSSLOnDDC2" { 
							      + id = (known after apply) 
							    }
						 

						
							  # null_resource.CreateCVADLicPFXOnDDC1 will be created 
							  + resource "null_resource" "CreateCVADLicPFXOnDDC1" { 
							      + id = (known after apply) 
							    }
						 

						
							  # null_resource.CreateCVADPFXOnDDC2 will be created 
							  + resource "null_resource" "CreateCVADPFXOnDDC2" { 
							      + id = (known after apply) 
							    }
						 

						
							  # null_resource.RebootDDC1-2 will be created 
							  + resource "null_resource" "RebootDDC1-2" { 
							      + id = (known after apply) 
							    }
						 

						
							  # null_resource.RebootDDC2-2 will be created 
							  + resource "null_resource" "RebootDDC2-2" { 
							      + id = (known after apply) 
							    }
						 

						
							Plan: 11 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
						 

						
							local_file.WriteIISSSLScriptIntoDataDirectory: Creating... 
							local_file.WritePFXScriptIntoDataDirectory: Creating... 
							local_file.WriteLicScriptIntoDataDirectory: Creating... 
							local_file.WriteIISSSLScriptIntoDataDirectory: Creation complete after 0s [id=86bab8825d5d9717e524218d8d52d3eab2c70796] 
							local_file.WriteLicScriptIntoDataDirectory: Creation complete after 0s [id=bfa627eaa71b9a817a1f342a8d013c72155addd5] 
							local_file.WritePFXScriptIntoDataDirectory: Creation complete after 0s [id=63683eea6a0c276da17943ff455714f492a090d3] 
							null_resource.CreateCVADLicPFXOnDDC1: Creating... 
							null_resource.CreateCVADPFXOnDDC2: Creating... 
							null_resource.CreateCVADPFXOnDDC2: Provisioning with 'file'... 
							null_resource.CreateCVADLicPFXOnDDC1: Provisioning with 'file'... 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.CreateCVADLicPFXOnDDC1: Provisioning with 'file'... 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.CreateCVADLicPFXOnDDC1: Provisioning with 'file'... 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.CreateCVADPFXOnDDC2: Provisioning with 'file'... 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.CreateCVADLicPFXOnDDC1: Provisioning with 'file'... 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.CreateCVADLicPFXOnDDC1: Provisioning with 'file'... 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.CreateCVADPFXOnDDC2: Provisioning with 'file'... 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.CreateCVADLicPFXOnDDC1: Provisioning with 'remote-exec'... 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec): Connecting to remote host via WinRM... 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   Host: 10.10.170.11 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   Port: 5985 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   User: administrator 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   Password: true 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   HTTPS: false 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   Insecure: false 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   NTLM: false 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   CACert: false 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec): Connected! 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.CreateCVADPFXOnDDC2: Provisioning with 'remote-exec'... 
							null_resource.CreateCVADPFXOnDDC2 (remote-exec): Connecting to remote host via WinRM... 
							null_resource.CreateCVADPFXOnDDC2 (remote-exec):   Host: 10.10.170.12 
							null_resource.CreateCVADPFXOnDDC2 (remote-exec):   Port: 5985 
							null_resource.CreateCVADPFXOnDDC2 (remote-exec):   User: administrator 
							null_resource.CreateCVADPFXOnDDC2 (remote-exec):   Password: true 
							null_resource.CreateCVADPFXOnDDC2 (remote-exec):   HTTPS: false 
							null_resource.CreateCVADPFXOnDDC2 (remote-exec):   Insecure: false 
							null_resource.CreateCVADPFXOnDDC2 (remote-exec):   NTLM: false 
							null_resource.CreateCVADPFXOnDDC2 (remote-exec):   CACert: false 
							null_resource.CreateCVADPFXOnDDC2 (remote-exec): Connected! 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/CVAD-Lic.ps1
						 

						
							null_resource.CreateCVADPFXOnDDC2 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/CVAD-PFX.ps1
						 

						
							 
							null_resource.CreateCVADPFXOnDDC2 (remote-exec):    PSParentPath: Microsoft.PowerShell.Security\Certificate::LocalMachine\Webhosting
						 

						
							null_resource.CreateCVADPFXOnDDC2 (remote-exec): Thumbprint                                Subject                                PSComputerName 
							null_resource.CreateCVADPFXOnDDC2 (remote-exec): ----------                                -------                                -------------- 
							null_resource.CreateCVADPFXOnDDC2 (remote-exec): XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX  CN=*.the-austrian-citrix-guy.at        localhost
						 

						
							 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.CreateCVADPFXOnDDC2: Creation complete after 8s [id=3398803702901324814] 
							null_resource.ChangeSSLOnDDC2: Creating... 
							null_resource.ChangeSSLOnDDC2: Provisioning with 'remote-exec'... 
							null_resource.ChangeSSLOnDDC2 (remote-exec): Connecting to remote host via WinRM... 
							null_resource.ChangeSSLOnDDC2 (remote-exec):   Host: 10.10.170.12 
							null_resource.ChangeSSLOnDDC2 (remote-exec):   Port: 5985 
							null_resource.ChangeSSLOnDDC2 (remote-exec):   User: administrator 
							null_resource.ChangeSSLOnDDC2 (remote-exec):   Password: true 
							null_resource.ChangeSSLOnDDC2 (remote-exec):   HTTPS: false 
							null_resource.ChangeSSLOnDDC2 (remote-exec):   Insecure: false 
							null_resource.ChangeSSLOnDDC2 (remote-exec):   NTLM: false 
							null_resource.ChangeSSLOnDDC2 (remote-exec):   CACert: false 
							null_resource.ChangeSSLOnDDC2 (remote-exec): Connected! 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
							null_resource.ChangeSSLOnDDC2 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/CVAD-IIS.ps1 
							null_resource.CreateCVADLicPFXOnDDC1: Still creating... [10s elapsed] 
							null_resource.ChangeSSLOnDDC2: Still creating... [10s elapsed] 
							null_resource.CreateCVADLicPFXOnDDC1: Still creating... [20s elapsed] 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.CreateCVADLicPFXOnDDC1: Provisioning with 'remote-exec'... 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec): Connecting to remote host via WinRM... 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   Host: 10.10.170.11 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   Port: 5985 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   User: administrator 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   Password: true 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   HTTPS: false 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   Insecure: false 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   NTLM: false 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):   CACert: false 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec): Connected! 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/CVAD-PFX.ps1
						 

						
							null_resource.ChangeSSLOnDDC2 (remote-exec): Attempting stop...
						 

						
							null_resource.ChangeSSLOnDDC2 (remote-exec): Internet services successfully stopped
						 

						
							null_resource.ChangeSSLOnDDC2 (remote-exec): Attempting start...
						 

						
							null_resource.ChangeSSLOnDDC2 (remote-exec): Internet services successfully restarted
						 

						
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.ChangeSSLOnDDC2: Creation complete after 16s [id=4814183603474837780] 
							null_resource.RebootDDC2-2: Creating... 
							null_resource.RebootDDC2-2: Provisioning with 'remote-exec'... 
							null_resource.RebootDDC2-2 (remote-exec): Connecting to remote host via WinRM... 
							null_resource.RebootDDC2-2 (remote-exec):   Host: 10.10.170.12 
							null_resource.RebootDDC2-2 (remote-exec):   Port: 5985 
							null_resource.RebootDDC2-2 (remote-exec):   User: administrator 
							null_resource.RebootDDC2-2 (remote-exec):   Password: true 
							null_resource.RebootDDC2-2 (remote-exec):   HTTPS: false 
							null_resource.RebootDDC2-2 (remote-exec):   Insecure: false 
							null_resource.RebootDDC2-2 (remote-exec):   NTLM: false 
							null_resource.RebootDDC2-2 (remote-exec):   CACert: false 
							null_resource.RebootDDC2-2 (remote-exec): Connected! 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;
						 

						
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec):    PSParentPath: Microsoft.PowerShell.Security\Certificate::LocalMachine\Webhosting
						 

						
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec): Thumbprint                                Subject                                PSComputerName 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec): ----------                                -------                                -------------- 
							null_resource.CreateCVADLicPFXOnDDC1 (remote-exec): XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX  CN=*.the-austrian-citrix-guy.at        localhost
						 

						
							 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
							null_resource.RebootDDC2-2 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/Reboot.ps1 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.CreateCVADLicPFXOnDDC1: Creation complete after 25s [id=6873988653914266758] 
							null_resource.ChangeSSLOnDDC1: Creating... 
							null_resource.ChangeSSLOnDDC1: Provisioning with 'remote-exec'... 
							null_resource.ChangeSSLOnDDC1 (remote-exec): Connecting to remote host via WinRM... 
							null_resource.ChangeSSLOnDDC1 (remote-exec):   Host: 10.10.170.11 
							null_resource.ChangeSSLOnDDC1 (remote-exec):   Port: 5985 
							null_resource.ChangeSSLOnDDC1 (remote-exec):   User: administrator 
							null_resource.ChangeSSLOnDDC1 (remote-exec):   Password: true 
							null_resource.ChangeSSLOnDDC1 (remote-exec):   HTTPS: false 
							null_resource.ChangeSSLOnDDC1 (remote-exec):   Insecure: false 
							null_resource.ChangeSSLOnDDC1 (remote-exec):   NTLM: false 
							null_resource.ChangeSSLOnDDC1 (remote-exec):   CACert: false 
							null_resource.ChangeSSLOnDDC1 (remote-exec): Connected! 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.RebootDDC2-2: Creation complete after 2s [id=3845374291350519787]
						 

						
							null_resource.ChangeSSLOnDDC1 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/CVAD-IIS.ps1 
							null_resource.ChangeSSLOnDDC1: Still creating... [10s elapsed]
						 

						
							null_resource.ChangeSSLOnDDC1 (remote-exec): Attempting stop...
						 

						
							null_resource.ChangeSSLOnDDC1 (remote-exec): Internet services successfully stopped
						 

						
							null_resource.ChangeSSLOnDDC1 (remote-exec): Attempting start...
						 

						
							null_resource.ChangeSSLOnDDC1 (remote-exec): Internet services successfully restarted
						 

						
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.ChangeSSLOnDDC1: Creation complete after 13s [id=8760879483318658965] 
							null_resource.RebootDDC1-2: Creating... 
							null_resource.RebootDDC1-2: Provisioning with 'remote-exec'... 
							null_resource.RebootDDC1-2 (remote-exec): Connecting to remote host via WinRM... 
							null_resource.RebootDDC1-2 (remote-exec):   Host: 10.10.170.11 
							null_resource.RebootDDC1-2 (remote-exec):   Port: 5985 
							null_resource.RebootDDC1-2 (remote-exec):   User: administrator 
							null_resource.RebootDDC1-2 (remote-exec):   Password: true 
							null_resource.RebootDDC1-2 (remote-exec):   HTTPS: false 
							null_resource.RebootDDC1-2 (remote-exec):   Insecure: false 
							null_resource.RebootDDC1-2 (remote-exec):   NTLM: false 
							null_resource.RebootDDC1-2 (remote-exec):   CACert: false 
							null_resource.RebootDDC1-2 (remote-exec): Connected! 
							#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
							&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
							null_resource.RebootDDC1-2 (remote-exec): C:\Users\Administrator&gt;powershell -File C:/TEMP/XDINST/Reboot.ps1 
							null_resource.RebootDDC1-2: Creation complete after 2s [id=2206709629528652083]
						 

						
							Apply complete! Resources: 11 added, 0 changed, 0 destroyed. 
							PS C:\_TACG\_CVADOnvSphere\_ConfigurationAfterStoreFront&gt;
						 
					
				
			
		
	

	
		The Terraform flow and all scripts ran successfully.
	 

	
		The License was registered on the License Server:
	 

	
		
	 

	
		
	 

	
		The IIS certificate was changed and bound successfully:
	 

	
		
	 

	
		Running a Site Configuration check shows everything is fine:
	 

	
		
	 

	
		The Terraform flow completed successfully, and you can start the next module.
	 

	
		 
	 

	
		Module 4: Create all further needed CVAD entities like the Hypervisor Connection, the Hypervisor Resource Pool, a Machine Catalog, and a Delivery Group 
		 
	

	
		
			IMPORTANT: 
		 

		
			This module can only be run on the previously created Administrative VM, as the deployment relies heavily on WinRM! 
			 
		 
	

	
		This module is split into the following configuration parts:
	 

	
		
			Creating the Hypervisor Connection, the Hypervisor Resource Pool, the Machine Catalog, and the Delivery Group with complete AutoScale configuration
		
	

	
		
			IMPORTANT: 
		 

		
			Before starting the Terraform workflow, please ensure you have configured the variables according to your needs. 
			 
		 
	

	
		The configuration can be started by following the standard Terraform workflow:
	 

	
		
			terraform init, 
			terraform plan
		 
	

	
		and if no errors occur
	 

	
		
			terraform apply
		 
	

	
		 
	 

	
		
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:"Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}		
		
			
				
					
						
							 
							PS C:\_TACG\_CVADOnvSphere\_CVAD-Entities&gt; terraform init
						 

						
							Initializing the backend...
						 

						
							Initializing provider plugins... 
							- Finding mastercard/restapi versions matching "1.18.2"... 
							- Finding citrix/citrix versions matching "0.6.0"... 
							- Finding latest version of citrix/citrixadc... 
							- Finding hashicorp/vsphere versions matching "&gt;= 2.7.0"... 
							- Installing mastercard/restapi v1.18.2... 
							- Installed mastercard/restapi v1.18.2 (self-signed, key ID DCB8C431D71C30AB) 
							- Installing citrix/citrix v0.6.0... 
							- Installed citrix/citrix v0.6.0 (self-signed, key ID BD4BD0E690CB7D88) 
							- Installing citrix/citrixadc v1.39.0... 
							- Installed citrix/citrixadc v1.39.0 (signed by a HashiCorp partner, key ID 25D62DD8407EA386) 
							- Installing hashicorp/vsphere v2.8.1... 
							- Installed hashicorp/vsphere v2.8.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\_CVADOnvSphere\_CVAD-Entities&gt; terraform plan 
							data.vsphere_datacenter.datacenter: Reading... 
							data.vsphere_datacenter.datacenter: Read complete after 0s [id=datacenter-3] 
							data.vsphere_datastore.datastore: Reading... 
							data.vsphere_host.host: Reading... 
							data.vsphere_network.network: Reading... 
							data.vsphere_compute_cluster.cluster: Reading... 
							data.vsphere_network.network: Read complete after 0s [id=network-12] 
							data.vsphere_datastore.datastore: Read complete after 0s [id=datastore-11] 
							data.vsphere_host.host: Read complete after 0s [id=host-8] 
							data.vsphere_compute_cluster.cluster: Read complete after 0s [id=domain-c17]
						 

						
							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.DG-TACG-TF-ONP-VSP will be created 
							  + resource "citrix_delivery_group" "DG-TACG-TF-ONP-VSP" { 
							      + associated_machine_catalogs = [ 
							          + { 
							              + machine_catalog = (known after apply) 
							              + machine_count   = 2 
							            }, 
							        ] 
							      + autoscale_settings          = { 
							          + autoscale_enabled                                   = true 
							          + disconnect_off_peak_idle_session_after_seconds      = 0 
							          + disconnect_peak_idle_session_after_seconds          = 120 
							          + log_off_off_peak_disconnected_session_after_seconds = 0 
							          + log_off_peak_disconnected_session_after_seconds     = 120 
							          + 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                              = "Suspend" 
							          + peak_disconnect_timeout_minutes                     = 0 
							          + peak_extended_disconnect_action                     = "Nothing" 
							          + peak_extended_disconnect_timeout_minutes            = 0 
							          + peak_log_off_action                                 = "Suspend" 
							          + power_off_delay_minutes                             = 30 
							          + power_time_schemes                                  = [ 
							              + { 
							                  + days_of_week          = [ 
							                      + "Monday", 
							                      + "Tuesday", 
							                      + "Wednesday", 
							                      + "Thursday", 
							                      + "Friday", 
							                    ] 
							                  + display_name          = "AS-TACG-TF" 
							                  + 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             = "W11 on VSP" 
							              + enable_session_roaming  = true 
							              + enabled                 = true 
							              + published_name          = "Desktop W11" 
							              + restricted_access_users = { 
							                  + allow_list = [ 
							                      + "TACG\\GRP-LOC-RPC-W11N", 
							                    ] 
							                } 
							            }, 
							        ] 
							      + id                          = (known after apply) 
							      + minimum_functional_level    = "L7_20" 
							      + name                        = "DG-TACG-TF-ONP-VSP" 
							      + reboot_schedules            = [ 
							          + { 
							              + days_in_week                 = [ 
							                  + "Monday", 
							                  + "Wednesday", 
							                  + "Friday", 
							                ] 
							              + frequency                    = "Weekly" 
							              + frequency_factor             = 1 
							              + ignore_maintenance_mode      = true 
							              + name                         = "RS-TACG-TF-VSP" 
							              + natural_reboot_schedule      = false 
							              + reboot_duration_minutes      = 0 
							              + reboot_notification_to_users = { 
							                  + notification_duration_minutes       = 15 
							                  + notification_message                = "test message" 
							                  + notification_repeat_every_5_minutes = true 
							                  + notification_title                  = "test title" 
							                } 
							              + reboot_schedule_enabled      = false 
							              + start_date                   = "2024-01-01" 
							              + start_time                   = "02:00" 
							            }, 
							        ] 
							      + restricted_access_users     = { 
							          + allow_list = [ 
							              + "TACG\\GRP-LOC-RPC-W11N", 
							            ] 
							        } 
							      + total_machines              = (known after apply) 
							    }
						 

						
							  # citrix_machine_catalog.MC-TACG-TF-ONP-VSP will be created 
							  + resource "citrix_machine_catalog" "MC-TACG-TF-ONP-VSP" { 
							      + allocation_type          = "Random" 
							      + description              = "MC by TF on VSP" 
							      + id                       = (known after apply) 
							      + is_power_managed         = true 
							      + minimum_functional_level = "L7_20" 
							      + name                     = "MC-TACG-TF-ONP-VSP" 
							      + provisioning_scheme      = { 
							          + hypervisor                     = (known after apply) 
							          + hypervisor_resource_pool       = (known after apply) 
							          + identity_type                  = "ActiveDirectory" 
							          + machine_account_creation_rules = { 
							              + naming_scheme      = "TF-VM-W11-#" 
							              + naming_scheme_type = "Numeric" 
							            } 
							          + machine_domain_identity        = { 
							              + domain                   = (sensitive value) 
							              + service_account          = (sensitive value) 
							              + service_account_password = (sensitive value) 
							            } 
							          + number_of_total_machines       = 2 
							          + vsphere_machine_config         = { 
							              + cpu_count       = 2 
							              + image_snapshot  = "vsp-main/vsp-libre" 
							              + master_image_vm = "TACG-VSP-W11-M" 
							              + memory_mb       = 4096 
							            } 
							        } 
							      + provisioning_type        = "MCS" 
							      + session_support          = "SingleSession" 
							      + zone                     = (known after apply) 
							    }
						 

						
							  # citrix_vsphere_hypervisor.TACG-TF-HypConn will be created 
							  + resource "citrix_vsphere_hypervisor" "TACG-TF-HypConn" { 
							      + addresses                                = [ 
							          + (sensitive value), 
							        ] 
							      + id                                       = (known after apply) 
							      + max_absolute_active_actions              = 20 
							      + max_absolute_new_actions_per_minute      = 10 
							      + max_power_actions_percentage_of_machines = 20 
							      + name                                     = "TACG-TF-HypConnTF" 
							      + password                                 = (sensitive value) 
							      + password_format                          = "PlainText" 
							      + username                                 = (sensitive value) 
							      + zone                                     = (known after apply) 
							    }
						 

						
							  # citrix_vsphere_hypervisor_resource_pool.TACG-TF-HypConnPool will be created 
							  + resource "citrix_vsphere_hypervisor_resource_pool" "TACG-TF-HypConnPool" { 
							      + cluster                   = { 
							          + cluster_name = "TACG" 
							          + datacenter   = (sensitive value) 
							        } 
							      + hypervisor                = (known after apply) 
							      + id                        = (known after apply) 
							      + name                      = "TACG-TF-HypConnPoolTF" 
							      + networks                  = [ 
							          + (sensitive value), 
							        ] 
							      + storage                   = [ 
							          + { 
							              + storage_name = (sensitive value) 
							              + superseded   = false 
							            }, 
							        ] 
							      + temporary_storage         = [ 
							          + { 
							              + storage_name = (sensitive value) 
							              + superseded   = false 
							            }, 
							        ] 
							      + use_local_storage_caching = false 
							    }
						 

						
							  # citrix_zone.TACG-TF-HYP-Zone will be created 
							  + resource "citrix_zone" "TACG-TF-HYP-Zone" { 
							      + id   = (known after apply) 
							      + name = "TACG-TF-Zone" 
							    }
						 

						
							Plan: 5 to add, 0 to change, 0 to destroy.
						 

						
							Changes to Outputs: 
							  + ZoneID = (known after apply)
						 

						
							───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
						 

						
							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\_CVADOnvSphere\_CVAD-Entities&gt; terraform apply 
							data.vsphere_datacenter.datacenter: Reading... 
							data.vsphere_datacenter.datacenter: Read complete after 0s [id=datacenter-3] 
							data.vsphere_datastore.datastore: Reading... 
							data.vsphere_compute_cluster.cluster: Reading... 
							data.vsphere_network.network: Reading... 
							data.vsphere_host.host: Reading... 
							data.vsphere_network.network: Read complete after 0s [id=network-12] 
							data.vsphere_datastore.datastore: Read complete after 0s [id=datastore-11] 
							data.vsphere_host.host: Read complete after 0s [id=host-8] 
							data.vsphere_compute_cluster.cluster: Read complete after 0s [id=domain-c17]
						 

						
							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.DG-TACG-TF-ONP-VSP will be created 
							  + resource "citrix_delivery_group" "DG-TACG-TF-ONP-VSP" { 
							      + associated_machine_catalogs = [ 
							          + { 
							              + machine_catalog = (known after apply) 
							              + machine_count   = 2 
							            }, 
							        ] 
							      + autoscale_settings          = { 
							          + autoscale_enabled                                   = true 
							          + disconnect_off_peak_idle_session_after_seconds      = 0 
							          + disconnect_peak_idle_session_after_seconds          = 120 
							          + log_off_off_peak_disconnected_session_after_seconds = 0 
							          + log_off_peak_disconnected_session_after_seconds     = 120 
							          + 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                              = "Suspend" 
							          + peak_disconnect_timeout_minutes                     = 0 
							          + peak_extended_disconnect_action                     = "Nothing" 
							          + peak_extended_disconnect_timeout_minutes            = 0 
							          + peak_log_off_action                                 = "Suspend" 
							          + power_off_delay_minutes                             = 30 
							          + power_time_schemes                                  = [ 
							              + { 
							                  + days_of_week          = [ 
							                      + "Monday", 
							                      + "Tuesday", 
							                      + "Wednesday", 
							                      + "Thursday", 
							                      + "Friday", 
							                    ] 
							                  + display_name          = "AS-TACG-TF" 
							                  + 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             = "W11 on VSP" 
							              + enable_session_roaming  = true 
							              + enabled                 = true 
							              + published_name          = "Desktop W11" 
							              + restricted_access_users = { 
							                  + allow_list = [ 
							                      + "TACG\\GRP-LOC-RPC-W11N", 
							                    ] 
							                } 
							            }, 
							        ] 
							      + id                          = (known after apply) 
							      + minimum_functional_level    = "L7_20" 
							      + name                        = "DG-TACG-TF-ONP-VSP" 
							      + reboot_schedules            = [ 
							          + { 
							              + days_in_week                 = [ 
							                  + "Monday", 
							                  + "Wednesday", 
							                  + "Friday", 
							                ] 
							              + frequency                    = "Weekly" 
							              + frequency_factor             = 1 
							              + ignore_maintenance_mode      = true 
							              + name                         = "RS-TACG-TF-VSP" 
							              + natural_reboot_schedule      = false 
							              + reboot_duration_minutes      = 0 
							              + reboot_notification_to_users = { 
							                  + notification_duration_minutes       = 15 
							                  + notification_message                = "test message" 
							                  + notification_repeat_every_5_minutes = true 
							                  + notification_title                  = "test title" 
							                } 
							              + reboot_schedule_enabled      = false 
							              + start_date                   = "2024-01-01" 
							              + start_time                   = "02:00" 
							            }, 
							        ] 
							      + restricted_access_users     = { 
							          + allow_list = [ 
							              + "TACG\\GRP-LOC-RPC-W11N", 
							            ] 
							        } 
							      + total_machines              = (known after apply) 
							    }
						 

						
							  # citrix_machine_catalog.MC-TACG-TF-ONP-VSP will be created 
							  + resource "citrix_machine_catalog" "MC-TACG-TF-ONP-VSP" { 
							      + allocation_type          = "Random" 
							      + description              = "MC by TF on VSP" 
							      + id                       = (known after apply) 
							      + is_power_managed         = true 
							      + minimum_functional_level = "L7_20" 
							      + name                     = "MC-TACG-TF-ONP-VSP" 
							      + provisioning_scheme      = { 
							          + hypervisor                     = (known after apply) 
							          + hypervisor_resource_pool       = (known after apply) 
							          + identity_type                  = "ActiveDirectory" 
							          + machine_account_creation_rules = { 
							              + naming_scheme      = "TF-VM-W11-#" 
							              + naming_scheme_type = "Numeric" 
							            } 
							          + machine_domain_identity        = { 
							              + domain                   = (sensitive value) 
							              + service_account          = (sensitive value) 
							              + service_account_password = (sensitive value) 
							            } 
							          + number_of_total_machines       = 2 
							          + vsphere_machine_config         = { 
							              + cpu_count       = 2 
							              + image_snapshot  = "vsp-main/vsp-libre" 
							              + master_image_vm = "TACG-VSP-W11-M" 
							              + memory_mb       = 4096 
							            } 
							        } 
							      + provisioning_type        = "MCS" 
							      + session_support          = "SingleSession" 
							      + zone                     = (known after apply) 
							    }
						 

						
							  # citrix_vsphere_hypervisor.TACG-TF-HypConn will be created 
							  + resource "citrix_vsphere_hypervisor" "TACG-TF-HypConn" { 
							      + addresses                                = [ 
							          + (sensitive value), 
							        ] 
							      + id                                       = (known after apply) 
							      + max_absolute_active_actions              = 20 
							      + max_absolute_new_actions_per_minute      = 10 
							      + max_power_actions_percentage_of_machines = 20 
							      + name                                     = "TACG-TF-HypConnTF" 
							      + password                                 = (sensitive value) 
							      + password_format                          = "PlainText" 
							      + username                                 = (sensitive value) 
							      + zone                                     = (known after apply) 
							    }
						 

						
							  # citrix_vsphere_hypervisor_resource_pool.TACG-TF-HypConnPool will be created 
							  + resource "citrix_vsphere_hypervisor_resource_pool" "TACG-TF-HypConnPool" { 
							      + cluster                   = { 
							          + cluster_name = "TACG" 
							          + datacenter   = (sensitive value) 
							        } 
							      + hypervisor                = (known after apply) 
							      + id                        = (known after apply) 
							      + name                      = "TACG-TF-HypConnPoolTF" 
							      + networks                  = [ 
							          + (sensitive value), 
							        ] 
							      + storage                   = [ 
							          + { 
							              + storage_name = (sensitive value) 
							              + superseded   = false 
							            }, 
							        ] 
							      + temporary_storage         = [ 
							          + { 
							              + storage_name = (sensitive value) 
							              + superseded   = false 
							            }, 
							        ] 
							      + use_local_storage_caching = false 
							    }
						 

						
							  # citrix_zone.TACG-TF-HYP-Zone will be created 
							  + resource "citrix_zone" "TACG-TF-HYP-Zone" { 
							      + id   = (known after apply) 
							      + name = "TACG-TF-Zone" 
							    }
						 

						
							Plan: 5 to add, 0 to change, 0 to destroy.
						 

						
							Changes to Outputs: 
							  + ZoneID = (known after apply)
						 

						
							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_zone.TACG-TF-HYP-Zone: Creating... 
							citrix_zone.TACG-TF-HYP-Zone: Creation complete after 0s [id=229ea4fe-8f8b-43df-9f43-45618065d857] 
							citrix_vsphere_hypervisor.TACG-TF-HypConn: Creating... 
							citrix_vsphere_hypervisor.TACG-TF-HypConn: Still creating... [10s elapsed] 
							citrix_vsphere_hypervisor.TACG-TF-HypConn: Still creating... [20s elapsed] 
							citrix_vsphere_hypervisor.TACG-TF-HypConn: Still creating... [30s elapsed] 
							citrix_vsphere_hypervisor.TACG-TF-HypConn: Still creating... [40s elapsed] 
							citrix_vsphere_hypervisor.TACG-TF-HypConn: Still creating... [50s elapsed] 
							citrix_vsphere_hypervisor.TACG-TF-HypConn: Still creating... [1m0s elapsed] 
							citrix_vsphere_hypervisor.TACG-TF-HypConn: Still creating... [1m10s elapsed] 
							citrix_vsphere_hypervisor.TACG-TF-HypConn: Creation complete after 1m10s [id=8744310a-78fb-40c3-822e-46592fb371c7] 
							citrix_vsphere_hypervisor_resource_pool.TACG-TF-HypConnPool: Creating... 
							citrix_vsphere_hypervisor_resource_pool.TACG-TF-HypConnPool: Still creating... [10s elapsed] 
							citrix_vsphere_hypervisor_resource_pool.TACG-TF-HypConnPool: Creation complete after 11s [id=956ed338-fccd-4666-aecf-4f3763becc87] 
							citrix_machine_catalog.MC-TACG-TF-ONP-VSP: Creating... 
							citrix_machine_catalog.MC-TACG-TF-ONP-VSP: Still creating... [10s elapsed] 
							citrix_machine_catalog.MC-TACG-TF-ONP-VSP: Still creating... [20s elapsed] 
							citrix_machine_catalog.MC-TACG-TF-ONP-VSP: Still creating... [30s elapsed] 
							citrix_machine_catalog.MC-TACG-TF-ONP-VSP: Still creating... [40s elapsed] 
							citrix_machine_catalog.MC-TACG-TF-ONP-VSP: Still creating... [50s elapsed] 
							citrix_machine_catalog.MC-TACG-TF-ONP-VSP: Still creating... [1m0s elapsed] 
							citrix_machine_catalog.MC-TACG-TF-ONP-VSP: Still creating... [1m10s elapsed] 
							citrix_machine_catalog.MC-TACG-TF-ONP-VSP: Still creating... [1m20s elapsed] 
							citrix_machine_catalog.MC-TACG-TF-ONP-VSP: Still creating... [1m30s elapsed] 
							citrix_machine_catalog.MC-TACG-TF-ONP-VSP: Still creating... [1m40s elapsed] 
							citrix_machine_catalog.MC-TACG-TF-ONP-VSP: Still creating... [1m50s elapsed] 
							citrix_machine_catalog.MC-TACG-TF-ONP-VSP: Still creating... [2m0s elapsed] 
							citrix_machine_catalog.MC-TACG-TF-ONP-VSP: Creation complete after 2m0s [id=20992308-6847-4451-8eaa-adf452f8bde3] 
							citrix_delivery_group.DG-TACG-TF-ONP-VSP: Creating... 
							citrix_delivery_group.DG-TACG-TF-ONP-VSP: Creation complete after 2s [id=97886252-fbf1-4a73-ad93-129d6ebac904]
						 

						
							Apply complete! Resources: 5 added, 0 changed, 0 destroyed.
						 

						
							PS C:\_TACG\_CVADOnvSphere\_CVAD-Entities&gt; 
							 
						 
					
				
			
		
	

	
		The Terraform flow and all scripts ran successfully:
	 

	
		Terraform successfully created a Hosting Connection and a Hosting Connection Resource Pool:
	 

	
		
	 

	
		A Zone was successfully created:
	 

	
		
	 

	
		A Machine Catalog was successfully created:
	 

	
		
	 

	
		A Delivery Group was successfully created:
	 

	
		
	 

	
		AutoScale was successfully configured:
	 

	
		
	 

	
		Accessing the VDI Desktop using StoreFront works:
	 

	
		
	 

	
		
	 

	
		An HDX session has been successfully brokenred, and we are connected to our VDI VM.
	 

	
		
	 

	
		 
	 

	
		This concludes our guide, "Using Terraform to deploy Citrix Virtual Apps and Desktops 2402 LTSR on vSphere 8."
	 

	
		You can find the related guide "Using Terraform to deploy a Citrix DaaS Resource Location on vSphere 8" on Citrix Tech Zone: https://community.citrix.com/tech-zone/build/deployment-guides/terraform-daas-vsphere8.
	 

	
		 
	 

	
		Appendix
	

	
		
			Disclaimer
		 

		
			EXCEPT WHERE EXPRESSLY PROVIDED OTHERWISE BY CLOUD SOFTWARE GROUP, THE CONTENT IN THIS DOCUMENT IS PROVIDED “AS IS” AND CLOUD SOFTWARE GROUP HEREBY DISCLAIMS ALL EXPRESS OR IMPLIED REPRESENTATIONS, WARRANTIES, GUARANTIES, AND CONDITIONS, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES OR CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD TO BE LEGALLY INVALID. CLOUD SOFTWARE GROUP MAKES NO REPRESENTATIONS, WARRANTIES, GUARANTIES, OR CONDITIONS AS TO THE QUALITY, SUITABILITY, TRUTH, ACCURACY, OR COMPLETENESS OF ANY OF THE CONTENT CONTAINED ON THE WEBSITE.
		 

		
			The above disclaimer applies to any damages, liability, or injuries caused by any failure of performance, error, omission, interruption, deletion, defect, delay in operation or transmission, computer virus, communication line failure, theft or destruction of or unauthorized access to, alteration of, or use, whether for breach of contract, tort, negligence or any other cause of action.
		 

		
			 
		 
	

	
		Module 1
	

	
		CVADOnvSphere-Creation-provider.tf
	 

	
		
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}		
		
			
				
					
						
							
								
									# Terraform deployment of Citrix Virtual Apps and Desktops on VMware vSphere
								

								
									## Definition of all required Terraform providers
								
								 

								
									terraform {
								

								
									    required_version = "&gt;= 1.8.4"
								
								 

								
									  required_providers {
								

								
									    vsphere = {
								

								
									      source  = "hashicorp/vsphere"
								

								
									      version = "&gt;= 2.7.0"
								

								
									    }
								
								 

								
									    restapi = {
								

								
									      source = "Mastercard/restapi"
								

								
									      version = "1.18.2"
								

								
									    }
								
								 

								
									    citrix = {
								

								
									      source  = "citrix/citrix"
								

								
									      version = "=0.6.0"
								

								
									    }
								
								 

								
									    citrixadc = {
								

								
									      source = "citrix/citrixadc"
								

								
									    }
								

								
									  
								

								
									  }
								

								
									}
								
								 

								
									provider "citrix" {
								

								
									  hostname      = var.CVAD_Provider-FQDN
								

								
									  client_id     = var.CVAD_Provider-UN
								

								
									  client_secret = var.CVAD_Provider-PW
								

								
									  disable_ssl_verification = true
								

								
									}
								
								 

								
									provider "citrixadc" {
								

								
									  endpoint             = "https://${var.NSVPX-NSIP1}"
								

								
									  username             = var.NSVPX-UN # NS_LOGIN env variable
								

								
									  password             = var.NSVPX-PW # NS_PASSWORD env variable
								

								
									  insecure_skip_verify = true
								

								
									}
								
								 

								
									provider "vsphere" {
								

								
									  user                 = var.vsphere_user
								

								
									  password             = var.vsphere_pw
								

								
									  vsphere_server       = var.vsphere_url
								

								
									  vim_keep_alive       = var.vsphere_vim_keep_alive
								

								
									  api_timeout          = var.vsphere_api_timeout
								

								
									  allow_unverified_ssl = true
								

								
									}
								
								 

								
									data "vsphere_datacenter" "datacenter" {
								

								
									  name                 = var.vsphere_datacenter
								

								
									}
								
								 

								
									data "vsphere_datastore" "datastore" {
								

								
									  name                 = var.vsphere_datastore
								

								
									  datacenter_id = data.vsphere_datacenter.datacenter.id
								

								
									}
								
								 

								
									data "vsphere_network" "network" {
								

								
									  name                 = var.vsphere_network
								

								
									  datacenter_id = data.vsphere_datacenter.datacenter.id
								

								
									}
								
								 

								
									data "vsphere_host" "host" {
								

								
									  name          = var.vsphere_host
								

								
									  datacenter_id = data.vsphere_datacenter.datacenter.id
								

								
									}
								
							
						
					
				
			
		
	

	
		 
	 

	
		
			
				CVADOnvSphere-Creation-Create.tf
			 

			
				
				
				
					
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}					
					
						
							
								
									
										
											
												# Terraform deployment of Citrix Virtual Apps and Desktops on VMware vSphere
											
											 

											
												## Create the NSVPX HA-Pair
											

											
												## Create the Windows Server-VMs for Delivery Controller and Storefront - due to resource constraints each DDC will also host SF
											
											 

											
												data "vsphere_virtual_machine" "W2K22_Template" {
											

											
												  name          = var.vsphere_W2K22_template_name
											

											
												  datacenter_id = data.vsphere_datacenter.datacenter.id
											

											
												}
											
											 

											
												data "vsphere_ovf_vm_template" "NSVPX_Template" {
											

											
												  name              = "NSVPX"
											

											
												  disk_provisioning = "thin"
											

											
												  resource_pool_id  = data.vsphere_resource_pool.pool.id
											

											
												  datastore_id      = data.vsphere_datastore.datastore.id
											

											
												  host_system_id    = data.vsphere_host.host.id
											

											
												  local_ovf_path   = join("/",["${path.module}", "${var.vsphere_nsvpx_ovf_path}"])
											
											 

											
												  ovf_network_map = {
											

											
												    "VM Network" : data.vsphere_network.network.id
											

											
												  }
											

											
												}
											
											 

											
												### Upload Userdata.iso for NSVPX1 Pre-Boot
											

											
												resource "vsphere_file" "NSVPX1_Upload_Iso" {
											

											
												  datacenter                = data.vsphere_datacenter.datacenter.name
											

											
												  datastore                 = data.vsphere_datastore.datastore.name
											

											
												  source_file               = join("",["${path.module}",var.vsphere_nsvpx1_iso_source_path])
											

											
												  destination_file          = var.vsphere_nsvpx1_iso_destination_path
											

											
												}
											
											 

											
												### Upload Userdata.iso for NSVPX2 Pre-Boot
											

											
												resource "vsphere_file" "NSVPX2_Upload_Iso" {
											

											
												  datacenter                = data.vsphere_datacenter.datacenter.name
											

											
												  datastore                 = data.vsphere_datastore.datastore.name
											

											
												  source_file               = join("",["${path.module}",var.vsphere_nsvpx2_iso_source_path])
											

											
												  destination_file          = var.vsphere_nsvpx2_iso_destination_path
											

											
												}
											
											 

											
												### Upload CVAD-ISO 
											

											
												resource "vsphere_file" "CVAD_Upload_Iso" {
											

											
												  datacenter                = data.vsphere_datacenter.datacenter.name
											

											
												  datastore                 = data.vsphere_datastore.datastore.name
											

											
												  source_file               = join("",["${path.module}",var.vsphere_cvad_iso_source_path])
											

											
												  destination_file          = var.vsphere_cvad_iso_destination_path
											

											
												}
											
											 

											
												### Create DDC1-VM
											

											
												resource "vsphere_virtual_machine" "ddc1-vm" {
											

											
												 # depends_on = [ vsphere_file.CVAD_Upload_Iso ]
											

											
												  name             = var.vsphere_ddc1_vmname
											

											
												  resource_pool_id = data.vsphere_resource_pool.pool.id
											

											
												  datastore_id     = data.vsphere_datastore.datastore.id
											

											
												  num_cpus         = var.vsphere_ddc1_cpus
											

											
												  memory           = var.vsphere_ddc1_ram
											

											
												  guest_id         = data.vsphere_virtual_machine.W2K22_Template.guest_id
											

											
												  scsi_type        = data.vsphere_virtual_machine.W2K22_Template.scsi_type
											

											
												  firmware         = "efi"
											

											
												  
											

											
												  
											

											
												  network_interface {
											

											
												    network_id   = data.vsphere_network.network.id
											

											
												    adapter_type = data.vsphere_virtual_machine.W2K22_Template.network_interface_types[0]
											

											
												  }
											
											 

											
												  disk {
											

											
												    label             = var.vsphere_ddc1_disk_name
											
											 

											
												    ### If you want to use the same disk size and type as hte template, uncomment this block
											

											
												    size             = data.vsphere_virtual_machine.W2K22_Template.disks.0.size
											

											
												    thin_provisioned = data.vsphere_virtual_machine.W2K22_Template.disks.0.thin_provisioned
											

											
												    
											

											
												    ### If you want to override the disk size and disk provisioning type, uncomment this block and set the corresponding variables
											

											
												    #size             = var.vsphere_ddc1_disk_size
											

											
												    #thin_provisioned = var.vsphere_ddc1_disk_provtype
											

											
												  }
											
											 

											
												    cdrom {
											

											
												    datastore_id              = data.vsphere_datastore.datastore.id
											

											
												    path                      = var.vsphere_cvad_iso_destination_path
											

											
												  }
											
											 

											
												  clone {
											

											
												    template_uuid = data.vsphere_virtual_machine.W2K22_Template.id
											
											 

											
												    customize {
											

											
												      
											

											
												      ### If you want to use the provider variables to customize the server, uncomment this block
											

											
												      windows_options {
											

											
												        computer_name               = var.vsphere_ddc1_hostname
											

											
												        admin_password              = var.vsphere_local_admin_pw
											

											
												        join_domain                 = var.vsphere_domainname
											

											
												        domain_admin_user           = var.vsphere_domain_admin_user
											

											
												        domain_admin_password       = var.vsphere_domain_admin_pw
											

											
												        full_name                   = var.vsphere_orgowner
											

											
												        organization_name           = var.vsphere_orgname
											

											
												       # run_once_command_list       = var.vsphere_ddc1_command_list
											

											
												        auto_logon                  = var.vsphere_autologon
											

											
												        auto_logon_count            = var.vsphere_autologon_count
											

											
												      }
											
											 

											
												      ### If you want to use a Sysprep file, uncomment this block
											

											
												      # windows_sysprep_text = file("${path.module}/sysprep.xml")
											
											 

											
												      network_interface {
											

											
												        ipv4_address        = var.vsphere_ddc1_ipv4
											

											
												        ipv4_netmask        = var.vsphere_ddc_ipv4netmask #24
											

											
												        dns_server_list     = var.vsphere_ddc_dns
											

											
												      }
											

											
												      ipv4_gateway          = var.vsphere_ddc_ipv4_gateway
											

											
												      timeout               = 60
											

											
												    }
											

											
												  }
											
											 

											
												}
											
											 

											
												### Create DDC2-VM
											

											
												resource "vsphere_virtual_machine" "ddc2-vm" {
											

											
												  # depends_on = [ vsphere_file.CVAD_Upload_Iso ]
											

											
												  name             = var.vsphere_ddc2_vmname
											

											
												  resource_pool_id = data.vsphere_resource_pool.pool.id
											

											
												  datastore_id     = data.vsphere_datastore.datastore.id
											

											
												  num_cpus         = var.vsphere_ddc2_cpus
											

											
												  memory           = var.vsphere_ddc2_ram
											

											
												  guest_id         = data.vsphere_virtual_machine.W2K22_Template.guest_id
											

											
												  scsi_type        = data.vsphere_virtual_machine.W2K22_Template.scsi_type
											

											
												  firmware         = "efi"
											

											
												  
											

											
												  
											

											
												  network_interface {
											

											
												    network_id   = data.vsphere_network.network.id
											

											
												    adapter_type = data.vsphere_virtual_machine.W2K22_Template.network_interface_types[0]
											

											
												  }
											
											 

											
												  disk {
											

											
												    label             = var.vsphere_ddc2_disk_name
											
											 

											
												    ### If you want to use the same disk size and type as hte template, uncomment this block
											

											
												    size             = data.vsphere_virtual_machine.W2K22_Template.disks.0.size
											

											
												    thin_provisioned = data.vsphere_virtual_machine.W2K22_Template.disks.0.thin_provisioned
											

											
												    
											

											
												    ### If you want to override the disk size and disk provisioning type, uncomment this block and set the corresponding variables
											

											
												    #size             = var.vsphere_ddc2_disk_size
											

											
												    #thin_provisioned = var.vsphere_ddc2_disk_provtype
											

											
												  }
											

											
												  cdrom {
											

											
												    datastore_id              = data.vsphere_datastore.datastore.id
											

											
												    path                      = var.vsphere_cvad_iso_destination_path
											

											
												  }
											
											 

											
												  clone {
											

											
												    template_uuid = data.vsphere_virtual_machine.W2K22_Template.id
											
											 

											
												    customize {
											

											
												      
											

											
												      ### If you want to use the provider variables to customize the server, uncomment this block
											

											
												      windows_options {
											

											
												        computer_name               = var.vsphere_ddc2_hostname
											

											
												        join_domain                 = var.vsphere_domainname
											

											
												        admin_password              = var.vsphere_local_admin_pw
											

											
												        domain_admin_user           = var.vsphere_domain_admin_user
											

											
												        domain_admin_password       = var.vsphere_domain_admin_pw
											

											
												        full_name                   = var.vsphere_orgowner
											

											
												        organization_name           = var.vsphere_orgname
											

											
												       # run_once_command_list       = var.vsphere_ddc2_command_list
											

											
												        auto_logon                  = var.vsphere_autologon
											

											
												        auto_logon_count            = var.vsphere_autologon_count
											

											
												      }
											
											 

											
												      ### If you want to use a Sysprep file, uncomment this block
											

											
												      # windows_sysprep_text = file("${path.module}/sysprep.xml")
											
											 

											
												      network_interface {
											

											
												        ipv4_address        = var.vsphere_ddc2_ipv4
											

											
												        ipv4_netmask        = var.vsphere_ddc_ipv4netmask #24
											

											
												        dns_server_list     = var.vsphere_ddc_dns
											

											
												      }
											

											
												      ipv4_gateway          = var.vsphere_ddc_ipv4_gateway
											

											
												      timeout               = 60
											

											
												    }
											

											
												  }
											
											 

											
												}
											
											 

											
												### Create NSVPX1
											

											
												resource "vsphere_virtual_machine" "NSVPX1" {
											

											
												  depends_on = [ vsphere_file.NSVPX1_Upload_Iso ]
											

											
												  name                 = var.vsphere_nsvpx1_vmname
											

											
												  datacenter_id        = data.vsphere_datacenter.datacenter.id
											

											
												  datastore_id         = data.vsphere_datastore.datastore.id
											

											
												  host_system_id       = data.vsphere_host.host.id
											

											
												  resource_pool_id     = data.vsphere_resource_pool.pool.id
											

											
												  #num_cpus             = var.vsphere_nsvpx1_cpus
											

											
												  num_cpus             = data.vsphere_ovf_vm_template.NSVPX_Template.num_cpus
											

											
												  num_cores_per_socket = data.vsphere_ovf_vm_template.NSVPX_Template.num_cores_per_socket
											

											
												  #memory               = var.vsphere_nsvpx1_ram
											

											
												  memory               = data.vsphere_ovf_vm_template.NSVPX_Template.memory
											

											
												  guest_id             = data.vsphere_ovf_vm_template.NSVPX_Template.guest_id
											

											
												  firmware             = "bios"
											

											
												  scsi_type            = data.vsphere_ovf_vm_template.NSVPX_Template.scsi_type
											

											
												  nested_hv_enabled    = data.vsphere_ovf_vm_template.NSVPX_Template.nested_hv_enabled
											

											
												  boot_delay           = 10000
											

											
												  dynamic "network_interface" {
											

											
												    for_each = data.vsphere_ovf_vm_template.NSVPX_Template.ovf_network_map
											

											
												    content {
											

											
												      network_id = network_interface.value
											

											
												    }
											

											
												  }
											

											
												  wait_for_guest_net_timeout = 0
											

											
												  wait_for_guest_ip_timeout  = 0
											
											 

											
												  ovf_deploy {
											

											
												    allow_unverified_ssl_cert = false
											

											
												    local_ovf_path            = data.vsphere_ovf_vm_template.NSVPX_Template.local_ovf_path
											

											
												    disk_provisioning         = data.vsphere_ovf_vm_template.NSVPX_Template.disk_provisioning
											

											
												    ovf_network_map           = data.vsphere_ovf_vm_template.NSVPX_Template.ovf_network_map
											

											
												  }
											
											 

											
												    cdrom {
											

											
												    datastore_id              = data.vsphere_datastore.datastore.id
											

											
												    path                      = var.vsphere_nsvpx1_iso_destination_path
											

											
												  }
											

											
												}
											

											
												 
											

											
												 ### Create NSVPX2
											

											
												resource "vsphere_virtual_machine" "NSVPX2" {
											

											
												  depends_on = [ vsphere_file.NSVPX2_Upload_Iso ]
											

											
												  name                 = var.vsphere_nsvpx2_vmname
											

											
												  datacenter_id        = data.vsphere_datacenter.datacenter.id
											

											
												  datastore_id         = data.vsphere_datastore.datastore.id
											

											
												  host_system_id       = data.vsphere_host.host.id
											

											
												  resource_pool_id     = data.vsphere_resource_pool.pool.id
											

											
												  #num_cpus             = var.vsphere_nsvpx2_cpus
											

											
												  num_cpus             = data.vsphere_ovf_vm_template.NSVPX_Template.num_cpus
											

											
												  num_cores_per_socket = data.vsphere_ovf_vm_template.NSVPX_Template.num_cores_per_socket
											

											
												  #memory               = var.vsphere_nsvpx2_ram
											

											
												  memory               = data.vsphere_ovf_vm_template.NSVPX_Template.memory
											

											
												  guest_id             = data.vsphere_ovf_vm_template.NSVPX_Template.guest_id
											

											
												  firmware             = "bios"
											

											
												  scsi_type            = data.vsphere_ovf_vm_template.NSVPX_Template.scsi_type
											

											
												  nested_hv_enabled    = data.vsphere_ovf_vm_template.NSVPX_Template.nested_hv_enabled
											

											
												  boot_delay           = 10000
											

											
												  dynamic "network_interface" {
											

											
												    for_each = data.vsphere_ovf_vm_template.NSVPX_Template.ovf_network_map
											

											
												    content {
											

											
												      network_id = network_interface.value
											

											
												    }
											

											
												  }
											

											
												  wait_for_guest_net_timeout = 0
											

											
												  wait_for_guest_ip_timeout  = 0
											
											 

											
												  ovf_deploy {
											

											
												    allow_unverified_ssl_cert = false
											

											
												    local_ovf_path            = data.vsphere_ovf_vm_template.NSVPX_Template.local_ovf_path
											

											
												    disk_provisioning         = data.vsphere_ovf_vm_template.NSVPX_Template.disk_provisioning
											

											
												    ovf_network_map           = data.vsphere_ovf_vm_template.NSVPX_Template.ovf_network_map
											

											
												  }
											
											 

											
												    cdrom {
											

											
												    datastore_id              = data.vsphere_datastore.datastore.id
											

											
												    path                      = var.vsphere_nsvpx2_iso_destination_path
											

											
												  }
											

											
												}
											
											 
											 
										
									
								
							
						
					
				

				
					 
				 

				
					
						Module 2
					

					
						CVADOnvSphere-InstallCVADandSF.tf
					 

					
						
						
						
							
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}							
							
								
									
										
											
												
													
														# Terraform deployment of Citrix Virtual Apps and Desktops on VMware vSphere
													

													
														## Install CVAD and SF with all chosen components and create a new site
													

													
														locals {
													

													
														  installPath       = join("/",[var.CVAD_Install_Source-Drive,var.CVAD_Install_Source-Path])
													

													
														  installSwitches   = join(" ",[var.CVAD_Install_Features-To-Install, var.CVAD_Install_CLI-Switch_NoReboot, var.CVAD_Install_CLI-Switch_ConfigureFirewall, var.CVAD_Install_CLI-Switch_QuietInstall, "/nosql","/Logpath", var.CVAD_Install_LogPath])
													

													
														  completeInstall   = join(" ",[local.installPath,local.installSwitches]) 
													

													
														  SFCompleteInstall = join("",[var.CVAD_Install_Source-Drive,var.SF_Install_Source-Path])
													
													 

													
														  #### Create CVAD-Installation script
													

													
														  CVADScriptcontent     = &lt;&lt;-EOT
													

													
														  New-Item -Path '${var.CVAD_Install_LogPath}' -ItemType Directory
													

													
														  ${local.completeInstall}
													

													
														  #Restart-Computer -Force
													

													
														  EOT
													
													 

													
														  #### Create SF-Installation script
													

													
														  SFScriptcontent     = &lt;&lt;-EOT
													

													
														  ${local.SFCompleteInstall}
													

													
														    EOT
													
													 

													
														  #### Create Reboot script
													

													
														  RebootScriptcontent     = &lt;&lt;-EOT
													

													
														  Restart-Computer -Force
													

													
														  EOT
													
													 

													
														  #### Create CVAD-Database-and-Site-Creation script
													

													
														  #### Need to invoke PowerShell as Domain User as the provisioner does not allow to be run in a Domain Users-context, but CVAD SDK needs to be run as a Domain User
													

													
														  CVADDBScriptcontent     = &lt;&lt;-EOT
													

													
														  $PSUsername = '${var.vsphere_domain_admin_userwithdomain}'
													

													
														  $PSPassword = '${var.vsphere_domain_admin_pw}'
													

													
														  $PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
													

													
														  $PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
													

													
														  Invoke-Command -ComputerName localhost -Credential $PSCredential -ScriptBlock {
													

													
														  Add-PSSnapin Citrix.*
													

													
														  $PSUsername = '${var.vsphere_domain_admin_userwithdomain}'
													

													
														  $PSPassword = '${var.vsphere_domain_admin_pw}'
													

													
														  $PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
													

													
														  $PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
													

													
														  New-XDDatabase -SiteName ${var.CVAD-SiteName} -AllDefaultDatabases -DatabaseServer ${var.CVAD-DB-ServerName} -DatabaseCredentials $PSCredential
													

													
														  
													

													
														  New-XDSite -AdminAddress ${var.CVAD-DDC1-Name} -AllDefaultDatabases -SiteName ${var.CVAD-SiteName} -DatabaseServer ${var.CVAD-DB-ServerName}
													

													
														  #Restart-Computer -Force
													

													
														  #Add-XDController -AdminAddress ${var.CVAD-DDC2-Name} -SiteControllerAddress ${var.CVAD-DDC1-Name}
													

													
														  }
													

													
														  EOT 
													
													 

													
														  #### Create Storefront-Configuration script
													

													
														  #### Need to invoke PowerShell as Domain User as the provisioner does not allow to be run in a Domain Users-context, but CVAD SDK needs to be run as a Domain User
													

													
														  ConfigureStorefrontScriptcontent     = &lt;&lt;-EOT
													

													
														  #$PSUsername = 'XXXXX\XXXXXXXXXX'
													

													
														#$PSPassword = 'XXXXXXXXXX'
													

													
														#$PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
													

													
														#$PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
													

													
														#Invoke-Command -ComputerName localhost -Credential $PSCredential -ScriptBlock {
													

													
														Add-PSSnapin Citrix.*
													
													 

													
														#Clear-Existing StoreFront deployments
													

													
														Clear-STFDeployment -Confirm:$false
													

													
														Write-Output 'Existing Deployments deleted.'
													
													 

													
														#Add new StoreFront deployment
													

													
														Add-STFDeployment -HostBaseUrl 'https://storefront.the-austrian-citrix-guy.at' -Confirm:$false
													

													
														Write-Output 'New deployment created.'
													
													 

													
														#Add new Authentication Service
													

													
														Add-STFAuthenticationService '/Citrix/Authentication'
													

													
														$authentication = Get-STFAuthenticationService
													

													
														Write-Output 'Authentication service created.'
													
													 

													
														Start-Sleep -Seconds 120
													
													 

													
														#Enable Access through a NetScaler Gateway
													

													
														Enable-STFAuthenticationServiceProtocol -Name CitrixAGBasic -AuthenticationService ($authentication)
													

													
														$protocols = Get-STFAuthenticationServiceProtocol -AuthenticationService ($authentication)
													
													 

													
														#Add NetScaler Gateway
													

													
														Add-STFRoamingGateway -Name TACG-TF-NSGW -LogonType Domain -GatewayUrl 'https://ns.the-austrian-citrix-guy.at' -CallbackUrl 'https://ns.the-austrian-citrix-guy.at/CitrixAuthService/AuthService.asmx' -SessionReliability:$true -SecureTicketAuthorityUrls 'http://TACG-TF-CVDDDC1.the-austrian-citrix-guy.at/scripts/ctxsta.dll,http://TACG-TF-CVDDDC2.the-austrian-citrix-guy.at/scripts/ctxsta.dll' -IsCloudGateway:$false
													

													
														Write-Output 'NSGW created.'
													
													 

													
														#Add Beacons for NetScaler Gateway
													

													
														Set-STFRoamingBeacon -Internal 'http://TACG-TF-CVDDDC1.the-austrian-citrix-guy.at' -External https://ns.the-austrian-citrix-guy.at,https://news.orf.at
													

													
														Write-Output 'Beacons created.'
													
													 

													
														#Add new Store Service
													

													
														Add-STFStoreService -VirtualPath '/Citrix/Store' -AuthenticationService $authentication -FarmName 'TACG-SF' -FarmType 'XenDesktop' -Servers 'TACG-TF-CVDDDC1.the-austrian-citrix-guy.at' -FriendlyName 'TACG-SF' -TransportType HTTPS -Port 443
													

													
														Write-Output 'Store service created.'
													
													 

													
														#Register NetScaler Gateway and Beacons
													

													
														$store = Get-STFStoreService -VirtualPath '/Citrix/Store'
													

													
														$gateway = Get-STFRoamingGateway -Name TACG-TF-NSGW
													

													
														Register-STFStoreGateway -Gateway $gateway -StoreService $store -DefaultGateway -UseFullVpn:$false
													

													
														Write-Output 'NSGW registered.'
													
													 

													
														#Enable WebReceiver
													

													
														Add-STFWebReceiverService -VirtualPath '/Citrix/StoreWeb' -StoreService $store
													

													
														$receiver = Get-STFWebReceiverService -VirtualPath '/Citrix/StoreWeb'
													

													
														Write-Output 'Web Receiver site created.'
													
													 

													
														#Enable PNAGent site
													

													
														Enable-STFStorePna -StoreService $store -AllowUserPasswordChange -DefaultPnaService
													

													
														Write-Output 'PNA site created.'
													
													 

													
														#Enable HTML5Fallback
													

													
														Set-STFWebReceiverPluginAssistant -WebReceiverService $receiver -Enabled $true -Html5Enabled "Fallback"
													

													
														Write-Output 'HTML5 site created.'
													

													
														Write-Output 'Storefront configuration complete.'
													

													
														Write-Output 'Registering Storefront in CVAD Site configuration.'
													
													 

													
														$PSUsername = 'XXXXX\XXXXXXXXXX'
													

													
														$PSPassword = 'XXXXXXXXXX'
													

													
														$PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
													

													
														$PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
													

													
														Invoke-Command -ComputerName localhost -Credential $PSCredential -ScriptBlock {
													

													
														Add-PSSnapin Citrix.*
													
													 

													
														#Add StoreFront configuration to CVAD site
													

													
														$configurationSlot = Get-BrokerConfigurationSlot -Name "RS"
													

													
														$storefrontUrl = 'https://storefront.the-austrian-citrix-guy.at/Citrix/StoreWeb'
													

													
														$configuration = New-BrokerStorefrontAddress -Description "Citrix Storefront Address" -Enabled $true -Name "Citrix StoreFront" -Url $storefrontUrl
													

													
														New-BrokerMachineConfiguration -Policy $configuration -ConfigurationSlotUid $configurationSlot.Uid -Description "Citrix Storefront Address Configuration" -LeafName 'TACG-SF'
													

													
														Write-Output 'Registration in CVAD Site complete.'
													

													
														Write-Output 'Reboot needed...'
													

													
														}
													

													
														  EOT
													
													 

													
														#### Create script to schedule a Task on DDC1 to enable replication as invoking PowerShell is not supported for this cmdlet
													

													
														CreateStoreFrontReplicationTaskonDDC1Scriptcontent     = &lt;&lt;-EOT
													

													
														 $time = (Get-Date).addMinutes(2)
													

													
														 $trigger = New-JobTrigger -Once -At $time
													

													
														 $JobName= -join('TACG-TF-StartSFReplication-' ,(Get-Date -Format "ddmmyyHHmmss"))
													

													
														 $PSUsername = '${var.vsphere_domain_admin_userwithdomain}'
													

													
														 $PSPassword = '${var.vsphere_domain_admin_pw}'
													

													
														 $PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
													

													
														 $PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
													

													
														 Register-ScheduledJob -file '${var.CVAD_Install_LogPath}/SF-CMD-EnableReplication.ps1' -Trigger $trigger -Credential $PSCredential -Name $JobName
													

													
														 Write-Output 'Replication-Job scheduled.'
													

													
														  }
													

													
														  EOT
													
													 

													
														#### Create script to schedule a Task on DDC2 to enable replication as invoking PowerShell is not supported for this cmdlet
													

													
														CreateStoreFrontReplicationTaskonDDC2Scriptcontent     = &lt;&lt;-EOT
													

													
														 $time = (Get-Date).addMinutes(2)
													

													
														 $trigger = New-JobTrigger -Once -At $time
													

													
														 $JobName= -join('TACG-TF-StartSFReplication-' ,(Get-Date -Format "ddmmyyHHmmss"))
													

													
														 $PSUsername = '${var.vsphere_domain_admin_userwithdomain}'
													

													
														 $PSPassword = '${var.vsphere_domain_admin_pw}'
													

													
														 $PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
													

													
														 $PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
													

													
														 Register-ScheduledJob -file '${var.CVAD_Install_LogPath}/SF-CMD-StartReplication.ps1' -Trigger $trigger -Credential $PSCredential -Name $JobName
													

													
														 Write-Output 'Replication-Job scheduled.'
													

													
														  }
													

													
														  EOT
													
													 

													
														#### Create script being started by DDC1 SF-Replication task
													

													
														CreateStoreFrontEnableReplicationTaskonDDC1Scriptcontent     = &lt;&lt;-EOT
													

													
														Add-PSSnapin Citrix.Storefront.*
													

													
														Start-Service -Name CitrixClusterService
													

													
														$JoinObject = Start-STFServerGroupJoin -IsAuthorizingServer -Confirm:$false
													

													
														$JoinPassCode = ($JoinObject | Select -ExpandProperty 'Passcode')
													

													
														$JoinPassCode | Out-File -Force -FilePath '${var.CVAD_Install_LogPath}/pc.txt' 
													

													
														Copy-Item -Path ${var.CVAD_Install_LogPath}/pc.txt -Destination ${var.CVAD-SMB-DDC2}
													

													
														Write-Output 'Replication enabled.'
													

													
														  }
													

													
														  EOT
													
													 

													
														#### Create script being started by DDC2 SF-Replication task
													

													
														CreateStoreFrontStartReplicationTaskonDDC2Scriptcontent     = &lt;&lt;-EOT
													

													
														Add-PSSnapin Citrix.Storefront.*
													

													
														Start-Service -Name CitrixClusterService
													

													
														$Passcode = Get-Content -Path '${var.CVAD_Install_LogPath}/pc.txt'
													

													
														Start-STFServerGroupJoin -AuthorizerHostName ${var.CVAD-DDC1-Name}.the-austrian-citrix-guy.at -Passcode $Passcode -Confirm:$false
													

													
														Wait-STFServerGroupJoin -WaitTimeout 20
													

													
														Write-Output 'Replication complete.'
													

													
														  }
													

													
														  EOT
													
													 

													
														#### Create the Add-a-DNS-Host-Entry script
													

													
														#### Need to invoke PowerShell as Domain User as the provisioner does not allow to be run in a Domain Users-context, but CVAD SDK needs to be run as a Domain User
													

													
														CreateDNSARecordScriptcontent     = &lt;&lt;-EOT
													

													
														$PSUsername = '${var.vsphere_domain_admin_userwithdomain}'
													

													
														$PSPassword = '${var.vsphere_domain_admin_pw}'
													

													
														$PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
													

													
														$PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
													

													
														Invoke-Command -ComputerName localhost -Credential $PSCredential -ScriptBlock {
													

													
														#Create DNS-A-Record for SF-HostBaseURL-Loadbalancing-IP
													

													
														Add-DnsServerResourceRecordA -Name '${var.CVAD-DDC1-Name}' -ZoneName 'the-austrian-citrix-guy.at' -IPv4Address ${var.CVAD-SF-FarmName-LBIP}
													

													
														} 
													

													
														EOT
													
													 

													
														}
													
													 

													
														#### Write CVAD Installation script into local data-directory
													

													
														resource "local_file" "WriteCVADScriptIntoDataDirectory" {
													

													
														  #depends_on = [ vsphere_virtual_machine.ddc1-vm, vsphere_virtual_machine.ddc2-vm ]
													

													
														  filename = "${path.module}/data/CVAD-Install.ps1"
													

													
														  content  = local.CVADScriptcontent
													

													
														}
													
													 

													
														#### Write StoreFront Installation script into local data-directory
													

													
														resource "local_file" "WriteSFScriptIntoDataDirectory" {
													

													
														  #depends_on = [ vsphere_virtual_machine.ddc1-vm, vsphere_virtual_machine.ddc2-vm ]
													

													
														  filename = "${path.module}/data/SF-Install.ps1"
													

													
														  content  = local.SFScriptcontent
													

													
														}
													
													 

													
														#### Write StoreFront configuration script into local data-directory
													

													
														resource "local_file" "StoreFrontConfigurationScriptIntoDataDirectory" {
													

													
														  #depends_on = [ vsphere_virtual_machine.ddc1-vm, vsphere_virtual_machine.ddc2-vm ]
													

													
														  filename = "${path.module}/data/SF-Config.ps1"
													

													
														  content  = local.ConfigureStorefrontScriptcontent
													

													
														}
													
													 

													
														#### Write Reboot script into local data-directory
													

													
														resource "local_file" "WriteRebootScriptIntoDataDirectory" {
													

													
														  #depends_on = [ vsphere_virtual_machine.ddc1-vm, vsphere_virtual_machine.ddc2-vm ]
													

													
														  filename = "${path.module}/data/Reboot.ps1"
													

													
														  content  = local.RebootScriptcontent
													

													
														}
													
													 

													
														#### Write the Add-a-DNS-Host-Entry script into local data-directory
													

													
														resource "local_file" "WriteLicScriptIntoDataDirectory" {
													

													
														  filename = "${path.module}/data/CVAD-CreateDNSARecordOnDC.ps1"
													

													
														  content  = local.CreateDNSARecordScriptcontent
													

													
														}
													
													 

													
														#### Write Database script into local data-directory
													

													
														resource "local_file" "WriteCVADDBScriptIntoDataDirectory" {
													

													
														  #depends_on = [ vsphere_virtual_machine.ddc1-vm, vsphere_virtual_machine.ddc2-vm ]
													

													
														  filename = "${path.module}/data/CVAD-DBs.ps1"
													

													
														  content  = local.CVADDBScriptcontent
													

													
														}
													
													 

													
														#### Write DDC1-Task-Creation script into local data-directory
													

													
														resource "local_file" "WriteDDC1SFTaskScriptIntoDataDirectory" {
													

													
														  #depends_on = [ vsphere_virtual_machine.ddc1-vm, vsphere_virtual_machine.ddc2-vm ]
													

													
														  filename = "${path.module}/data/SF-Task-StartReplication-DDC1.ps1"
													

													
														  content  = local.CreateStoreFrontReplicationTaskonDDC1Scriptcontent
													

													
														}
													
													 

													
														#### Write DDC2-Task-Creation script into local data-directory
													

													
														resource "local_file" "WriteDDC2SFTaskScriptIntoDataDirectory" {
													

													
														  #depends_on = [ vsphere_virtual_machine.ddc1-vm, vsphere_virtual_machine.ddc2-vm ]
													

													
														  filename = "${path.module}/data/SF-Task-StartReplication-DDC2.ps1"
													

													
														  content  = local.CreateStoreFrontReplicationTaskonDDC2Scriptcontent
													

													
														}
													
													 

													
														#### Write DDC1-Task-Run script script into local data-directory
													

													
														resource "local_file" "WriteDDC1SFTaskRunScriptIntoDataDirectory" {
													

													
														  #depends_on = [ vsphere_virtual_machine.ddc1-vm, vsphere_virtual_machine.ddc2-vm ]
													

													
														  filename = "${path.module}/data/SF-CMD-EnableReplication.ps1"
													

													
														  content  = local.CreateStoreFrontEnableReplicationTaskonDDC1Scriptcontent
													

													
														}
													
													 

													
														#### Write DDC2-Task-Run script into local data-directory
													

													
														resource "local_file" "WriteDDC2SFTaskRunScriptIntoDataDirectory" {
													

													
														  #depends_on = [ vsphere_virtual_machine.ddc1-vm, vsphere_virtual_machine.ddc2-vm ]
													

													
														  filename = "${path.module}/data/SF-CMD-StartReplication.ps1"
													

													
														  content  = local.CreateStoreFrontStartReplicationTaskonDDC2Scriptcontent
													

													
														}
													
													 

													
														##### Upload CVAD-Installation scripts to DDC1 and execute it
													

													
														###### Set the Provisioner-Connection
													

													
														resource "null_resource" "InstallCVADFromCDROMOnDDC1" {
													

													
														  depends_on = [ local_file.WriteCVADScriptIntoDataDirectory ]
													

													
														  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 CVAD-Installation script to DDC1
													

													
														  provisioner "file" {
													

													
														    source      = "${path.module}/data/CVAD-Install.ps1"
													

													
														    destination = "${var.CVAD_Install_LogPath}/CVAD-Install.ps1"
													

													
														    
													

													
														  }
													
													 

													
														###### Execute the CVAD Installation script
													

													
														 provisioner "remote-exec" {
													

													
														    inline = [
													

													
														      "powershell -File ${var.CVAD_Install_LogPath}/CVAD-Install.ps1"
													

													
														    ]
													

													
														  }
													

													
														}
													
													 

													
														##### Upload CVAD Installation script to DDC2 and execute it
													

													
														###### Set the Provisioner-Connection
													

													
														resource "null_resource" "InstallCVADFromCDROMOnDDC2" {
													

													
														  depends_on = [ local_file.WriteCVADScriptIntoDataDirectory ]
													

													
														  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 CVAD-Installation script to DDC2
													

													
														  provisioner "file" {
													

													
														    source      = "${path.module}/data/CVAD-Install.ps1"
													

													
														    destination = "${var.CVAD_Install_LogPath}/CVAD-Install.ps1"
													

													
														    
													

													
														  }
													
													 

													
														###### Execute the CVAD Installation script
													

													
														  provisioner "remote-exec" {
													

													
														    inline = [
													

													
														      "powershell -File ${var.CVAD_Install_LogPath}/CVAD-Install.ps1"
													

													
														    ]
													

													
														  }
													

													
														} 
													
													 

													
														### Wait for 2 minutes to complete reboot and settling of all tasks on DDC-VMs
													

													
														resource "time_sleep" "WaitToRebootDDC1AfterCVADInstall" {
													

													
														  depends_on = [ null_resource.InstallCVADFromCDROMOnDDC1 ]
													

													
														  create_duration = "600s"
													

													
														}
													
													 

													
														resource "time_sleep" "WaitToRebootDDC2AfterCVADInstall" {
													

													
														  depends_on = [ null_resource.InstallCVADFromCDROMOnDDC2 ]
													

													
														  create_duration = "600s"
													

													
														} 
													
													 

													
														##### Reboot DDC1 
													

													
														###### Set the Provisioner-Connection
													

													
														resource "null_resource" "RebootDDC1" {
													

													
														  depends_on = [ time_sleep.WaitToRebootDDC1AfterCVADInstall ]
													
													 

													
														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 Reboot script to DDC1
													

													
														  provisioner "file" {
													

													
														    source      = "${path.module}/data/Reboot.ps1"
													

													
														    destination = "${var.CVAD_Install_LogPath}/Reboot.ps1"
													

													
														    
													

													
														  }
													
													 

													
														###### Execute the Reboot script
													

													
														  provisioner "remote-exec" {
													

													
														    inline = [
													

													
														      "powershell -File ${var.CVAD_Install_LogPath}/Reboot.ps1"
													

													
														    ]
													

													
														  }
													

													
														}
													
													 

													
														##### Reboot DDC2 
													

													
														###### Set the Provisioner-Connection
													

													
														resource "null_resource" "RebootDDC2" {
													

													
														  depends_on = [ time_sleep.WaitToRebootDDC2AfterCVADInstall ]
													
													 

													
														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 Reboot script to DDC2
													

													
														  provisioner "file" {
													

													
														    source      = "${path.module}/data/Reboot.ps1"
													

													
														    destination = "${var.CVAD_Install_LogPath}/Reboot.ps1"
													

													
														    
													

													
														  }
													
													 

													
														###### Execute the Reboot script
													

													
														  provisioner "remote-exec" {
													

													
														    inline = [
													

													
														      "powershell -File ${var.CVAD_Install_LogPath}/Reboot.ps1"
													

													
														    ]
													

													
														  }
													

													
														}
													
													 

													
														### Wait for 2 minutes to complete reboot and settling of all tasks on DDC-VMs
													

													
														resource "time_sleep" "WaitForRebootofDDC1" {
													

													
														  depends_on = [ null_resource.RebootDDC1 ]
													

													
														  create_duration = "120s"
													

													
														}
													
													 

													
														resource "time_sleep" "WaitForRebootofDDC2" {
													

													
														  depends_on = [ null_resource.RebootDDC2 ]
													

													
														  create_duration = "120s"
													

													
														} 
													
													 

													
														### Create the databases for CVAD  
													

													
														#### Upload DB-Creation script to DDC1 and execute it
													

													
														###### Set the Provisioner-Connection
													

													
														resource "null_resource" "CreateCVADDBs" {
													

													
														 depends_on = [ time_sleep.WaitForRebootofDDC1 ]
													

													
														 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 DB-Creation script to DDC1
													

													
														  provisioner "file" {
													

													
														    source      = "${path.module}/data/CVAD-DBs.ps1"
													

													
														    destination = "${var.CVAD_Install_LogPath}/CVAD-DBs.ps1"
													

													
														    
													

													
														  }
													
													 

													
														###### Execute the DB-Creation script to DDC1
													

													
														 provisioner "remote-exec" {
													

													
														    inline = [
													

													
														      "powershell -File ${var.CVAD_Install_LogPath}/CVAD-DBs.ps1"
													

													
														    ]
													

													
														  }
													

													
														}  
													
													 

													
														### Install Storefront on DDC1  
													

													
														#### Upload SF-Installation script to DDC1 and execute it
													

													
														###### Set the Provisioner-Connection
													

													
														resource "null_resource" "InstallSFOnDDC1" {
													

													
														 #depends_on = [ null_resource.CreateCVADDBs ]
													

													
														 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 SF-Installation and SF-Configuration scripts to DDC1
													

													
														  provisioner "file" {
													

													
														    source      = "${path.module}/data/SF-Install.ps1"
													

													
														    destination = "${var.CVAD_Install_LogPath}/SF-Install.ps1"
													

													
														    
													

													
														  }
													
													 

													
														  provisioner "file" {
													

													
														    source      = "${path.module}/data/SF-Config.ps1"
													

													
														    destination = "${var.CVAD_Install_LogPath}/SF-Config.ps1"
													

													
														    
													

													
														  }
													
													 

													
														###### Execute the SF-Installation script on DDC1
													

													
														 provisioner "remote-exec" {
													

													
														    inline = [
													

													
														      "powershell -File ${var.CVAD_Install_LogPath}/SF-Install.ps1"
													

													
														    ]
													

													
														  }
													

													
														} 
													

													
														### Install Storefront on DDC2  
													

													
														#### Upload SF-Installation script to DDC2 and execute it
													

													
														###### Set the Provisioner-Connection
													

													
														resource "null_resource" "InstallSFOnDDC2" {
													

													
														 #depends_on = [ null_resource.CreateCVADDBs ]
													

													
														 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 SF-Installation and SF-Configuration scripts to DDC2
													

													
														  provisioner "file" {
													

													
														    source      = "${path.module}/data/SF-Install.ps1"
													

													
														    destination = "${var.CVAD_Install_LogPath}/SF-Install.ps1"
													

													
														    
													

													
														  }
													
													 

													
														  provisioner "file" {
													

													
														    source      = "${path.module}/data/SF-Config.ps1"
													

													
														    destination = "${var.CVAD_Install_LogPath}/SF-Config.ps1"
													

													
														    
													

													
														  }
													
													 

													
														###### Execute the SF-Installation script on DDC2
													

													
														 provisioner "remote-exec" {
													

													
														    inline = [
													

													
														      "powershell -File ${var.CVAD_Install_LogPath}/SF-Install.ps1"
													

													
														    ]
													

													
														  }
													

													
														} 
													
													 

													
														### Reboot DDC1 and DDC2 after Storefront-Installation   
													

													
														##### Set the Provisioner-Connection
													

													
														resource "null_resource" "RebootDDC1AfterStoreFrontInstall" {
													

													
														 depends_on = [ null_resource.InstallSFOnDDC1 ]
													

													
														 connection {
													

													
														    type            = var.Provisioner_Type
													

													
														    user            = var.Provisioner_Admin-Username
													

													
														    password        = var.Provisioner_Admin-Password
													

													
														    host            = var.Provisioner_DDC1-IP
													

													
														    timeout         = var.Provisioner_Timeout
													
													 

													
														  }
													
													 

													
														###### Execute the Reboot script on DDC1
													

													
														 provisioner "remote-exec" {
													

													
														    inline = [
													

													
														      "powershell -File ${var.CVAD_Install_LogPath}/Reboot.ps1"
													

													
														    ]
													

													
														  }
													

													
														} 
													
													 

													
														##### Set the Provisioner-Connection
													

													
														resource "null_resource" "RebootDDC2AfterStoreFrontInstall" {
													

													
														 depends_on = [ null_resource.InstallSFOnDDC2 ]
													

													
														 connection {
													

													
														    type            = var.Provisioner_Type
													

													
														    user            = var.Provisioner_Admin-Username
													

													
														    password        = var.Provisioner_Admin-Password
													

													
														    host            = var.Provisioner_DDC2-IP
													

													
														    timeout         = var.Provisioner_Timeout
													
													 

													
														  }
													
													 

													
														##### Execute the Reboot script on DDC2
													

													
														 provisioner "remote-exec" {
													

													
														    inline = [
													

													
														      "powershell -File ${var.CVAD_Install_LogPath}/Reboot.ps1"
													

													
														    ]
													

													
														  }
													

													
														} 
													
													 

													
														### Wait for 2 minutes to complete reboot and settling of all tasks on DDC-VMs
													

													
														resource "time_sleep" "WaitForRebootofDDC1AfterStoreFrontInstall" {
													

													
														  depends_on = [ null_resource.RebootDDC1AfterStoreFrontInstall ]
													

													
														  create_duration = "120s"
													

													
														}
													
													 

													
														resource "time_sleep" "WaitForRebootofDDC2AfterStorefrontInstall" {
													

													
														  depends_on = [ null_resource.RebootDDC2AfterStoreFrontInstall ]
													

													
														  create_duration = "120s"
													

													
														} 
													
													 

													
														### Configure Storefront on DDC1  
													

													
														#### Set the Provisioner-Connection
													

													
														resource "null_resource" "ConfigureSFOnDDC1" {
													

													
														 depends_on = [ time_sleep.WaitForRebootofDDC1AfterStoreFrontInstall ]
													

													
														 connection {
													

													
														    type            = var.Provisioner_Type
													

													
														    user            = var.Provisioner_Admin-Username
													

													
														    password        = var.Provisioner_Admin-Password
													

													
														    host            = var.Provisioner_DDC1-IP
													

													
														    timeout         = var.Provisioner_Timeout
													
													 

													
														  }
													
													 

													
														##### Execute the SF-Configuration script on DDC1
													

													
														 provisioner "remote-exec" {
													

													
														    inline = [
													

													
														      "powershell -File ${var.CVAD_Install_LogPath}/SF-Config.ps1"
													

													
														    ]
													

													
														  }
													

													
														} 
													
													 

													
														### Reboot DDC1 after Storefront-Configuration   
													

													
														#### Set the Provisioner-Connection
													

													
														resource "null_resource" "RebootDDC1AfterStoreFrontConfiguration" {
													

													
														 depends_on = [ null_resource.ConfigureSFOnDDC1 ]
													

													
														 connection {
													

													
														    type            = var.Provisioner_Type
													

													
														    user            = var.Provisioner_Admin-Username
													

													
														    password        = var.Provisioner_Admin-Password
													

													
														    host            = var.Provisioner_DDC1-IP
													

													
														    timeout         = var.Provisioner_Timeout
													
													 

													
														  }
													
													 

													
														##### Execute the Reboot script on DDC1
													

													
														 provisioner "remote-exec" {
													

													
														    inline = [
													

													
														      "powershell -File ${var.CVAD_Install_LogPath}/Reboot.ps1"
													

													
														    ]
													

													
														  }
													

													
														}
													
													 

													
														### Wait for 2 minutes to complete reboot and settling of all tasks on DDC-VMs
													

													
														resource "time_sleep" "WaitForRebootofDDC1AfterStoreFrontConfiguration" {
													

													
														  depends_on = [ null_resource.RebootDDC1AfterStoreFrontConfiguration ]
													

													
														  create_duration = "120s"
													

													
														}
													
													 

													
														########### This completes the Storefront configuration. 
													

													
														########### We need to start the replication of the Storefront CLuster. 
													
													 

													
														/* ##### Upload Task-Creation and Task-Run scripts for enabling replication to DDC1 and execute them
													

													
														###### Set the Provisioner-Connection
													

													
														resource "null_resource" "SFTasksOnDDC1" {
													

													
														  depends_on = [ local_file.WriteCVADScriptIntoDataDirectory ]
													

													
														  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 Task-Creation script to DDC1
													

													
														  provisioner "file" {
													

													
														    source      = "${path.module}/data/SF-Task-StartReplication-DDC1.ps1"
													

													
														    destination = "${var.CVAD_Install_LogPath}/data/SF-Task-StartReplication-DDC1.ps1"
													

													
														    
													

													
														  }
													
													 

													
														###### Upload Task-Run script to DDC1
													

													
														  provisioner "file" {
													

													
														    source      = "${path.module}/data/SF-CMD-EnableReplication.ps1"
													

													
														    destination = "${var.CVAD_Install_LogPath}/data/SF-CMD-EnableReplication.ps1"
													

													
														    
													

													
														  }
													
													 

													
														###### Execute the Task-Creation script
													

													
														 provisioner "remote-exec" {
													

													
														    inline = [
													

													
														      "powershell -File ${var.CVAD_Install_LogPath}/SF-Task-StartReplication-DDC1.ps1"
													

													
														    ]
													

													
														  }
													
													 

													
														###### Execute the Task-Run script
													

													
														 provisioner "remote-exec" {
													

													
														    inline = [
													

													
														      "powershell -File ${var.CVAD_Install_LogPath}/SF-CMD-EnableReplication.ps1"
													

													
														    ]
													

													
														  }
													
													 

													
														}
													
													 

													
														##### Upload Task-Creation and Task-Run scripts for enabling replication to DDC2 and execute them
													

													
														###### Set the Provisioner-Connection
													

													
														resource "null_resource" "SFTasksOnDDC2" {
													

													
														  depends_on = [ local_file.WriteCVADScriptIntoDataDirectory ]
													

													
														  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 Task-Creation script to DDC2
													

													
														  provisioner "file" {
													

													
														    source      = "${path.module}/data/SF-Task-StartReplication-DDC2.ps1"
													

													
														    destination = "${var.CVAD_Install_LogPath}/data/SF-Task-StartReplication-DDC2.ps1"
													

													
														    
													

													
														  }
													
													 

													
														###### Upload Task-Run script to DDC1
													

													
														  provisioner "file" {
													

													
														    source      = "${path.module}/data/SF-CMD-StartReplication.ps1"
													

													
														    destination = "${var.CVAD_Install_LogPath}/data/SF-CMD-StartReplication.ps1"
													

													
														    
													

													
														  }
													
													 

													
														###### Execute the Task-Creation script
													

													
														 provisioner "remote-exec" {
													

													
														    inline = [
													

													
														      "powershell -File ${var.CVAD_Install_LogPath}/SF-Task-StartReplication-DDC2.ps1"
													

													
														    ]
													

													
														  }
													
													 

													
														###### Execute the Task-Run script
													

													
														 provisioner "remote-exec" {
													

													
														    inline = [
													

													
														      "powershell -File ${var.CVAD_Install_LogPath}/SF-CMD-StartReplication.ps1"
													

													
														    ]
													

													
														  }
													
													 

													
														}
													
													 

													
														### Reboot DDC1 and DDC2 after Storefront-Replication   
													

													
														##### Set the Provisioner-Connection
													

													
														resource "null_resource" "RebootDDC1AfterStoreFrontReplication" {
													

													
														 depends_on = [ null_resource.SFTasksOnDDC1 ]
													

													
														 connection {
													

													
														    type            = var.Provisioner_Type
													

													
														    user            = var.Provisioner_Admin-Username
													

													
														    password        = var.Provisioner_Admin-Password
													

													
														    host            = var.Provisioner_DDC1-IP
													

													
														    timeout         = var.Provisioner_Timeout
													
													 

													
														  }
													
													 

													
														###### Execute the Reboot script on DDC1
													

													
														 provisioner "remote-exec" {
													

													
														    inline = [
													

													
														      "powershell -File ${var.CVAD_Install_LogPath}/Reboot.ps1"
													

													
														    ]
													

													
														  }
													

													
														} 
													
													 

													
														### Reboot DDC1 and DDC2 after Storefront-Installation   
													

													
														##### Set the Provisioner-Connection
													

													
														resource "null_resource" "RebootDDC2AfterStoreFrontReplication" {
													

													
														 depends_on = [ null_resource.SFTasksOnDDC2 ]
													

													
														 connection {
													

													
														    type            = var.Provisioner_Type
													

													
														    user            = var.Provisioner_Admin-Username
													

													
														    password        = var.Provisioner_Admin-Password
													

													
														    host            = var.Provisioner_DDC2-IP
													

													
														    timeout         = var.Provisioner_Timeout
													
													 

													
														  }
													
													 

													
														###### Execute the Reboot script on DDC1
													

													
														 provisioner "remote-exec" {
													

													
														    inline = [
													

													
														      "powershell -File ${var.CVAD_Install_LogPath}/Reboot.ps1"
													

													
														    ]
													

													
														  }
													

													
														} 
													
													 

													
														### Upload and execute the Add-a-DNS-Host-Entry script on DC 
													

													
														resource "null_resource" "CreateDNSRecord" {
													

													
														connection {
													

													
														    type            = var.Provisioner_Type
													

													
														    user            = var.Provisioner_Admin-Username
													

													
														    password        = var.Provisioner_Admin-Password
													

													
														    host            = var.Provisioner_DC-IP
													

													
														    timeout         = var.Provisioner_Timeout
													
													 

													
														  }
													
													 

													
														   provisioner "file" {
													

													
														    source      = "${path.module}/data/CVAD-CreateDNSARecordOnDC.ps1"
													

													
														    destination = "${var.CVAD_Install_LogPath}/CVAD-CreateDNSARecordOnDC.ps1"
													

													
														    
													

													
														  }
													
													 

													
														provisioner "remote-exec" {
													

													
														    inline = [
													

													
														      "powershell -File ${var.CVAD_Install_LogPath}/CVAD-CreateDNSARecordOnDC.ps1"
													

													
														    ]
													

													
														  }
													

													
														}
													
													 

													
														###########################################################################################
													

													
														########### This finally completes the Storefront part of the Terraform script. ###########
													

													
														###########################################################################################
													

													
														 */
													
												
											
										
									
								
							
						

						
							 
						 

						
							
								Module 3
							

							
								CVADOnvSphere-Scripts.tf
							 

							
								
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}								
								
									
										
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}										
										
											
												
													
														
															
																
																	# Terraform deployment of Citrix Virtual Apps and Desktops on VMware vSphere
																

																
																	## Run PowerShell-Scripts on the DDCs
																
																 

																
																	### Install CVAD and SF with all chosen components and create a new site
																

																
																	locals {
																

																
																	  #### Create License- and PFX-Import script
																

																
																	  #### Need to invoke PowerShell as Domain User as the provisioner does not allow to be run in a Domain Users-context, but CVAD SDK needs to be run as a Domain User
																

																
																	  LicScriptcontent     = &lt;&lt;-EOT
																

																
																	  $PSUsername = '${var.vsphere_domain_admin_userwithdomain}'
																

																
																	  $PSPassword = '${var.vsphere_domain_admin_pw}'
																

																
																	  $PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
																

																
																	  $PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
																

																
																	  Invoke-Command -ComputerName localhost -Credential $PSCredential -ScriptBlock {
																

																
																	  Add-PSSnapin Citrix.*
																

																
																	  $LICCertHash = (Get-LicCertificate -AdminAddress '${var.CVAD-Lic-ServerAddress}').CertHash
																

																
																	  $LICPath = '${var.CVAD_Install_LogPath}/${var.CVAD-Lic-Name}'
																

																
																	  Import-LicLicenseFile -AdminAddress '${var.CVAD-Lic-ServerAddress}' -CertHash $LICCertHash -Filename $LICPath.Replace('/','\')
																

																
																	  Restart-Service -Name 'Citrix Licensing'
																

																
																	  }
																

																
																	  EOT
																
																 

																
																	  #### Create PFX-Import script
																

																
																	  #### Need to invoke PowerShell as Domain User as the provisioner does not allow to be run in a Domain Users-context, but CVAD SDK needs to be run as a Domain User
																

																
																	  PFXScriptcontent     = &lt;&lt;-EOT
																

																
																	  $PSUsername = '${var.vsphere_domain_admin_userwithdomain}'
																

																
																	  $PSPassword = '${var.vsphere_domain_admin_pw}'
																

																
																	  $PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
																

																
																	  $PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
																

																
																	  Invoke-Command -ComputerName localhost -Credential $PSCredential -ScriptBlock {
																

																
																	  Add-PSSnapin Citrix.*
																

																
																	  Import-PfxCertificate -CertStoreLocation Cert:\LocalMachine\Webhosting -FilePath '${var.CVAD_Install_LogPath}/${var.CVAD-PFX-Name}'
																

																
																	  }
																

																
																	  EOT
																
																 

																
																	  #### Create script to change IIS certificate from self-signed certificate to public certificate 
																

																
																	  #### Need to invoke PowerShell as Domain User as the provisioner does not allow to be run in a Domain Users-context, but CVAD SDK needs to be run as a Domain User
																

																
																	  ChangeIISSSLScriptcontent     = &lt;&lt;-EOT
																

																
																	  $PSUsername = '${var.vsphere_domain_admin_userwithdomain}'
																

																
																	  $PSPassword = '${var.vsphere_domain_admin_pw}'
																

																
																	  $PSSecurePassword = ConvertTo-SecureString $PSPassword -AsPlainText -Force
																

																
																	  $PSCredential = New-Object System.Management.Automation.PSCredential ($PSUsername, $PSSecurePassword)
																

																
																	  Invoke-Command -ComputerName localhost -Credential $PSCredential -ScriptBlock {
																

																
																	  Remove-WebBinding -Name '${var.CVAD-IIS-SiteName}' -IP '*' -Port 443 -Protocol https
																

																
																	  New-WebBinding -Name '${var.CVAD-IIS-SiteName}' -IP '*' -Port 443 -Protocol https
																

																
																	  $SSLCert = Get-ChildItem -Path Cert:\LocalMachine\WebHosting | Where-Object {$_.Subject.Contains('${var.CVAD-IIS-CertSubject}')}
																

																
																	  $Binding = Get-WebBinding -Name '${var.CVAD-IIS-SiteName}' -Protocol "https"
																

																
																	  $Binding.AddSslCertificate($SSLCert.GetCertHashString(), "WebHosting")
																

																
																	  IISReset
																

																
																	  }
																

																
																	  EOT
																
																 

																
																	}
																
																 

																
																	#### Write License- and PFX-Import script into local data-directory
																

																
																	resource "local_file" "WriteLicScriptIntoDataDirectory" {
																

																
																	  #depends_on = [ time_sleep.WaitForRebootofDDC1AfterStoreFrontReplication, time_sleep.WaitForRebootofDDC2AfterStoreFrontReplication ]
																

																
																	  filename = "${path.module}/data/CVAD-Lic.ps1"
																

																
																	  content  = local.LicScriptcontent
																

																
																	}
																
																 

																
																	#### Write PFX-Import script into local data-directory
																

																
																	resource "local_file" "WritePFXScriptIntoDataDirectory" {
																

																
																	  #depends_on = [ time_sleep.WaitForRebootofDDC1AfterStoreFrontReplication, time_sleep.WaitForRebootofDDC2AfterStoreFrontReplication  ]
																

																
																	  filename = "${path.module}/data/CVAD-PFX.ps1"
																

																
																	  content  = local.PFXScriptcontent
																

																
																	} 
																
																 

																
																	#### Write PFX-Import script into local data-directory
																

																
																	resource "local_file" "WriteIISSSLScriptIntoDataDirectory" {
																

																
																	  #depends_on = [ time_sleep.WaitForRebootofDDC1AfterStoreFrontReplication, time_sleep.WaitForRebootofDDC2AfterStoreFrontReplication ]
																

																
																	  filename = "${path.module}/data/CVAD-IIS.ps1"
																

																
																	  content  = local.ChangeIISSSLScriptcontent
																

																
																	}
																
																 

																
																	### Upload license file, PFX certificate and Lic script to DDC1 and execute it
																

																
																	#### Set the Provisioner-Connection
																

																
																	 resource "null_resource" "CreateCVADLicPFXOnDDC1" {
																

																
																	 depends_on = [ local_file.WriteLicScriptIntoDataDirectory ]
																

																
																	 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 License script to DDC1
																

																
																	  provisioner "file" {
																

																
																	    source      = "${path.module}/data/CVAD-Lic.ps1"
																

																
																	    destination = "${var.CVAD_Install_LogPath}/CVAD-Lic.ps1"
																

																
																	    
																

																
																	  }
																
																 

																
																	###### Upload License file to DDC1
																

																
																	  provisioner "file" {
																

																
																	    source      = "${path.module}/data/${var.CVAD-Lic-Name}"
																

																
																	    destination = "${var.CVAD_Install_LogPath}/${var.CVAD-Lic-Name}"
																

																
																	    
																

																
																	  }
																
																 

																
																	###### Upload PFX script to DDC1
																

																
																	  provisioner "file" {
																

																
																	    source      = "${path.module}/data/CVAD-PFX.ps1"
																

																
																	    destination = "${var.CVAD_Install_LogPath}/CVAD-PFX.ps1"
																

																
																	    
																

																
																	  }
																
																 

																
																	###### Upload PFX file to DDC1
																

																
																	  provisioner "file" {
																

																
																	    source      = "${path.module}/data/${var.CVAD-PFX-Name}"
																

																
																	    destination = "${var.CVAD_Install_LogPath}/${var.CVAD-PFX-Name}"
																

																
																	    
																

																
																	  }
																
																 

																
																	###### Upload IIS-SSLscript to DDC1
																

																
																	  provisioner "file" {
																

																
																	    source      = "${path.module}/data/CVAD-IIS.ps1"
																

																
																	    destination = "${var.CVAD_Install_LogPath}/CVAD-IIS.ps1"
																

																
																	    
																

																
																	  }
																

																
																	 
																

																
																	###### Execute the License script to DDC1
																

																
																	 provisioner "remote-exec" {
																

																
																	    inline = [
																

																
																	      "powershell -File ${var.CVAD_Install_LogPath}/CVAD-Lic.ps1"
																

																
																	    ]
																

																
																	  }
																
																 

																
																	###### Execute the PFX script to DDC1
																

																
																	 provisioner "remote-exec" {
																

																
																	    inline = [
																

																
																	      "powershell -File ${var.CVAD_Install_LogPath}/CVAD-PFX.ps1"
																

																
																	    ]
																

																
																	  }
																

																
																	}
																

																
																	 
																

																
																	### Upload PFX certificate and PFX script to DDC2 and execute it
																

																
																	#### Set the Provisioner-Connection
																

																
																	 resource "null_resource" "CreateCVADPFXOnDDC2" {
																

																
																	 depends_on = [ local_file.WriteLicScriptIntoDataDirectory ]
																

																
																	 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 PFX script to DDC2
																

																
																	  provisioner "file" {
																

																
																	    source      = "${path.module}/data/CVAD-PFX.ps1"
																

																
																	    destination = "${var.CVAD_Install_LogPath}/CVAD-PFX.ps1"
																

																
																	    
																

																
																	  }
																
																 

																
																	###### Upload PFX file to DDC2
																

																
																	  provisioner "file" {
																

																
																	    source      = "${path.module}/data/${var.CVAD-PFX-Name}"
																

																
																	    destination = "${var.CVAD_Install_LogPath}/${var.CVAD-PFX-Name}"
																

																
																	    
																

																
																	  }
																
																 

																
																	###### Upload IIS-SSLscript to DDC2
																

																
																	  provisioner "file" {
																

																
																	    source      = "${path.module}/data/CVAD-IIS.ps1"
																

																
																	    destination = "${var.CVAD_Install_LogPath}/CVAD-IIS.ps1"
																

																
																	    
																

																
																	  }
																
																 

																
																	###### Execute the PFX script onto DDC2
																

																
																	 provisioner "remote-exec" {
																

																
																	    inline = [
																

																
																	      "powershell -File ${var.CVAD_Install_LogPath}/CVAD-PFX.ps1"
																

																
																	    ]
																

																
																	  }
																

																
																	}
																
																 

																
																	### Execute IIS-SSL script on DDC1 
																

																
																	#### Set the Provisioner-Connection
																

																
																	 resource "null_resource" "ChangeSSLOnDDC1" {
																

																
																	 depends_on = [ null_resource.CreateCVADLicPFXOnDDC1 ]
																

																
																	 connection {
																

																
																	    type            = var.Provisioner_Type
																

																
																	    user            = var.Provisioner_Admin-Username
																

																
																	    password        = var.Provisioner_Admin-Password
																

																
																	    host            = var.Provisioner_DDC1-IP
																

																
																	    timeout         = var.Provisioner_Timeout
																
																 

																
																	  }
																

																
																	 
																

																
																	###### Execute the IIS-SSL script on DDC1
																

																
																	 provisioner "remote-exec" {
																

																
																	    inline = [
																

																
																	      "powershell -File ${var.CVAD_Install_LogPath}/CVAD-IIS.ps1"
																

																
																	    ]
																

																
																	  }
																

																
																	}
																

																
																	 
																

																
																	### Execute IIS-SSL script on DDC2d execute it
																

																
																	#### Set the Provisioner-Connection
																

																
																	 resource "null_resource" "ChangeSSLOnDDC2" {
																

																
																	 depends_on = [ null_resource.CreateCVADPFXOnDDC2 ]
																

																
																	connection {
																

																
																	    type            = var.Provisioner_Type
																

																
																	    user            = var.Provisioner_Admin-Username
																

																
																	    password        = var.Provisioner_Admin-Password
																

																
																	    host            = var.Provisioner_DDC2-IP
																

																
																	    timeout         = var.Provisioner_Timeout
																
																 

																
																	  }
																

																
																	 
																

																
																	###### Execute the IIS-SSL script on DDC2
																

																
																	 provisioner "remote-exec" {
																

																
																	    inline = [
																

																
																	      "powershell -File ${var.CVAD_Install_LogPath}/CVAD-IIS.ps1"
																

																
																	    ]
																

																
																	  }
																

																
																	}
																
																 

																
																	##### Reboot DDC1 
																

																
																	###### Set the Provisioner-Connection
																

																
																	resource "null_resource" "RebootDDC1-2" {
																

																
																	  depends_on = [ null_resource.ChangeSSLOnDDC1 ]
																
																 

																
																	connection {
																

																
																	    type            = var.Provisioner_Type
																

																
																	    user            = var.Provisioner_Admin-Username
																

																
																	    password        = var.Provisioner_Admin-Password
																

																
																	    host            = var.Provisioner_DDC1-IP
																

																
																	    timeout         = var.Provisioner_Timeout
																
																 

																
																	  }
																
																 

																
																	###### Execute the Reboot script
																

																
																	  provisioner "remote-exec" {
																

																
																	    inline = [
																

																
																	      "powershell -File ${var.CVAD_Install_LogPath}/Reboot.ps1"
																

																
																	    ]
																

																
																	  }
																

																
																	}
																
																 

																
																	##### Reboot DDC2 
																

																
																	###### Set the Provisioner-Connection
																

																
																	resource "null_resource" "RebootDDC2-2" {
																

																
																	  depends_on = [ null_resource.ChangeSSLOnDDC2 ]
																
																 

																
																	connection {
																

																
																	    type            = var.Provisioner_Type
																

																
																	    user            = var.Provisioner_Admin-Username
																

																
																	    password        = var.Provisioner_Admin-Password
																

																
																	    host            = var.Provisioner_DDC2-IP
																

																
																	    timeout         = var.Provisioner_Timeout
																
																 

																
																	  }
																
																 

																
																	###### Execute the Reboot script
																

																
																	  provisioner "remote-exec" {
																

																
																	    inline = [
																

																
																	      "powershell -File ${var.CVAD_Install_LogPath}/Reboot.ps1"
																

																
																	    ]
																

																
																	  }
																

																
																	}
																
																 
																 
																 
																
																	#########################################################################################################################################
																

																
																	#########################################################################################################################################
																

																
																	##                                                  That completes the initial site setup                                              ##
																

																
																	#########################################################################################################################################
																

																
																	#########################################################################################################################################
																
															
														
													
												
											
										
									

									
										
											 
											Module 4
										

										
											CVADOnvSphere-CreateEntities.tf
										 

										
											
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}											
											
												
													
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}													
													
														
															
																
																	
																		
																			
																				
																					
																						# Terraform deployment of Citrix Virtual Apps and Desktops on VMware vSphere
																					

																					
																						## Create the Hosting Connection, a Machine Catalog and a Delivery Group
																					

																					
																						data "vsphere_datacenter" "datacenter" {
																					

																					
																						  name          = var.vsphere_datacenter
																					

																					
																						}
																					
																					 

																					
																						data "vsphere_datastore" "datastore" {
																					

																					
																						  name          = var.vsphere_datastore
																					

																					
																						  datacenter_id = data.vsphere_datacenter.datacenter.id
																					

																					
																						}
																					
																					 

																					
																						data "vsphere_network" "network" {
																					

																					
																						  name          = var.vsphere_network
																					

																					
																						  datacenter_id = data.vsphere_datacenter.datacenter.id
																					

																					
																						}
																					
																					 

																					
																						data "vsphere_host" "host" {
																					

																					
																						  name          = var.vsphere_host
																					

																					
																						  datacenter_id = data.vsphere_datacenter.datacenter.id
																					

																					
																						}
																					
																					 

																					
																						### If a cluster is available, uncomment this block
																					

																					
																						data "vsphere_compute_cluster" "cluster" {
																					

																					
																						name            = "TACG"
																					

																					
																						datacenter_id   = data.vsphere_datacenter.datacenter.id
																					

																					
																						}
																					
																					 

																					
																						### Create the necessary zone
																					

																					
																						resource "citrix_zone" "TACG-TF-HYP-Zone" {
																					

																					
																						    name        = "${var.CVAD_HC-ZoneName}"
																					

																					
																						}
																					
																					 

																					
																						output "ZoneID" {
																					

																					
																						    value       = citrix_zone.TACG-TF-HYP-Zone.id
																					

																					
																						    
																					

																					
																						    } 
																					

																					
																						### Create the Hypervisor Connection
																					

																					
																						resource "citrix_vsphere_hypervisor" "TACG-TF-HypConn" {
																					

																					
																						    depends_on = [ citrix_zone.TACG-TF-HYP-Zone ]
																					

																					
																						    name            = "${var.CVAD_HC-ConnectionName}"
																					

																					
																						    zone            = citrix_zone.TACG-TF-HYP-Zone.id
																					

																					
																						    username        = "${var.vsphere_user}"
																					

																					
																						    password        = "${var.vsphere_pw}"        
																					

																					
																						    password_format = "PlainText"
																					

																					
																						    addresses       = [
																					

																					
																						                          "${var.vsphere_url2}"
																					

																					
																						                      ]
																					

																					
																						    max_absolute_active_actions = 20
																					
																					 

																					
																						}
																					
																					 

																					
																						### Create the Hypervisor Resource Pool
																					

																					
																						resource "citrix_vsphere_hypervisor_resource_pool" "TACG-TF-HypConnPool" {
																					

																					
																						    depends_on = [ citrix_vsphere_hypervisor.TACG-TF-HypConn ]
																					

																					
																						    name                        = "${var.CVAD_HC-ConnectionResPoolName}"
																					

																					
																						    hypervisor                  = citrix_vsphere_hypervisor.TACG-TF-HypConn.id
																					

																					
																						    cluster                     = {
																					

																					
																						                  datacenter    = data.vsphere_datacenter.datacenter.name
																					

																					
																						                  cluster_name  = data.vsphere_compute_cluster.cluster.name
																					

																					
																						                  #host          = "93.83.208.30"
																					

																					
																						    }
																					

																					
																						    networks                    = [ data.vsphere_network.network.name, ]
																					

																					
																						    storage                     = [
																					

																					
																						                                    {
																					

																					
																						                                    storage_name = data.vsphere_datastore.datastore.name
																					

																					
																						                                    }
																					

																					
																						                                  ]
																					

																					
																						    temporary_storage           = [ 
																					

																					
																						                                    {
																					

																					
																						                                    storage_name = data.vsphere_datastore.datastore.name
																					

																					
																						                                    }
																					

																					
																						                                  ]    
																					

																					
																						    use_local_storage_caching = false
																					
																					 

																					
																						        
																					

																					
																						}
																					
																					 

																					
																						### Create the Machine Catalog
																					

																					
																						resource "citrix_machine_catalog" "MC-TACG-TF-ONP-VSP" {
																					

																					
																						 depends_on = [ citrix_vsphere_hypervisor_resource_pool.TACG-TF-HypConnPool ]
																					

																					
																						  name                                = "${var.CVAD_MC-Name}"
																					

																					
																						  description                         = "${var.CVAD_MC-Description}"
																					

																					
																						  zone                                = citrix_zone.TACG-TF-HYP-Zone.id
																					

																					
																						  allocation_type                     = "${var.CVAD_MC-AllocationType}" # "Random"
																					

																					
																						  session_support                     = "${var.CVAD_MC-SessionType}" # "SingleSession"
																					

																					
																						  is_power_managed                    = true
																					
																					 

																					
																						  provisioning_type                   = "MCS" 
																					

																					
																						  provisioning_scheme                 = {
																					

																					
																						      identity_type                   = "ActiveDirectory"
																					

																					
																						       hypervisor                     = citrix_vsphere_hypervisor.TACG-TF-HypConn.id
																					

																					
																						       hypervisor_resource_pool       = citrix_vsphere_hypervisor_resource_pool.TACG-TF-HypConnPool.id
																					

																					
																						    
																					

																					
																						    machine_domain_identity = {
																					

																					
																						            domain                    = "${var.vsphere_domainname}"
																					

																					
																						            service_account           = "${var.vsphere_domain_admin_userwithdomain}"
																					

																					
																						            service_account_password  = "${var.vsphere_domain_admin_pw}"
																					

																					
																						        }
																					
																					 

																					
																						    vsphere_machine_config            =  {
																					

																					
																						           master_image_vm                  = "${var.CVAD_MC-MasterImageName}"
																					

																					
																						           cpu_count                        = "${var.CVAD_MC-CpuCount}"
																					

																					
																						           memory_mb                        = "${var.CVAD_MC-MemorySize}"
																					

																					
																						           image_snapshot                   = "${var.CVAD_MC-MasterImageSnapshot}"
																					

																					
																						                                         }
																					
																					 

																					
																						    number_of_total_machines          = "${var.CVAD_MC-MachineCount}"
																					
																					 

																					
																						    machine_account_creation_rules    =  {
																					

																					
																						      naming_scheme                          = "${var.CVAD_MC-NamingSchemeName}"
																					

																					
																						      naming_scheme_type                     = "${var.CVAD_MC-NamingSchemeType}" 
																					

																					
																						      domain                                 = "${var.CVAD_MC-DomainName}"
																					

																					
																						            #main_ou                        = "${var.CVAD_MC-domain-ou}"
																					

																					
																						                                         }     
																					

																					
																						                                                
																					

																					
																						    os_type                           = "${var.CVAD_MC-OSType}" 
																					

																					
																						    #storage_type                      = "${var.cc-mc-storage_type}" 
																					

																					
																						    #use_managed_disks                 = "${var.cc-mc-use_managed_disks}" 
																					

																					
																						  }
																					

																					
																						} 
																					
																					 

																					
																						resource "citrix_delivery_group" "DG-TACG-TF-ONP-VSP" {
																					

																					
																						  depends_on = [ citrix_machine_catalog.MC-TACG-TF-ONP-VSP ]
																					

																					
																						    name                             = "${var.CVAD_DG-Name}"
																					

																					
																						    associated_machine_catalogs      = [
																					

																					
																						                                          {
																					

																					
																						                                            machine_catalog   = citrix_machine_catalog.MC-TACG-TF-ONP-VSP.id
																					

																					
																						                                            machine_count     = "${var.CVAD_MC-MachineCount}"
																					

																					
																						                                          }
																					

																					
																						                                       ]
																					
																					 

																					
																						    desktops                         = [
																					

																					
																						                                          {
																					

																					
																						                                            published_name          = "${var.CVAD_DG-DesktopName}"
																					

																					
																						                                            description             = "${var.CVAD_DG-DesktopDescription}"
																					

																					
																						                                            restricted_access_users = {
																					

																					
																						                                                                        allow_list = [ "${var.CVAD_DG-AllowedUsers}" ]
																					

																					
																						                                                                      }
																					

																					
																						                                            enabled                 = true
																					

																					
																						                                            enable_session_roaming  = "${var.CVAD_DG-SessionRoaming}"
																					

																					
																						                                          }
																					

																					
																						                                       ] 
																					
																					 

																					
																						    autoscale_settings              =  {
																					

																					
																						                                          autoscale_enabled                               = "${var.CVAD_DG-AS-Enabled}"
																					

																					
																						                                          disconnect_peak_idle_session_after_seconds      = "${var.CVAD_DG-AS-PeakDisconnectTime}"
																					

																					
																						                                          log_off_peak_disconnected_session_after_seconds = "${var.CVAD_DG-AS-PeakLogoffTime}"
																					

																					
																						                                          peak_log_off_action                             = "${var.CVAD_DG-AS-PeakLogoffAction}"
																					

																					
																						                                          peak_disconnect_action                          = "${var.CVAD_DG-AS-PeakDisconnectAction}"
																					

																					
																						                                          power_time_schemes                              = [
																					

																					
																						                                                                                               {
																					

																					
																						                                                                                                  days_of_week        = [
																					

																					
																						                                                                                                                          "Monday",
																					

																					
																						                                                                                                                          "Tuesday",
																					

																					
																						                                                                                                                          "Wednesday",
																					

																					
																						                                                                                                                          "Thursday",
																					

																					
																						                                                                                                                          "Friday"
																					

																					
																						                                                                                                                        ]
																					

																					
																						                                                                                                  name                = "${var.CVAD_DG-AS-Name}"
																					

																					
																						                                                                                                  display_name        = "${var.CVAD_DG-AS-DisplayName}"
																					

																					
																						                                                                                                  peak_time_ranges    = [ "09:00-17:00" ]
																					

																					
																						                                                                                     
																					

																					
																						                                                                                                  pool_size_schedules = [
																					

																					
																						                                                                                                                          {
																					

																					
																						                                                                                                                              time_range = "09:00-17:00",
																					

																					
																						                                                                                                                              pool_size =  "${var.CVAD_DG-AS-PoolSize}"
																					

																					
																						                                                                                                                          }
																					

																					
																						                                                                                                                        ]
																					

																					
																						                                                                                                  pool_using_percentage = false
																					

																					
																						                                                                                              }, 
																					

																					
																						                                                                                            ]
																					

																					
																						                                       } 
																					
																					 

																					
																						    restricted_access_users         =  {
																					

																					
																						                                          allow_list = [ "${var.CVAD_DG-AllowedUsers}" ]            
																					

																					
																						                                       } 
																					
																					 

																					
																						    reboot_schedules                =  [
																					

																					
																						                                          {
																					

																					
																						                                            name                                        = "${var.CVAD_DG-RS-Name}"
																					

																					
																						                                            reboot_schedule_enabled                     = "${var.CVAD_DG-RS-Enabled}"
																					

																					
																						                                            frequency                                   = "${var.CVAD_DG-RS-Frequency}" #"Weekly"
																					

																					
																						                                            frequency_factor                            = 1
																					

																					
																						                                            days_in_week                                = [
																					

																					
																						                                                                                              "Monday",
																					

																					
																						                                                                                              "Wednesday",
																					

																					
																						                                                                                              "Friday"
																					

																					
																						                                                                                          ]
																					

																					
																						                                            start_time                                  = "${var.CVAD_DG-RS-StartTime}"
																					

																					
																						                                            start_date                                  = "${var.CVAD_DG-RS-StartDate}" #"2024-05-25"
																					

																					
																						                                            reboot_duration_minutes                     = 0
																					

																					
																						                                            ignore_maintenance_mode                     = "${var.CVAD_DG-RS-IgnoreMaint}"
																					

																					
																						                                            natural_reboot_schedule                     = "${var.CVAD_DG-RS-IsNaturalReboot}"
																					

																					
																						                                            reboot_notification_to_users                = {
																					

																					
																						                                                                                              notification_duration_minutes = 15
																					

																					
																						                                                                                              notification_message = "test message"
																					

																					
																						                                                                                              notification_title = "test title"
																					

																					
																						                                                                                              notification_repeat_every_5_minutes = true
																					

																					
																						                                                                                          }
																					

																					
																						                                          },
																					

																					
																						                                      ] 
																					
																					 

																					
																						    minimum_functional_level      = "L7_20"
																					

																					
																						}
																					
																					 
																					 
																				
																			
																			 

																			
																				#########################################################################################################################################
																			

																			
																				#########################################################################################################################################
																			

																			
																				##                                                  That completes the ENtities setup****                                              ##
																			

																			
																				#########################################################################################################################################
																			

																			
																				#########################################################################################################################################
																			
																		
																	
																
															
														
													
												

												
													 
												 
											
										
									

									
										Example of Variable definitions:
									

									
										
											
												
													
														
															
																
																	
																		# Variables for Citrix CVAD on vSphere 8 
																	

																	
																		## Definition of Site-related variables - needed for uploading and executing PowerShell scripts on the VMs
																	

																	
																		variable "CVAD-PFX-Name" {
																	

																	
																		  type        = string
																	

																	
																		  description = "REQUIRED: Filename of the PFX certificate"
																	

																	
																		}
																	

																	
																		variable "CVAD-Lic-Name" {
																	

																	
																		  type        = string
																	

																	
																		  description = "REQUIRED: Filename of the CVAD license"
																	

																	
																		}
																	

																	
																		variable "CVAD-Lic-ServerAddress" {
																	

																	
																		  type        = string
																	

																	
																		  description = "REQUIRED: FQDN of License Server"
																	

																	
																		}
																	

																	
																		variable "CVAD_Install_LogPath" {
																	

																	
																		  type        = string
																	

																	
																		  default     = "C:/TEMP/XDINST"
																	

																	
																		  description = "REQUIRED: Log-Path where Installer logs are written"
																	

																	
																		}
																	
																	 

																	
																		## Definition of Storefront-related variables - needed for uploading and executing PowerShell scripts on the VMs
																	

																	
																		variable "CVAD-IIS-CertSubject" {
																	

																	
																		  type        = string
																	

																	
																		  description = "REQUIRED: Subject of the PFX-Certificate to be bound to the IIS site"
																	

																	
																		}
																	

																	
																		variable "CVAD-IIS-SiteName" {
																	

																	
																		  type        = string
																	

																	
																		  description = "REQUIRED: Site-Name of the IIS-Site"
																	

																	
																		}
																	
																	 

																	
																		## Definition of Domain-related variables
																	

																	
																		variable "vsphere_domain_admin_user" {
																	

																	
																		  type        = string
																	

																	
																		  sensitive   = true
																	

																	
																		  description = "REQUIRED: Domain-Admin-Username"
																	

																	
																		}
																	

																	
																		variable "vsphere_domain_admin_userwithdomain" {
																	

																	
																		  type        = string
																	

																	
																		  sensitive   = true
																	

																	
																		  description = "REQUIRED: Domain-Admin-Username"
																	

																	
																		}
																	

																	
																		variable "vsphere_domain_admin_pw" {
																	

																	
																		  type        = string
																	

																	
																		  sensitive   = true
																	

																	
																		  description = "REQUIRED: Domain-Admin-PAssword"
																	

																	
																		}
																	

																	
																		variable "vsphere_local_admin_pw" {
																	

																	
																		  type        = string
																	

																	
																		  sensitive   = true
																	

																	
																		  description = "REQUIRED: Domain-Admin-PAssword"
																	

																	
																		}
																	
																	 

																	
																		## Definition of Connection-related variables - needed for uploading and executing PowerShell scripts on the VMs
																	

																	
																		variable "Provisioner_Type" {
																	

																	
																		  type        = string
																	

																	
																		  default     = "winrm"
																	

																	
																		  description = "REQUIRED: Connection type for Terraform provisioner"
																	

																	
																		}
																	

																	
																		variable "Provisioner_Admin-Username" {
																	

																	
																		  type        = string
																	

																	
																		  sensitive   = true
																	

																	
																		  description = "REQUIRED: Username with sufficient rights to use Provisioner"
																	

																	
																		}
																	

																	
																		variable "Provisioner_Admin-Password" {
																	

																	
																		  type        = string
																	

																	
																		  sensitive   = true
																	

																	
																		  description = "REQUIRED: Password for User with sufficient rights to use Provisioner"
																	

																	
																		}
																	

																	
																		variable "Provisioner_DomainAdmin-Username" {
																	

																	
																		  type        = string
																	

																	
																		  sensitive   = true
																	

																	
																		  description = "REQUIRED: Domain-Username with sufficient rights to use Provisioner"
																	

																	
																		}
																	

																	
																		variable "Provisioner_DomainAdmin-Password" {
																	

																	
																		  type        = string
																	

																	
																		  sensitive   = true
																	

																	
																		  description = "REQUIRED: Password for Domain-User with sufficient rights to use Provisioner"
																	

																	
																		}
																	

																	
																		variable "Provisioner_DDC1-IP" {
																	

																	
																		  type        = string
																	

																	
																		   description = "REQUIRED: IP-Address to connect to DDC1"
																	

																	
																		}
																	

																	
																		variable "Provisioner_DDC2-IP" {
																	

																	
																		  type        = string
																	

																	
																		   description = "REQUIRED: IP-Address to connect to DDC2"
																	

																	
																		}
																	

																	
																		variable "Provisioner_DC-IP" {
																	

																	
																		  type        = string
																	

																	
																		   description = "REQUIRED: IP-Address to connect to DC"
																	

																	
																		}
																	

																	
																		variable "Provisioner_Timeout" {
																	

																	
																		  type        = string
																	

																	
																		  default     = "1800s"
																	

																	
																		  description = "REQUIRED: Timeout (in s) for Provisioner"
																	

																	
																		}
																	
																
															
														
													
												
											
										
									

									
										 
										Example of setting the Variables:
									

									
										
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}										
									
								
							
						
					
				
			
		
	

	
		
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}		
		
			
				
					
						
							
								
									... 
									    "CVAD-IIS-CertSubject":"the-austrian-citrix-guy.at",
								

								
									    "CVAD-IIS-SiteName":"Default Web Site",
								

								
									    "CVAD-Lic-Name":"FID_c22d92a1_7e59_4820_a3a9_68e8b5ecdad9.lic",
								

								
									    "CVAD-Lic-ServerAddress":"https://localhost:8083",
								

								
									    "CVAD-PFX-Name":"tacg.2024.pfx",
								

								
									    "CVAD_Install_LogPath":"C:/TEMP/XDINST",
								

								
									    "vsphere_domain_admin_user":"administrator",
								

								
									    "vsphere_domain_admin_userwithdomain":"XXXXX\\XXXXXXXXXX",
								

								
									    "vsphere_domain_admin_pw":"XXXXXXXXXX",
								

								
									    "vsphere_local_admin_pw":"XXXXXXXXXX",
								

								
									    "Provisioner_Type":"winrm",
								

								
									    "Provisioner_Admin-Username":"administrator", 
								

								
									    "Provisioner_Admin-Password":"XXXXXXXXXX", 
								

								
									    "Provisioner_DomainAdmin-Username":"XXXXX\\XXXXXXXXXX", 
								

								
									    "Provisioner_DomainAdmin-Password":"XXXXXXXXXX", 
								

								
									    "Provisioner_DDC1-IP":"10.10.170.11",
								

								
									    "Provisioner_DDC2-IP":"10.10.170.12",
								

								
									    "Provisioner_DC-IP":"10.10.100.1",
								

								
									    "Provisioner_Timeout":"1800s",
								

								
									...
								
							
						
					
				
			
		
	

	
		 
	 


.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:"Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_06/vcenter-creating-vms.jpg.b59453436ab7235fc2878660073a13a5.jpg" length="81214" type="image/jpeg"/><pubDate>Mon, 01 Jul 2024 11:48:00 +0000</pubDate></item><item><title>Deployment Guide: Using Terraform to deploy a Citrix Cloud Resource Location on vSphere 8</title><link>https://community.stage.citrix.com/tech-zone/automation/terraform-daas-vsphere8/</link><description><![CDATA[Using Terraform to deploy a Citrix Cloud Resource Location on vSphere 8



	Overview



	This guide will showcase the possibility of deploying a Citrix DaaS Resource Location on vSphere 8 using Terraform. We want to reduce manual interventions to the absolute minimum. 
	 
	In the end, you will have created:
 


	
		A new Citrix Cloud Resource Location (RL) running on-premise on vSphere 8
	
	
		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 on vSphere 8 
	



	
		NOTE:
	 

	
		Deploying a Machine Catalog and a Delivery Group in the just-created Resource Location in Part 1 is outside the scope of this guide. 
		You can find detailed instructions for creating a Machine Catalog and a Delivery Group in these guides:
	 

	
		Citrix DaaS and Terraform - Automatic Deployment of a Resource Location on Amazon EC2
	 

	
		Citrix DaaS and Terraform - Automatic Deployment of a Resource Location on Google Cloud Platform (GCP)
	 

	
		Citrix DaaS and Terraform - Automatic Deployment of a Resource Location on Microsoft Azure 
		 
	 



	 
 


	
		IMPORTANT:
	 

	
		You can find the related guide "Using Terraform to deploy Citrix Virtual Apps and Desktops 2402 LTSR on vSphere 8" on Citrix Tech Zone: https://community.citrix.com/tech-zone/build/deployment-guides/terraform-cvad-vsphere8 
		 
	 



	 
	Installation and Configuration of Terraform



	Please find a description of installing Terraform and initial configurations in our Tech Zone Deployment Guide Installing Terraform and Configuring the Citrix Terraform Provider
 


	All Terraform configuration files can be found later on GitHub.
 


	 
	Deployment overview 
	 
	We use an existing domain in this guide and will not deploy a new one. 
	For further instructions on 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 primary Domain Controller using IPSec-based Site-to-Site VPNs. Each Resource Location has its subdomain. 
	 
 


	
		NOTE: 
	 

	
		We did not use loop-constructs or count keywords —each resource is created explicitly—for easier reading and understanding. 
		 
	 



	 
	The Terraform flow is split into different parts:
 


	
		Module One - this part can be run on any computer where Terraform is installed :

		
			
				Creating the initially needed Resources on vSphere 8:

				
					
						Creating a Windows 11-based Master Image VM 
					
					
						Creating two Windows Server 2022-based VMs used as Cloud Connector VMs in step 2
					
					
						Creating a Windows Server 2022-based VM acting as an Administrative workstation for running the Terraform Modules 2 and 3 is necessary because WinRM is used for further configuration and deployment in Modules 2 and 3!
					
					
						Creating all necessary scripts for joining the VMs to the existing sub-domain
					
					
						Putting the VMs into the existing sub-domain
					
				
			
		
	



	 
 


	
		IMPORTANT: 
	 

	
		The Master Image needs to be configured before deployment - Terraform will configure a blank, Domain-joined VM! 
	 

	
		The Administrative VM must have Terraform installed - use the Chocolately Guidance provided previously for installation! 
		 
	 



	 
 


	
		Module Two - this part can only be run on the previously created Administrative VM as the deployment of Modules 2 and 3 relies heavily on WinRM:

		
			
				Configuring the three previously created Virtual Machines on vSphere 8:

				
					
						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
					
				
			
		
	
	
		Module Three:
		
			
				Creating the Hypervisor Connection and the Hypervisor Resource Pool 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
					
				
			
		
	



	
		IMPORTANT:
	 

	
		Make sure that all Terraform-related VMs can communicate using WinRM. 
		The deployment fails if the Admin-VM cannot connect to the CCs using WinRM. 
		Various configuration guides for WinRM can be found on the Internet.
	 

	
		We rely on GPOs related to WinRM to configure all Domain Members accordingly! 
		 
	 



	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:
 


	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 td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:"Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\TACG&gt; test-wsman -ComputerName &lt;IP-Address of the computer you want to reach&gt; -Credential &lt;IP-Address of the computer you want to reach&gt;\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 &lt;IP-Address of the computer you want to reach&gt; -Credential &lt;IP-Address of the computer you wish to reach&gt;\administrator  
					 

					
						The response should look like:   
						[172.31.22.104]: PS C:\Users\Administrator\Documents&gt;
					 
				
			
		
	



	 
 


	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 #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}	
	
		
			
				
					
						
							locals {
						 

						
							  #### Test the WinRM communication
						 

						
							  #### Need to invoke PowerShell as Domain User as the provisioner does not allow it to be run in a Domain Users-context
						 

						
							  TerraformTestWinRMScript     = &lt;&lt;-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 td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:"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!  
					 

					
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt; 
						null_resource.CreateTestScriptOnCC1 (remote-exec): C:\Users\Administrator&gt;powershell -File c:/temp/xdinst/DATA/Terraform-Test-WinRM.ps1
					 

					
						#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML 
						&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;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 that must be set. Some Configuration settings are propagated throughout the whole Terraform configuration...
 


	You must start each of the three 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.
 


	
		IMPORTANT:
	 

	
		Each module/step must be completed successfully before the next module can be started! 
		 
	 



	 
	File System structure



	Root-Directory
 


	Module 1: _CConvSphere-Creation:
 


	
		
			
				
					Filename                                                                                                       
				 
			
			
				
					Purpose                                                
				 
			
		
	
	
		
			
				
					_CConvSphere-Creation-Create.tf
				 
			
			
				
					Resource configuration and primary flow definition
				 
			
		
		
			
				
					_CConvSphere-Creation-Create-variables.tf
				 
			
			
				
					Definition of Variables
				 
			
		
		
			
				
					_CConvSphere-Creation-Create.auto.tfvars.json
				 
			
			
				
					Setting the values of the Variables
				 
			
		
		
			
				
					_CConvSphere-Creation-Provider.tf
				 
			
			
				
					Provider definition and configuration
				 
			
		
		
			
				
					_CConvSphere-Creation-Provider-variables.tf
				 
			
			
				
					Definition of Variables
				 
			
		
		
			
				
					_CConvSphere-Creation-Provider.auto.tfvars.json
				 
			
			
				
					Setting the values of the Variables
				 
			
		
	



	 
	Module 2: _CConvSphere-Install:
 


	
		
			
				
					Filename                                                                                                        
				 
			
			
				
					Purpose                                               
				 
			
		
	
	
		
			
				
					_CConvSphere-Install-CreatePreReqs.tf
				 
			
			
				
					Resource configuration and primary flow definition
				 
			
		
		
			
				
					_CConvSphere-Install-CreatePreReqs-variables.tf
				 
			
			
				
					Definition of Variables
				 
			
		
		
			
				
					_CConvSphere-Install-CreatePreReqs.auto.tfvars.json
				 
			
			
				
					Setting the values of the Variables
				 
			
		
		
			
				
					_CConvSphere-Install-Provider.tf
				 
			
			
				
					Provider definition and configuration
				 
			
		
		
			
				
					_CConvSphere-Install-Provider-variables.tf
				 
			
			
				
					Definition of Variables
				 
			
		
		
			
				
					_CConvSphere-Install-Provider.auto.tfvars.json
				 
			
			
				
					Setting the values of the Variables
				 
			
		
		
			
				
					DATA Directory
				 
			
			
				
					Contains dynamically created scripts which will be uploaded and executed on the Cloud Connector VMs
				 
			
		
		
			
				
					 
				 
			
			
				
					 
				 
			
		
	



	Module 3: _CConvSphere-CCStuff:
 


	
		
			
				
					Filename                                                                                                        
				 
			
			
				
					Purpose                                               
				 
			
		
	
	
		
			
				
					_CConvSphere-CCStuff-CreateCCEntities.tf
				 
			
			
				
					Resource configuration and primary flow definition
				 
			
		
		
			
				
					_CConvSphere-CCStuff-CreateCCEntities-variables.tf
				 
			
			
				
					Definition of Variables
				 
			
		
		
			
				
					_CConvSphere-CCStuff-CreateCCEntities.auto.tfvars.json
				 
			
			
				
					Setting the values of the Variables
				 
			
		
		
			
				
					_CConvSphere-CCStuff-Provider.tf
				 
			
			
				
					Provider definition and configuration
				 
			
		
		
			
				
					_CConvSphere-CCStuff-Provider-variables.tf
				 
			
			
				
					Definition of Variables
				 
			
		
		
			
				
					_CConvSphere-CCStuff-Provider.auto.tfvars.json
				 
			
			
				
					Setting the values of the Variables
				 
			
		
	



	 
 


	
		CAUTION:
	 

	
		All Terraform-related directories and files (.terraform, -terraform.lock.hcl, terraform.tfstate, terraform.tfstate) must not be changed or deleted - doing so might break the deployment! 
		 
	 



	Change the settings in the .json files to match your needs.
 


	To ensure a smooth and error-free build, the following prerequisites must be met before setting the corresponding settings or running the Terraform workflow.
 


	 
 


	Software Components for Configuration and Deployment



	
		IMPORTANT: 
		 
		The Cloud Connector Installer and the CVAD PowerShell SDK Installer must be manually uploaded to a location where Terraform can download them during the configuration run. 
		 
	 



	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 to which all necessary software is uploaded. 
	The URIs of the Storage Repository can be set in the corresponding variables:
 


	
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}	
	
		
			
				
					
						
							... 
							"CC_Install_CWCURI":"https://XXXX.blob.core.windows.net/tfdata/cwcconnector.exe", 
							"CC_Install_RPoSHURI":"https://XXXX.blob.core.windows.net/tfdata/CitrixPoshSdk.exe",
						 

						
							...
						 
					
				
			
		
	



	 
 


	Module 1: Create the initially needed Resources on vSphere 8



	This module is split into the following configuration parts:
 


	
		Creating the initially needed Resources on Amazon EC2:

		
			
				Creating a Windows 11-based Master Image VM used for deploying a Machine Catalog
			
			
				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 Modules 2 and 3—this is necessary because we will use WinRM for further configuration and deployment in Modules 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
			
		
	



	Terraform automatically does all these steps.
 


	
		IMPORTANT:
	 

	
		This first part is separated from the next steps as we assume no Admin-VM is installed in the corresponding VPC/Subnet. 
	 

	
		The Admin-VM is created now in Module 1, and all subsequent modules must be run from it. Working with WinRM calls over the Internet would pose a security threat and significantly reduce the performance of the Terraform-based deployment. 
	 

	
		These are the reasons for the split approach! 
		 
	 



	 
 


	
		IMPORTANT:
	 

	
		Before starting the Terraform workflow, please ensure 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 td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:"Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\_TACG\_CCRLOnvSphere\CCOnvSphere - Creation&gt; terraform init
					 

					
						Initializing the backend...
					 

					
						Initializing provider plugins... 
						- terraform.io/builtin/terraform is built in to Terraform 
						- Finding citrix/citrix versions matching "0.6.0"... 
						- Finding hashicorp/vsphere versions matching "&gt;= 2.7.0"... 
						- Finding mastercard/restapi versions matching "1.18.2"... 
						- Finding latest version of hashicorp/local... 
						- Installing hashicorp/vsphere v2.8.1... 
						- Installed hashicorp/vsphere v2.8.1 (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.6.0... 
						- Installed citrix/citrix v0.6.0 (self-signed, key ID BD4BD0E690CB7D88)
					 

					
						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\_CCRLOnvSphere\CCOnvSphere - Creation&gt; terraform plan 
						data.vsphere_datacenter.datacenter: Reading... 
						data.vsphere_datacenter.datacenter: Read complete after 0s [id=datacenter-3] 
						data.vsphere_network.network: Reading... 
						data.vsphere_host.host: Reading... 
						data.vsphere_datastore.datastore: Reading... 
						data.vsphere_compute_cluster.cluster: Reading... 
						data.vsphere_virtual_machine.W2K22_Template: Reading... 
						data.vsphere_network.network: Read complete after 0s [id=network-12] 
						data.vsphere_datastore.datastore: Read complete after 0s [id=datastore-11] 
						data.vsphere_host.host: Read complete after 0s [id=host-8] 
						data.vsphere_compute_cluster.cluster: Read complete after 0s [id=domain-c17] 
						data.vsphere_virtual_machine.W2K22_Template: Read complete after 0s [id=422af676-c52e-0618-c5c1-19d7bba0f35f]
					 

					
						Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: 
						  + create 
						 &lt;= 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) 
						 &lt;= 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) 
						    }
					 

					
						  # 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) 
						    }
					 

					
						  # vsphere_content_library.contentlibrary will be created 
						  + resource "vsphere_content_library" "contentlibrary" { 
						      + description     = "TACG Content Library" 
						      + id              = (known after apply) 
						      + name            = "TACG-CL" 
						      + storage_backing = [ 
						          + "datastore-11", 
						        ] 
						    }
					 

					
						  # vsphere_virtual_machine.AVM-vm will be created 
						  + resource "vsphere_virtual_machine" "AVM-vm" { 
						      + annotation                              = (known after apply) 
						      + boot_retry_delay                        = 10000 
						      + change_version                          = (known after apply) 
						      + cpu_limit                               = -1 
						      + cpu_share_count                         = (known after apply) 
						      + cpu_share_level                         = "normal" 
						      + datastore_id                            = "datastore-11" 
						      + default_ip_address                      = (known after apply) 
						      + ept_rvi_mode                            = "automatic" 
						      + extra_config_reboot_required            = true 
						      + firmware                                = "efi" 
						      + force_power_off                         = true 
						      + guest_id                                = "windows2019srvNext_64Guest" 
						      + guest_ip_addresses                      = (known after apply) 
						      + hardware_version                        = (known after apply) 
						      + host_system_id                          = (known after apply) 
						      + hv_mode                                 = "hvAuto" 
						      + id                                      = (known after apply) 
						      + ide_controller_count                    = 2 
						      + imported                                = (known after apply) 
						      + latency_sensitivity                     = "normal" 
						      + memory                                  = 8192 
						      + memory_limit                            = -1 
						      + memory_share_count                      = (known after apply) 
						      + memory_share_level                      = "normal" 
						      + migrate_wait_timeout                    = 30 
						      + moid                                    = (known after apply) 
						      + name                                    = "TF-ONPRL-AVM" 
						      + num_cores_per_socket                    = 1 
						      + num_cpus                                = 2 
						      + power_state                             = (known after apply) 
						      + poweron_timeout                         = 300 
						      + reboot_required                         = (known after apply) 
						      + resource_pool_id                        = "resgroup-18" 
						      + run_tools_scripts_after_power_on        = true 
						      + run_tools_scripts_after_resume          = true 
						      + run_tools_scripts_before_guest_shutdown = true 
						      + run_tools_scripts_before_guest_standby  = true 
						      + sata_controller_count                   = 0 
						      + scsi_bus_sharing                        = "noSharing" 
						      + scsi_controller_count                   = 1 
						      + scsi_type                               = "pvscsi" 
						      + shutdown_wait_timeout                   = 3 
						      + storage_policy_id                       = (known after apply) 
						      + swap_placement_policy                   = "inherit" 
						      + sync_time_with_host                     = true 
						      + tools_upgrade_policy                    = "manual" 
						      + uuid                                    = (known after apply) 
						      + vapp_transport                          = (known after apply) 
						      + vmware_tools_status                     = (known after apply) 
						      + vmx_path                                = (known after apply) 
						      + wait_for_guest_ip_timeout               = 0 
						      + wait_for_guest_net_routable             = true 
						      + wait_for_guest_net_timeout              = 5
					 

					
						      + cdrom { 
						          + datastore_id   = "datastore-11" 
						          + device_address = (known after apply) 
						          + key            = (known after apply) 
						          + path           = "TF-CVAD-ISO/Citrix_Virtual_Apps_and_Desktops_7_2402_LTSR.iso" 
						        }
					 

					
						      + clone { 
						          + template_uuid = "422af676-c52e-0618-c5c1-19d7bba0f35f" 
						          + timeout       = 30
					 

					
						          + customize { 
						              + ipv4_gateway = "10.10.0.1" 
						              + timeout      = 60
					 

					
						              + network_interface { 
						                  + dns_server_list = [ 
						                      + "10.10.100.1", 
						                    ] 
						                  + ipv4_address    = "10.10.170.33" 
						                  + ipv4_netmask    = 16 
						                }
					 

					
						              + windows_options { 
						                  + admin_password        = (sensitive value) 
						                  + auto_logon            = true 
						                  + auto_logon_count      = 2 
						                  + computer_name         = "TACG-TF-VSPAVM" 
						                  + domain_admin_password = (sensitive value) 
						                  + domain_admin_user     = (sensitive value) 
						                  + full_name             = (sensitive value) 
						                  + join_domain           = (sensitive value) 
						                  + organization_name     = (sensitive value) 
						                  + run_once_command_list = [ 
						                      + "enable winrm", 
						                    ] 
						                  + time_zone             = 85 
						                } 
						            } 
						        }
					 

					
						      + disk { 
						          + attach            = false 
						          + controller_type   = "scsi" 
						          + datastore_id      = "&lt;computed&gt;" 
						          + device_address    = (known after apply) 
						          + disk_mode         = "persistent" 
						          + disk_sharing      = "sharingNone" 
						          + eagerly_scrub     = false 
						          + io_limit          = -1 
						          + io_reservation    = 0 
						          + io_share_count    = 0 
						          + io_share_level    = "normal" 
						          + keep_on_remove    = false 
						          + key               = 0 
						          + label             = "0" 
						          + path              = (known after apply) 
						          + size              = 60 
						          + storage_policy_id = (known after apply) 
						          + thin_provisioned  = true 
						          + unit_number       = 0 
						          + uuid              = (known after apply) 
						          + write_through     = false 
						        }
					 

					
						      + network_interface { 
						          + adapter_type          = "vmxnet3" 
						          + bandwidth_limit       = -1 
						          + bandwidth_reservation = 0 
						          + bandwidth_share_count = (known after apply) 
						          + bandwidth_share_level = "normal" 
						          + device_address        = (known after apply) 
						          + key                   = (known after apply) 
						          + mac_address           = (known after apply) 
						          + network_id            = "network-12" 
						        } 
						    }
					 

					
						  # vsphere_virtual_machine.CC1-vm will be created 
						  + resource "vsphere_virtual_machine" "CC1-vm" { 
						      + annotation                              = (known after apply) 
						      + boot_retry_delay                        = 10000 
						      + change_version                          = (known after apply) 
						      + cpu_limit                               = -1 
						      + cpu_share_count                         = (known after apply) 
						      + cpu_share_level                         = "normal" 
						      + datastore_id                            = "datastore-11" 
						      + default_ip_address                      = (known after apply) 
						      + ept_rvi_mode                            = "automatic" 
						      + extra_config_reboot_required            = true 
						      + firmware                                = "efi" 
						      + force_power_off                         = true 
						      + guest_id                                = "windows2019srvNext_64Guest" 
						      + guest_ip_addresses                      = (known after apply) 
						      + hardware_version                        = (known after apply) 
						      + host_system_id                          = (known after apply) 
						      + hv_mode                                 = "hvAuto" 
						      + id                                      = (known after apply) 
						      + ide_controller_count                    = 2 
						      + imported                                = (known after apply) 
						      + latency_sensitivity                     = "normal" 
						      + memory                                  = 8192 
						      + memory_limit                            = -1 
						      + memory_share_count                      = (known after apply) 
						      + memory_share_level                      = "normal" 
						      + migrate_wait_timeout                    = 30 
						      + moid                                    = (known after apply) 
						      + name                                    = "TF-ONPRL-CC1" 
						      + num_cores_per_socket                    = 1 
						      + num_cpus                                = 2 
						      + power_state                             = (known after apply) 
						      + poweron_timeout                         = 300 
						      + reboot_required                         = (known after apply) 
						      + resource_pool_id                        = "resgroup-18" 
						      + run_tools_scripts_after_power_on        = true 
						      + run_tools_scripts_after_resume          = true 
						      + run_tools_scripts_before_guest_shutdown = true 
						      + run_tools_scripts_before_guest_standby  = true 
						      + sata_controller_count                   = 0 
						      + scsi_bus_sharing                        = "noSharing" 
						      + scsi_controller_count                   = 1 
						      + scsi_type                               = "pvscsi" 
						      + shutdown_wait_timeout                   = 3 
						      + storage_policy_id                       = (known after apply) 
						      + swap_placement_policy                   = "inherit" 
						      + sync_time_with_host                     = true 
						      + tools_upgrade_policy                    = "manual" 
						      + uuid                                    = (known after apply) 
						      + vapp_transport                          = (known after apply) 
						      + vmware_tools_status                     = (known after apply) 
						      + vmx_path                                = (known after apply) 
						      + wait_for_guest_ip_timeout               = 0 
						      + wait_for_guest_net_routable             = true 
						      + wait_for_guest_net_timeout              = 5
					 

					
						      + cdrom { 
						          + datastore_id   = "datastore-11" 
						          + device_address = (known after apply) 
						          + key            = (known after apply) 
						          + path           = "TF-CVAD-ISO/Citrix_Virtual_Apps_and_Desktops_7_2402_LTSR.iso" 
						        }
					 

					
						      + clone { 
						          + template_uuid = "422af676-c52e-0618-c5c1-19d7bba0f35f" 
						          + timeout       = 30
					 

					
						          + customize { 
						              + ipv4_gateway = "10.10.0.1" 
						              + timeout      = 60
					 

					
						              + network_interface { 
						                  + dns_server_list = [ 
						                      + "10.10.100.1", 
						                    ] 
						                  + ipv4_address    = "10.10.170.31" 
						                  + ipv4_netmask    = 16 
						                }
					 

					
						              + windows_options { 
						                  + admin_password        = (sensitive value) 
						                  + auto_logon            = true 
						                  + auto_logon_count      = 2 
						                  + computer_name         = "TACG-TF-VSPCC1" 
						                  + domain_admin_password = (sensitive value) 
						                  + domain_admin_user     = (sensitive value) 
						                  + full_name             = (sensitive value) 
						                  + join_domain           = (sensitive value) 
						                  + organization_name     = (sensitive value) 
						                  + run_once_command_list = [ 
						                      + "enable winrm", 
						                    ] 
						                  + time_zone             = 85 
						                } 
						            } 
						        }
					 

					
						      + disk { 
						          + attach            = false 
						          + controller_type   = "scsi" 
						          + datastore_id      = "&lt;computed&gt;" 
						          + device_address    = (known after apply) 
						          + disk_mode         = "persistent" 
						          + disk_sharing      = "sharingNone" 
						          + eagerly_scrub     = false 
						          + io_limit          = -1 
						          + io_reservation    = 0 
						          + io_share_count    = 0 
						          + io_share_level    = "normal" 
						          + keep_on_remove    = false 
						          + key               = 0 
						          + label             = "0" 
						          + path              = (known after apply) 
						          + size              = 60 
						          + storage_policy_id = (known after apply) 
						          + thin_provisioned  = true 
						          + unit_number       = 0 
						          + uuid              = (known after apply) 
						          + write_through     = false 
						        }
					 

					
						      + network_interface { 
						          + adapter_type          = "vmxnet3" 
						          + bandwidth_limit       = -1 
						          + bandwidth_reservation = 0 
						          + bandwidth_share_count = (known after apply) 
						          + bandwidth_share_level = "normal" 
						          + device_address        = (known after apply) 
						          + key                   = (known after apply) 
						          + mac_address           = (known after apply) 
						          + network_id            = "network-12" 
						        } 
						    }
					 

					
						  # vsphere_virtual_machine.CC2-vm will be created 
						  + resource "vsphere_virtual_machine" "CC2-vm" { 
						      + annotation                              = (known after apply) 
						      + boot_retry_delay                        = 10000 
						      + change_version                          = (known after apply) 
						      + cpu_limit                               = -1 
						      + cpu_share_count                         = (known after apply) 
						      + cpu_share_level                         = "normal" 
						      + datastore_id                            = "datastore-11" 
						      + default_ip_address                      = (known after apply) 
						      + ept_rvi_mode                            = "automatic" 
						      + extra_config_reboot_required            = true 
						      + firmware                                = "efi" 
						      + force_power_off                         = true 
						      + guest_id                                = "windows2019srvNext_64Guest" 
						      + guest_ip_addresses                      = (known after apply) 
						      + hardware_version                        = (known after apply) 
						      + host_system_id                          = (known after apply) 
						      + hv_mode                                 = "hvAuto" 
						      + id                                      = (known after apply) 
						      + ide_controller_count                    = 2 
						      + imported                                = (known after apply) 
						      + latency_sensitivity                     = "normal" 
						      + memory                                  = 8192 
						      + memory_limit                            = -1 
						      + memory_share_count                      = (known after apply) 
						      + memory_share_level                      = "normal" 
						      + migrate_wait_timeout                    = 30 
						      + moid                                    = (known after apply) 
						      + name                                    = "TF-ONPRL-CC2" 
						      + num_cores_per_socket                    = 1 
						      + num_cpus                                = 2 
						      + power_state                             = (known after apply) 
						      + poweron_timeout                         = 300 
						      + reboot_required                         = (known after apply) 
						      + resource_pool_id                        = "resgroup-18" 
						      + run_tools_scripts_after_power_on        = true 
						      + run_tools_scripts_after_resume          = true 
						      + run_tools_scripts_before_guest_shutdown = true 
						      + run_tools_scripts_before_guest_standby  = true 
						      + sata_controller_count                   = 0 
						      + scsi_bus_sharing                        = "noSharing" 
						      + scsi_controller_count                   = 1 
						      + scsi_type                               = "pvscsi" 
						      + shutdown_wait_timeout                   = 3 
						      + storage_policy_id                       = (known after apply) 
						      + swap_placement_policy                   = "inherit" 
						      + sync_time_with_host                     = true 
						      + tools_upgrade_policy                    = "manual" 
						      + uuid                                    = (known after apply) 
						      + vapp_transport                          = (known after apply) 
						      + vmware_tools_status                     = (known after apply) 
						      + vmx_path                                = (known after apply) 
						      + wait_for_guest_ip_timeout               = 0 
						      + wait_for_guest_net_routable             = true 
						      + wait_for_guest_net_timeout              = 5
					 

					
						      + cdrom { 
						          + datastore_id   = "datastore-11" 
						          + device_address = (known after apply) 
						          + key            = (known after apply) 
						          + path           = "TF-CVAD-ISO/Citrix_Virtual_Apps_and_Desktops_7_2402_LTSR.iso" 
						        }
					 

					
						      + clone { 
						          + template_uuid = "422af676-c52e-0618-c5c1-19d7bba0f35f" 
						          + timeout       = 30
					 

					
						          + customize { 
						              + ipv4_gateway = "10.10.0.1" 
						              + timeout      = 60
					 

					
						              + network_interface { 
						                  + dns_server_list = [ 
						                      + "10.10.100.1", 
						                    ] 
						                  + ipv4_address    = "10.10.170.32" 
						                  + ipv4_netmask    = 16 
						                }
					 

					
						              + windows_options { 
						                  + admin_password        = (sensitive value) 
						                  + auto_logon            = true 
						                  + auto_logon_count      = 2 
						                  + computer_name         = "TACG-TF-VSPCC2" 
						                  + domain_admin_password = (sensitive value) 
						                  + domain_admin_user     = (sensitive value) 
						                  + full_name             = (sensitive value) 
						                  + join_domain           = (sensitive value) 
						                  + organization_name     = (sensitive value) 
						                  + run_once_command_list = [ 
						                      + "enable winrm", 
						                    ] 
						                  + time_zone             = 85 
						                } 
						            } 
						        }
					 

					
						      + disk { 
						          + attach            = false 
						          + controller_type   = "scsi" 
						          + datastore_id      = "&lt;computed&gt;" 
						          + device_address    = (known after apply) 
						          + disk_mode         = "persistent" 
						          + disk_sharing      = "sharingNone" 
						          + eagerly_scrub     = false 
						          + io_limit          = -1 
						          + io_reservation    = 0 
						          + io_share_count    = 0 
						          + io_share_level    = "normal" 
						          + keep_on_remove    = false 
						          + key               = 0 
						          + label             = "0" 
						          + path              = (known after apply) 
						          + size              = 60 
						          + storage_policy_id = (known after apply) 
						          + thin_provisioned  = true 
						          + unit_number       = 0 
						          + uuid              = (known after apply) 
						          + write_through     = false 
						        }
					 

					
						      + network_interface { 
						          + adapter_type          = "vmxnet3" 
						          + bandwidth_limit       = -1 
						          + bandwidth_reservation = 0 
						          + bandwidth_share_count = (known after apply) 
						          + bandwidth_share_level = "normal" 
						          + device_address        = (known after apply) 
						          + key                   = (known after apply) 
						          + mac_address           = (known after apply) 
						          + network_id            = "network-12" 
						        } 
						    } 
						(OUTPUT SHORTENED)
					 

					
						Plan: 6 to add, 0 to change, 0 to destroy.
					 

					
						Changes to Outputs: 
						  + terraform_data_BR_Read = (known after apply)
					 

					
						────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
					 

					
						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\_CCRLOnvSphere\CCOnvSphere - Creation&gt; terraform apply 
						data.vsphere_datacenter.datacenter: Reading... 
						data.vsphere_datacenter.datacenter: Read complete after 0s [id=datacenter-3] 
						data.vsphere_network.network: Reading... 
						data.vsphere_compute_cluster.cluster: Reading... 
						data.vsphere_datastore.datastore: Reading... 
						data.vsphere_host.host: Reading... 
						data.vsphere_virtual_machine.W2K22_Template: Reading... 
						data.vsphere_network.network: Read complete after 0s [id=network-12] 
						data.vsphere_datastore.datastore: Read complete after 0s [id=datastore-11] 
						data.vsphere_host.host: Read complete after 0s [id=host-8] 
						data.vsphere_compute_cluster.cluster: Read complete after 0s [id=domain-c17] 
						data.vsphere_virtual_machine.W2K22_Template: Read complete after 0s [id=422af676-c52e-0618-c5c1-19d7bba0f35f]
					 

					
						Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: 
						  + create 
						 &lt;= 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) 
						 &lt;= 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) 
						    }
					 

					
						  # 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) 
						    }
					 

					
						  # vsphere_content_library.contentlibrary will be created 
						  + resource "vsphere_content_library" "contentlibrary" { 
						      + description     = "TACG Content Library" 
						      + id              = (known after apply) 
						      + name            = "TACG-CL" 
						      + storage_backing = [ 
						          + "datastore-11", 
						        ] 
						    }
					 

					
						  # vsphere_virtual_machine.AVM-vm will be created 
						  + resource "vsphere_virtual_machine" "AVM-vm" { 
						      + annotation                              = (known after apply) 
						      + boot_retry_delay                        = 10000 
						      + change_version                          = (known after apply) 
						      + cpu_limit                               = -1 
						      + cpu_share_count                         = (known after apply) 
						      + cpu_share_level                         = "normal" 
						      + datastore_id                            = "datastore-11" 
						      + default_ip_address                      = (known after apply) 
						      + ept_rvi_mode                            = "automatic" 
						      + extra_config_reboot_required            = true 
						      + firmware                                = "efi" 
						      + force_power_off                         = true 
						      + guest_id                                = "windows2019srvNext_64Guest" 
						      + guest_ip_addresses                      = (known after apply) 
						      + hardware_version                        = (known after apply) 
						      + host_system_id                          = (known after apply) 
						      + hv_mode                                 = "hvAuto" 
						      + id                                      = (known after apply) 
						      + ide_controller_count                    = 2 
						      + imported                                = (known after apply) 
						      + latency_sensitivity                     = "normal" 
						      + memory                                  = 8192 
						      + memory_limit                            = -1 
						      + memory_share_count                      = (known after apply) 
						      + memory_share_level                      = "normal" 
						      + migrate_wait_timeout                    = 30 
						      + moid                                    = (known after apply) 
						      + name                                    = "TF-ONPRL-AVM" 
						      + num_cores_per_socket                    = 1 
						      + num_cpus                                = 2 
						      + power_state                             = (known after apply) 
						      + poweron_timeout                         = 300 
						      + reboot_required                         = (known after apply) 
						      + resource_pool_id                        = "resgroup-18" 
						      + run_tools_scripts_after_power_on        = true 
						      + run_tools_scripts_after_resume          = true 
						      + run_tools_scripts_before_guest_shutdown = true 
						      + run_tools_scripts_before_guest_standby  = true 
						      + sata_controller_count                   = 0 
						      + scsi_bus_sharing                        = "noSharing" 
						      + scsi_controller_count                   = 1 
						      + scsi_type                               = "pvscsi" 
						      + shutdown_wait_timeout                   = 3 
						      + storage_policy_id                       = (known after apply) 
						      + swap_placement_policy                   = "inherit" 
						      + sync_time_with_host                     = true 
						      + tools_upgrade_policy                    = "manual" 
						      + uuid                                    = (known after apply) 
						      + vapp_transport                          = (known after apply) 
						      + vmware_tools_status                     = (known after apply) 
						      + vmx_path                                = (known after apply) 
						      + wait_for_guest_ip_timeout               = 0 
						      + wait_for_guest_net_routable             = true 
						      + wait_for_guest_net_timeout              = 5
					 

					
						      + cdrom { 
						          + datastore_id   = "datastore-11" 
						          + device_address = (known after apply) 
						          + key            = (known after apply) 
						          + path           = "TF-CVAD-ISO/Citrix_Virtual_Apps_and_Desktops_7_2402_LTSR.iso" 
						        }
					 

					
						      + clone { 
						          + template_uuid = "422af676-c52e-0618-c5c1-19d7bba0f35f" 
						          + timeout       = 30
					 

					
						          + customize { 
						              + ipv4_gateway = "10.10.0.1" 
						              + timeout      = 60
					 

					
						              + network_interface { 
						                  + dns_server_list = [ 
						                      + "10.10.100.1", 
						                    ] 
						                  + ipv4_address    = "10.10.170.33" 
						                  + ipv4_netmask    = 16 
						                }
					 

					
						              + windows_options { 
						                  + admin_password        = (sensitive value) 
						                  + auto_logon            = true 
						                  + auto_logon_count      = 2 
						                  + computer_name         = "TACG-TF-VSPAVM" 
						                  + domain_admin_password = (sensitive value) 
						                  + domain_admin_user     = (sensitive value) 
						                  + full_name             = (sensitive value) 
						                  + join_domain           = (sensitive value) 
						                  + organization_name     = (sensitive value) 
						                  + run_once_command_list = [ 
						                      + "enable winrm", 
						                    ] 
						                  + time_zone             = 85 
						                } 
						            } 
						        }
					 

					
						      + disk { 
						          + attach            = false 
						          + controller_type   = "scsi" 
						          + datastore_id      = "&lt;computed&gt;" 
						          + device_address    = (known after apply) 
						          + disk_mode         = "persistent" 
						          + disk_sharing      = "sharingNone" 
						          + eagerly_scrub     = false 
						          + io_limit          = -1 
						          + io_reservation    = 0 
						          + io_share_count    = 0 
						          + io_share_level    = "normal" 
						          + keep_on_remove    = false 
						          + key               = 0 
						          + label             = "0" 
						          + path              = (known after apply) 
						          + size              = 60 
						          + storage_policy_id = (known after apply) 
						          + thin_provisioned  = true 
						          + unit_number       = 0 
						          + uuid              = (known after apply) 
						          + write_through     = false 
						        }
					 

					
						      + network_interface { 
						          + adapter_type          = "vmxnet3" 
						          + bandwidth_limit       = -1 
						          + bandwidth_reservation = 0 
						          + bandwidth_share_count = (known after apply) 
						          + bandwidth_share_level = "normal" 
						          + device_address        = (known after apply) 
						          + key                   = (known after apply) 
						          + mac_address           = (known after apply) 
						          + network_id            = "network-12" 
						        } 
						    }
					 

					
						  # vsphere_virtual_machine.CC1-vm will be created 
						  + resource "vsphere_virtual_machine" "CC1-vm" { 
						      + annotation                              = (known after apply) 
						      + boot_retry_delay                        = 10000 
						      + change_version                          = (known after apply) 
						      + cpu_limit                               = -1 
						      + cpu_share_count                         = (known after apply) 
						      + cpu_share_level                         = "normal" 
						      + datastore_id                            = "datastore-11" 
						      + default_ip_address                      = (known after apply) 
						      + ept_rvi_mode                            = "automatic" 
						      + extra_config_reboot_required            = true 
						      + firmware                                = "efi" 
						      + force_power_off                         = true 
						      + guest_id                                = "windows2019srvNext_64Guest" 
						      + guest_ip_addresses                      = (known after apply) 
						      + hardware_version                        = (known after apply) 
						      + host_system_id                          = (known after apply) 
						      + hv_mode                                 = "hvAuto" 
						      + id                                      = (known after apply) 
						      + ide_controller_count                    = 2 
						      + imported                                = (known after apply) 
						      + latency_sensitivity                     = "normal" 
						      + memory                                  = 8192 
						      + memory_limit                            = -1 
						      + memory_share_count                      = (known after apply) 
						      + memory_share_level                      = "normal" 
						      + migrate_wait_timeout                    = 30 
						      + moid                                    = (known after apply) 
						      + name                                    = "TF-ONPRL-CC1" 
						      + num_cores_per_socket                    = 1 
						      + num_cpus                                = 2 
						      + power_state                             = (known after apply) 
						      + poweron_timeout                         = 300 
						      + reboot_required                         = (known after apply) 
						      + resource_pool_id                        = "resgroup-18" 
						      + run_tools_scripts_after_power_on        = true 
						      + run_tools_scripts_after_resume          = true 
						      + run_tools_scripts_before_guest_shutdown = true 
						      + run_tools_scripts_before_guest_standby  = true 
						      + sata_controller_count                   = 0 
						      + scsi_bus_sharing                        = "noSharing" 
						      + scsi_controller_count                   = 1 
						      + scsi_type                               = "pvscsi" 
						      + shutdown_wait_timeout                   = 3 
						      + storage_policy_id                       = (known after apply) 
						      + swap_placement_policy                   = "inherit" 
						      + sync_time_with_host                     = true 
						      + tools_upgrade_policy                    = "manual" 
						      + uuid                                    = (known after apply) 
						      + vapp_transport                          = (known after apply) 
						      + vmware_tools_status                     = (known after apply) 
						      + vmx_path                                = (known after apply) 
						      + wait_for_guest_ip_timeout               = 0 
						      + wait_for_guest_net_routable             = true 
						      + wait_for_guest_net_timeout              = 5
					 

					
						      + cdrom { 
						          + datastore_id   = "datastore-11" 
						          + device_address = (known after apply) 
						          + key            = (known after apply) 
						          + path           = "TF-CVAD-ISO/Citrix_Virtual_Apps_and_Desktops_7_2402_LTSR.iso" 
						        }
					 

					
						      + clone { 
						          + template_uuid = "422af676-c52e-0618-c5c1-19d7bba0f35f" 
						          + timeout       = 30
					 

					
						          + customize { 
						              + ipv4_gateway = "10.10.0.1" 
						              + timeout      = 60
					 

					
						              + network_interface { 
						                  + dns_server_list = [ 
						                      + "10.10.100.1", 
						                    ] 
						                  + ipv4_address    = "10.10.170.31" 
						                  + ipv4_netmask    = 16 
						                }
					 

					
						              + windows_options { 
						                  + admin_password        = (sensitive value) 
						                  + auto_logon            = true 
						                  + auto_logon_count      = 2 
						                  + computer_name         = "TACG-TF-VSPCC1" 
						                  + domain_admin_password = (sensitive value) 
						                  + domain_admin_user     = (sensitive value) 
						                  + full_name             = (sensitive value) 
						                  + join_domain           = (sensitive value) 
						                  + organization_name     = (sensitive value) 
						                  + run_once_command_list = [ 
						                      + "enable winrm", 
						                    ] 
						                  + time_zone             = 85 
						                } 
						            } 
						        }
					 

					
						      + disk { 
						          + attach            = false 
						          + controller_type   = "scsi" 
						          + datastore_id      = "&lt;computed&gt;" 
						          + device_address    = (known after apply) 
						          + disk_mode         = "persistent" 
						          + disk_sharing      = "sharingNone" 
						          + eagerly_scrub     = false 
						          + io_limit          = -1 
						          + io_reservation    = 0 
						          + io_share_count    = 0 
						          + io_share_level    = "normal" 
						          + keep_on_remove    = false 
						          + key               = 0 
						          + label             = "0" 
						          + path              = (known after apply) 
						          + size              = 60 
						          + storage_policy_id = (known after apply) 
						          + thin_provisioned  = true 
						          + unit_number       = 0 
						          + uuid              = (known after apply) 
						          + write_through     = false 
						        }
					 

					
						      + network_interface { 
						          + adapter_type          = "vmxnet3" 
						          + bandwidth_limit       = -1 
						          + bandwidth_reservation = 0 
						          + bandwidth_share_count = (known after apply) 
						          + bandwidth_share_level = "normal" 
						          + device_address        = (known after apply) 
						          + key                   = (known after apply) 
						          + mac_address           = (known after apply) 
						          + network_id            = "network-12" 
						        } 
						    }
					 

					
						  # vsphere_virtual_machine.CC2-vm will be created 
						  + resource "vsphere_virtual_machine" "CC2-vm" { 
						      + annotation                              = (known after apply) 
						      + boot_retry_delay                        = 10000 
						      + change_version                          = (known after apply) 
						      + cpu_limit                               = -1 
						      + cpu_share_count                         = (known after apply) 
						      + cpu_share_level                         = "normal" 
						      + datastore_id                            = "datastore-11" 
						      + default_ip_address                      = (known after apply) 
						      + ept_rvi_mode                            = "automatic" 
						      + extra_config_reboot_required            = true 
						      + firmware                                = "efi" 
						      + force_power_off                         = true 
						      + guest_id                                = "windows2019srvNext_64Guest" 
						      + guest_ip_addresses                      = (known after apply) 
						      + hardware_version                        = (known after apply) 
						      + host_system_id                          = (known after apply) 
						      + hv_mode                                 = "hvAuto" 
						      + id                                      = (known after apply) 
						      + ide_controller_count                    = 2 
						      + imported                                = (known after apply) 
						      + latency_sensitivity                     = "normal" 
						      + memory                                  = 8192 
						      + memory_limit                            = -1 
						      + memory_share_count                      = (known after apply) 
						      + memory_share_level                      = "normal" 
						      + migrate_wait_timeout                    = 30 
						      + moid                                    = (known after apply) 
						      + name                                    = "TF-ONPRL-CC2" 
						      + num_cores_per_socket                    = 1 
						      + num_cpus                                = 2 
						      + power_state                             = (known after apply) 
						      + poweron_timeout                         = 300 
						      + reboot_required                         = (known after apply) 
						      + resource_pool_id                        = "resgroup-18" 
						      + run_tools_scripts_after_power_on        = true 
						      + run_tools_scripts_after_resume          = true 
						      + run_tools_scripts_before_guest_shutdown = true 
						      + run_tools_scripts_before_guest_standby  = true 
						      + sata_controller_count                   = 0 
						      + scsi_bus_sharing                        = "noSharing" 
						      + scsi_controller_count                   = 1 
						      + scsi_type                               = "pvscsi" 
						      + shutdown_wait_timeout                   = 3 
						      + storage_policy_id                       = (known after apply) 
						      + swap_placement_policy                   = "inherit" 
						      + sync_time_with_host                     = true 
						      + tools_upgrade_policy                    = "manual" 
						      + uuid                                    = (known after apply) 
						      + vapp_transport                          = (known after apply) 
						      + vmware_tools_status                     = (known after apply) 
						      + vmx_path                                = (known after apply) 
						      + wait_for_guest_ip_timeout               = 0 
						      + wait_for_guest_net_routable             = true 
						      + wait_for_guest_net_timeout              = 5
					 

					
						      + cdrom { 
						          + datastore_id   = "datastore-11" 
						          + device_address = (known after apply) 
						          + key            = (known after apply) 
						          + path           = "TF-CVAD-ISO/Citrix_Virtual_Apps_and_Desktops_7_2402_LTSR.iso" 
						        }
					 

					
						      + clone { 
						          + template_uuid = "422af676-c52e-0618-c5c1-19d7bba0f35f" 
						          + timeout       = 30
					 

					
						          + customize { 
						              + ipv4_gateway = "10.10.0.1" 
						              + timeout      = 60
					 

					
						              + network_interface { 
						                  + dns_server_list = [ 
						                      + "10.10.100.1", 
						                    ] 
						                  + ipv4_address    = "10.10.170.32" 
						                  + ipv4_netmask    = 16 
						                }
					 

					
						              + windows_options { 
						                  + admin_password        = (sensitive value) 
						                  + auto_logon            = true 
						                  + auto_logon_count      = 2 
						                  + computer_name         = "TACG-TF-VSPCC2" 
						                  + domain_admin_password = (sensitive value) 
						                  + domain_admin_user     = (sensitive value) 
						                  + full_name             = (sensitive value) 
						                  + join_domain           = (sensitive value) 
						                  + organization_name     = (sensitive value) 
						                  + run_once_command_list = [ 
						                      + "enable winrm", 
						                    ] 
						                  + time_zone             = 85 
						                } 
						            } 
						        }
					 

					
						      + disk { 
						          + attach            = false 
						          + controller_type   = "scsi" 
						          + datastore_id      = "&lt;computed&gt;" 
						          + device_address    = (known after apply) 
						          + disk_mode         = "persistent" 
						          + disk_sharing      = "sharingNone" 
						          + eagerly_scrub     = false 
						          + io_limit          = -1 
						          + io_reservation    = 0 
						          + io_share_count    = 0 
						          + io_share_level    = "normal" 
						          + keep_on_remove    = false 
						          + key               = 0 
						          + label             = "0" 
						          + path              = (known after apply) 
						          + size              = 60 
						          + storage_policy_id = (known after apply) 
						          + thin_provisioned  = true 
						          + unit_number       = 0 
						          + uuid              = (known after apply) 
						          + write_through     = false 
						        }
					 

					
						      + network_interface { 
						          + adapter_type          = "vmxnet3" 
						          + bandwidth_limit       = -1 
						          + bandwidth_reservation = 0 
						          + bandwidth_share_count = (known after apply) 
						          + bandwidth_share_level = "normal" 
						          + device_address        = (known after apply) 
						          + key                   = (known after apply) 
						          + mac_address           = (known after apply) 
						          + network_id            = "network-12" 
						        } 
						    } 
						Plan: 6 to add, 0 to change, 0 to destroy.
					 

					
						Changes to Outputs: 
						  + terraform_data_BR_Read = (known after apply)
					 

					
						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
					 

					
						local_file.GetBearerToken: Creating... 
						local_file.GetBearerToken: Creation complete after 0s [id=5b56a3020e023483ec3670687ed1da82c83c4090] 
						terraform_data.GetBT: Creating... 
						terraform_data.GetBT: Provisioning with 'local-exec'... 
						terraform_data.GetBT (local-exec): Executing: ["PowerShell" "-File" "./GetBT.ps1"] 
						vsphere_content_library.contentlibrary: Creating... 
						vsphere_virtual_machine.CC2-vm: Creating... 
						vsphere_virtual_machine.AVM-vm: Creating... 
						vsphere_virtual_machine.CC1-vm: Creating... 
						vsphere_content_library.contentlibrary: Creation complete after 0s [id=f1c0e9bd-4243-41a9-915d-b35393c94658] 
						terraform_data.GetBT: Creation complete after 3s [id=dc90d7cb-7d3d-188b-f0c8-5ac67a19de1a] 
						data.local_file.Retrieve_BT: Reading... 
						data.local_file.Retrieve_BT: Read complete after 0s [id=4d8d99529a791a5e66bdb70b3485ec992c775c31] 
						vsphere_virtual_machine.CC2-vm: Still creating... [10s elapsed] 
						vsphere_virtual_machine.AVM-vm: Still creating... [10s elapsed] 
						vsphere_virtual_machine.CC1-vm: Still creating... [10s elapsed] 
						vsphere_virtual_machine.CC2-vm: Still creating... [20s elapsed] 
						vsphere_virtual_machine.AVM-vm: Still creating... [20s elapsed] 
						vsphere_virtual_machine.CC1-vm: Still creating... [20s elapsed] 
						vsphere_virtual_machine.CC2-vm: Still creating... [30s elapsed] 
						vsphere_virtual_machine.AVM-vm: Still creating... [30s elapsed] 
						vsphere_virtual_machine.CC1-vm: Still creating... [30s elapsed] 
						vsphere_virtual_machine.CC2-vm: Still creating... [40s elapsed] 
						vsphere_virtual_machine.AVM-vm: Still creating... [40s elapsed] 
						vsphere_virtual_machine.CC1-vm: Still creating... [40s elapsed] 
						vsphere_virtual_machine.CC2-vm: Still creating... [50s elapsed] 
						vsphere_virtual_machine.AVM-vm: Still creating... [50s elapsed] 
						vsphere_virtual_machine.CC1-vm: Still creating... [50s elapsed] 
						vsphere_virtual_machine.CC2-vm: Still creating... [1m0s elapsed] 
						vsphere_virtual_machine.AVM-vm: Still creating... [1m0s elapsed] 
						vsphere_virtual_machine.CC1-vm: Still creating... [1m0s elapsed] 
						vsphere_virtual_machine.CC2-vm: Still creating... [1m10s elapsed] 
						vsphere_virtual_machine.AVM-vm: Still creating... [1m10s elapsed] 
						vsphere_virtual_machine.CC1-vm: Still creating... [1m10s elapsed] 
						vsphere_virtual_machine.CC2-vm: Still creating... [1m20s elapsed] 
						vsphere_virtual_machine.AVM-vm: Still creating... [1m20s elapsed] 
						vsphere_virtual_machine.CC1-vm: Still creating... [1m20s elapsed] 
						vsphere_virtual_machine.CC2-vm: Still creating... [1m30s elapsed] 
						vsphere_virtual_machine.AVM-vm: Still creating... [1m30s elapsed] 
						vsphere_virtual_machine.CC1-vm: Still creating... [1m30s elapsed] 
						vsphere_virtual_machine.CC2-vm: Still creating... [1m40s elapsed] 
						vsphere_virtual_machine.AVM-vm: Still creating... [1m40s elapsed] 
						vsphere_virtual_machine.CC1-vm: Still creating... [1m40s elapsed] 
						vsphere_virtual_machine.CC2-vm: Still creating... [1m50s elapsed] 
						vsphere_virtual_machine.AVM-vm: Still creating... [1m50s elapsed] 
						vsphere_virtual_machine.CC1-vm: Still creating... [1m50s elapsed] 
						vsphere_virtual_machine.CC2-vm: Still creating... [2m0s elapsed] 
						vsphere_virtual_machine.AVM-vm: Still creating... [2m0s elapsed] 
						vsphere_virtual_machine.CC1-vm: Still creating... [2m0s elapsed] 
						vsphere_virtual_machine.CC2-vm: Still creating... [2m10s elapsed] 
						vsphere_virtual_machine.CC1-vm: Still creating... [2m10s elapsed] 
						vsphere_virtual_machine.AVM-vm: Still creating... [2m10s elapsed] 
						vsphere_virtual_machine.CC2-vm: Creation complete after 2m17s [id=422a7888-189a-9b92-e504-1aabded68d06] 
						vsphere_virtual_machine.CC1-vm: Creation complete after 2m17s [id=422ad2b8-3e31-26f8-80b5-bf6a207dd097] 
						vsphere_virtual_machine.AVM-vm: Creation complete after 2m17s [id=422a80d3-9512-3ae9-497e-c4320c9810a2]
					 

					
						Apply complete! Resources: 6 added, 0 changed, 0 destroyed.
					 

					
						Outputs:
					 

					
						terraform_data_BR_Read = "CWSAuth bearer=…aQ" 
						PS C:\_TACG\_CCRLOnvSphere\CCOnvSphere - Creation&gt; 
						 
					 
				
			
		
	

	
		 
	 

	
		Module 2: Install and Configure all Resources needed for the Resource Location on vSphere 8 
		 
	

	
		
			IMPORTANT: 
		 

		
			This module can only be run on the previously created Administrative VM, as the deployment of Modules 2 and 3 relies heavily on WinRM! Please install Terraform on the Administrative VM before trying to run the scripts! 
			 
		 
	

	
		This module is split into the following configuration parts:
	 

	
		
			Configuring the three previously created Virtual Machines on vSphere 8:

			
				
					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
				
			
		
	

	
		 
	 

	
		
			NOTE: 
		 

		
			This module is complex as many automated actions and different entities are created and run on the VMs. It contains a mixture of PowerShell scripts, REST-API calls, and Terraform scripts to achieve a working deployment. 
			 
		 
	

	
		Please ensure you have configured the variables according to your needs using the corresponding .auto.tfvars.json file.
	 

	
		Terraform runs various scripts before creating the CCs' 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:
	 

	
		Examples of necessary scripts: 
		At first, Terraform writes the configuration file without the Resource Location ID:
	 

	
		
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}		
		
			
				
					
						
							
								### 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"
							 

							
								 
							 

							
								}
							 
						
					
				
			

			
				Afterwards, the Resource Location is created:
			 

			
				
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}				
				
					
						
							
								
									
										### 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  = &lt;&lt;-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"
									 

									
										}
									 
								
							
						
					
				
			

			
				 
			 

			
				
					IMPORTANT: 
					 
					The Terraform script will pause for some time after creating the Resource Location! 
					This is because of time constraints in the back-end. 
				 

				
					Creating the Zone related to the Resource Location needs some time—we have seen delays of up to 8 minutes before the Zone was created. 
					If the script proceeds too fast, the Zone ID readout will fail, and the Terraform script will not be able to continue successfully! 
					 
				 
			

			
				 
				After installing further prerequisites, Terraform runs more PowerShell scripts to get all the needed IDs and updates the configuration file for the CWC Installer. 
				The CWC Installer then installs the Cloud Connector software and registers the CCs to Citrix Cloud.
			 

			
				Before running Terraform, we cannot see the Resource Location: 
				
			 

			
				No Cloud Connector-Software is installed on the Cloud Connector VMs: 
				
			 

			
				After installing the Cloud Connector software, the Cloud Connectivity Test runs successfully: 
				
			 

			
				The Resource Location is now available: 
				
			 

			
				 
			 

			
				
					IMPORTANT:
				 

				
					Before starting the Terraform workflow, please ensure 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 td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:"Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}				
				
					
						
							
								
									 
								 

								
									PS C:\_tacg\_CCRLOnvSphere\CCOnvSphere - Installation&gt; 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.6.0"...
								 

								
									- Finding hashicorp/vsphere versions matching "&gt;= 2.7.0"...
								 

								
									- Finding latest version of hashicorp/local...
								 

								
									- Finding latest version of hashicorp/time...
								 

								
									- Finding latest version of hashicorp/null...
								 

								
									- Installing hashicorp/vsphere v2.8.1...
								 

								
									- Installed hashicorp/vsphere v2.8.1 (signed by HashiCorp)
								 

								
									- Installing hashicorp/local v2.5.1...
								 

								
									- Installed hashicorp/local v2.5.1 (signed by HashiCorp)
								 

								
									- Installing hashicorp/time v0.11.2...
								 

								
									- Installed hashicorp/time v0.11.2 (signed by HashiCorp)
								 

								
									- Installing hashicorp/null v3.2.2...
								 

								
									- Installed hashicorp/null v3.2.2 (signed by HashiCorp)
								 

								
									- Installing mastercard/restapi v1.18.2...
								 

								
									- Installed mastercard/restapi v1.18.2 (self-signed, key ID DCB8C431D71C30AB)
								 

								
									- Installing citrix/citrix v0.6.0...
								 

								
									- Installed citrix/citrix v0.6.0 (self-signed, key ID BD4BD0E690CB7D88)
								 

								
									 
								 

								
									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\_CCRLOnvSphere\CCOnvSphere - Installation&gt; terraform plan
								 

								
									 
								 

								
									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 = "3600s"
								 

								
									      + id              = (known after apply)
								 

								
									    }
								 

								
									 
								 

								
									  # time_sleep.wait_1800_seconds_CC2 will be created
								 

								
									  + resource "time_sleep" "wait_1800_seconds_CC2" {
								 

								
									      + create_duration = "3600s"
								 

								
									      + 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: 28 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\_CCRLOnvSphere\CCOnvSphere - Installation&gt; terraform apply
								 

								
									 
								 

								
									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 = "3600s"
								 

								
									      + id              = (known after apply)
								 

								
									    }
								 

								
									 
								 

								
									  # time_sleep.wait_1800_seconds_CC2 will be created
								 

								
									  + resource "time_sleep" "wait_1800_seconds_CC2" {
								 

								
									      + create_duration = "3600s"
								 

								
									      + 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: 28 to add, 0 to change, 0 to destroy.                                                                                                                                                                                                                                                                                                                                                                         Do you want to perform these actions?                                                                                                                                                                       Terraform will perform the actions described above.                                                                                                                                                     null_resource.ExecuteInstallPoSHSDKOnAVM (local-exec): Executing: ["PowerShell" "-Command" " c:/temp/xdinst/DATA/InstallPoSHSDKOnAVM.ps1"]
								 

								
									local_file.CWC-Configuration: Creating...
								 

								
									local_file.InstallCWCOnCC: Creating...
								 

								
									local_file.Log: Creating...
								 

								
									local_file.RestartCC: Creating...
								 

								
									local_file.InstallPoSHSDKOnAVM: Creating...
								 

								
									local_file.RestartCC: Creation complete after 0s [id=606daeae95b6e85b5e1c2a68bae8841fe8d9cbc4]
								 

								
									local_file.Log: Creation complete after 0s [id=d725ce92ca8335439a5d83acf47f3ca2c957a515]
								 

								
									local_file.CWC-Configuration: Creation complete after 0s [id=0257d2b92f197fa4d043b6cd4c4959be284dddc2]
								 

								
									local_file.InstallCWCOnCC: Creation complete after 0s [id=f2a65867695c5c51a1b9df42f4c1a7aa5d3a553f]
								 

								
									local_file.InstallPoSHSDKOnAVM: Creation complete after 0s [id=45b8a434e6ee1d259932d6a358c3a46a9d1bf536]
								 

								
									null_resource.UploadRequiredComponentsToCC2: Creating...
								 

								
									local_file.GetBearerToken: Creating...
								 

								
									null_resource.UploadRequiredComponentsToCC2: Provisioning with 'file'...
								 

								
									local_file.GetBearerToken: Creation complete after 0s [id=0c179d18ae6c2518d31d13e395b22c0a7e1273b5]
								 

								
									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]
								 

								
									null_resource.ExecuteInstallPoSHSDKOnAVM: Still creating... [30s elapsed]
								 

								
									null_resource.ExecuteInstallPoSHSDKOnAVM: Still creating... [40s elapsed]
								 

								
									null_resource.ExecuteInstallPoSHSDKOnAVM: Still creating... [50s elapsed]
								 

								
									null_resource.ExecuteInstallPoSHSDKOnAVM: Still creating... [1m0s elapsed]
								 

								
									null_resource.ExecuteInstallPoSHSDKOnAVM: Creation complete after 1m9s [id=6422740967799617564]
								 

								
									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 2s [id=d96eaba7-02f8-a4e2-4ff2-096eccd50288]
								 

								
									local_file.CreateRLScript: Creating...
								 

								
									local_file.CreateRLScript: Creation complete after 0s [id=ef81cd64e2984ecb37259d9c54471a84fa2e6d52]
								 

								
									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 2s [id=6107f15a-fe36-3fb5-39fb-afa10d1c5e00]
								 

								
									time_sleep.wait_60_seconds: Creating...
								 

								
									time_sleep.wait_900_seconds: Creating...
								 

								
									time_sleep.wait_900_seconds: Still creating... [10s elapsed] 
									 
								 

								
									(Output shortened)... 
									 
								 

								
									time_sleep.wait_60_seconds: Creation complete after 1m0s [id=2024-06-12T06:59:57Z]
								 

								
									local_file.GetSiteIDScript: Creating...
								 

								
									local_file.GetSiteIDScript: Creation complete after 0s [id=b6be87d7eeed66b79fa8b73087b022ec61a02963]
								 

								
									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"]
								 

								
									time_sleep.wait_900_seconds: Still creating... [1m0s elapsed]
								 

								
									terraform_data.SiteID: Creation complete after 1s [id=0b8bcf57-03c7-c0f4-d244-4df764410a0b]
								 

								
									time_sleep.wait_900_seconds: Still creating... [1m10s elapsed] 
									 
								 

								
									(Output shortened)...
								 

								
									 
									time_sleep.wait_900_seconds: Creation complete after 15m0s [id=2024-06-12T07:13:57Z]
								 

								
									local_file.GetZoneIDScript: Creating...
								 

								
									local_file.GetZoneIDScript: Creation complete after 0s [id=115f23c21fede21831afd56be9015c26642c22ad]
								 

								
									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=ae253c04-354c-1eb5-9613-a27e7cdcc64b]
								 

								
									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=8ffc95a9-5b43-1a9f-b2c9-e74c3aba6361]
								 

								
									local_file.CreateValidCWCOnAVM: Creating...
								 

								
									local_file.CreateValidCWCOnAVM: Creation complete after 0s [id=a604e262e4e39a6b3ffda411193dae893a1d0b4f]
								 

								
									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 3s [id=c4f0a5b5-89bc-2fff-e487-730fb0e62566]
								 

								
									null_resource.UploadRequiredComponentsToCC1: Creating...
								 

								
									null_resource.UploadRequiredComponentsToCC1: Provisioning with 'file'...
								 

								
									#&lt; CLIXML
								 

								
									&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
								 

								
									&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadRequiredComponentsToCC1: Provisioning with 'file'...
								 

								
									#&lt; CLIXML
								 

								
									&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
								 

								
									&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadRequiredComponentsToCC1: Provisioning with 'file'...
								 

								
									#&lt; CLIXML
								 

								
									&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
								 

								
									&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadRequiredComponentsToCC1: Provisioning with 'file'...
								 

								
									#&lt; CLIXML
								 

								
									&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
								 

								
									&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadRequiredComponentsToCC1: Provisioning with 'file'...
								 

								
									#&lt; CLIXML
								 

								
									&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
								 

								
									&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadRequiredComponentsToCC1: Provisioning with 'file'...
								 

								
									#&lt; CLIXML
								 

								
									&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadRequiredComponentsToCC1: Still creating... [10s elapsed]
								 

								
									#&lt; CLIXML
								 

								
									&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadRequiredComponentsToCC1: Creation complete after 10s [id=2061159527767408743]
								 

								
									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.10.170.31
								 

								
									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!
								 

								
									#&lt; CLIXML
								 

								
									&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
								 

								
									&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;
								 

								
									null_resource.CallRequiredScriptsOnCC1 (remote-exec): C:\Users\Administrator&gt;powershell -File c:/temp/xdinst/DATA/InstallCWCOnCC.ps1
								 

								
									null_resource.CallRequiredScriptsOnCC1: Still creating... [10s elapsed]
								 

								
									#&lt; CLIXML
								 

								
									&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
								 

								
									&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.CallRequiredScriptsOnCC1: Creation complete after 16s [id=3131811345469631998]
								 

								
									time_sleep.wait_1800_seconds_CC1: Creating...
								 

								
									time_sleep.wait_1800_seconds_CC1: Still creating... [10s elapsed]
								 

								
									time_sleep.wait_1800_seconds_CC1: Still creating... [20s elapsed]
								 

								
									time_sleep.wait_1800_seconds_CC1: Creation complete after 1h0m0s [id=2024-06-12T08:14:28Z]
								 

								
									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.10.170.31
								 

								
									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
								 

								
									null_resource.CallRebootScriptOnCC1 (remote-exec): Connected!
								 

								
									#&lt; CLIXML
								 

								
									&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
								 

								
									&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;
								 

								
									Apply complete! Resources: 28 added, 0 changed, 0 destroyed.
								 

								
									 
								 

								
									PS C:\_Tacg\_CCRLOnvSphere\CCOnvSphere - Installation&gt;
								 
							
						
					
				
			

			
				This Terraform workflow 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
				
			

			
				The environment is now ready to deploy a Hosting Connection and a Hosting Connection Pool using Module 3.
			 

			
				
					NOTE:
				 

				
					Deploying a Machine Catalog and a Delivery Group after creation of a Resource Location in Module 1 is out-of-scope of this guide. 
					You can find detailed instructions for creating a Machine Catalog and a Delivery Group in these guides:
				 

				
					Citrix DaaS and Terraform - Automatic Deployment of a Resource Location on Amazon EC2
				 

				
					Citrix DaaS and Terraform - Automatic Deployment of a Resource Location on Google Cloud Platform (GCP)
				 

				
					Citrix DaaS and Terraform - Automatic Deployment of a Resource Location on Microsoft Azure 
					 
				 
			

			
				 
			 

			
				Module 3: Deploy a Hosting Connection and a Hosting Connection Pool on vSphere 8 and Citrix Cloud
			

			
				This module is split into the following configuration parts:
			 

			
				
					Creating a Hosting Connection and a Hosting Connection Pool
				
			

			
				The Terraform configuration contains some idle time slots to ensure that background operations on Amazon EC2 or the VMs can be completed before the following configuration steps occur. 
				We have seen different elapsed configuration times related to varying loads on vSphere! 
			 

			
				Before Terraform can create the Hosting Connection and the Hosting Connection Pool, It must retrieve the Site ID and Zone ID of the newly created Resource Location. 
			 

			
				As the Citrix Terraform Provider currently lacks Cloud-level functionalities implemented, Terraform needs PowerShell scripts to retrieve the IDs. It created the necessary scripts with all the required variables, saved them, and ran them in Module 2. 
				 
			 

			
				
					NOTE: 
				 

				
					The Citrix Remote PowerShell SDK must be installed on the Administrative Machine, as Terraform relies on the SDK! 
					 
				 
			

			
				After retrieving the IDs, Terraform configures a Hosting Connection and a Hosting Connection Pool associated with the Hypervisor Connection. 
			 

			
				Before running Terraform, no Terraform-related entities are available: 
				
			 

			
				 
			 

			
				
					IMPORTANT:
				 

				
					Before starting the Terraform workflow, please ensure 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 td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:"Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}				
				
					
						
							
								
									PS C:\_TACG\_CCRLOnvSphere\CCOnvSphere - CCStuff&gt; terraform init
								 

								
									 
								 

								
									Initializing the backend...
								 

								
									 
								 

								
									Initializing provider plugins...
								 

								
									- Finding citrix/citrix versions matching "0.6.0"...
								 

								
									- Finding hashicorp/vsphere versions matching "&gt;= 2.7.0"...
								 

								
									- Finding mastercard/restapi versions matching "1.18.2"...
								 

								
									- Finding latest version of hashicorp/local...
								 

								
									- Installing hashicorp/vsphere v2.8.1...
								 

								
									- Installed hashicorp/vsphere v2.8.1 (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.6.0...
								 

								
									- Installed citrix/citrix v0.6.0 (self-signed, key ID BD4BD0E690CB7D88)
								 

								
									 
								 

								
									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 required changes 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\_CCRLOnvSphere\CCOnvSphere - CCStuff&gt; terraform plan
								 

								
									data.local_file.LoadZoneID: Reading...
								 

								
									data.local_file.LoadZoneID: Read complete after 0s [id=3d42f4003172f5acf56185f33af0709ff33f7f27]
								 

								
									data.vsphere_datacenter.datacenter: Reading...
								 

								
									data.vsphere_datacenter.datacenter: Read complete after 0s [id=datacenter-3]
								 

								
									data.vsphere_host.host: Reading...
								 

								
									data.vsphere_network.network: Reading...
								 

								
									data.vsphere_datastore.datastore: Reading...
								 

								
									data.vsphere_compute_cluster.cluster: Reading...
								 

								
									data.vsphere_network.network: Read complete after 0s [id=network-12]
								 

								
									data.vsphere_datastore.datastore: Read complete after 0s [id=datastore-11]
								 

								
									data.vsphere_host.host: Read complete after 0s [id=host-8]
								 

								
									data.vsphere_compute_cluster.cluster: Read complete after 0s [id=domain-c17]
								 

								
									 
								 

								
									Terraform used the selected providers to generate the following execution plan. Resource actions are indicated by the
								 

								
									following symbols:
								 

								
									  + create
								 

								
									 
								 

								
									Terraform will perform the following actions:
								 

								
									 
								 

								
									  # citrix_vsphere_hypervisor.CreateHypervisorConnection will be created
								 

								
									  + resource "citrix_vsphere_hypervisor" "CreateHypervisorConnection" {
								 

								
									      + addresses                                = [
								 

								
									          + "https://vcxxxxx.xxxxxxxx.xxxx/sdk",
								 

								
									        ]
								 

								
									      + id                                       = (known after apply)
								 

								
									      + max_absolute_active_actions              = 40
								 

								
									      + max_absolute_new_actions_per_minute      = 10
								 

								
									      + max_power_actions_percentage_of_machines = 20
								 

								
									      + name                                     = "TACG-TF-ONP-vSphere-HypConn"
								 

								
									      + password                                 = (sensitive)
								 

								
									      + password_format                          = "PlainText"
								 

								
									      + ssl_thumbprints                          = [
								 

								
									          + "0377BE89D96540C66402F6301611A93B7221F14E",
								 

								
									        ]
								 

								
									      + username                                 = "(sensitive)"
								 

								
									      + zone                                     = "73fde083-a652-4db5-a150-238a8bed2dcc"
								 

								
									    }
								 

								
									 
								 

								
									  # citrix_vsphere_hypervisor_resource_pool.CreateHypervisorPool will be created
								 

								
									  + resource "citrix_vsphere_hypervisor_resource_pool" "CreateHypervisorPool" {
								 

								
									      + cluster                   = {
								 

								
									          + cluster_name = "TACG"
								 

								
									          + datacenter   = (sensitive value)
								 

								
									        }
								 

								
									      + hypervisor                = (known after apply)
								 

								
									      + id                        = (known after apply)
								 

								
									      + name                      = "TACG-TF-ONP-vSphere-HypConnPool"
								 

								
									      + networks                  = [
								 

								
									          + (sensitive value),
								 

								
									        ]
								 

								
									      + storage                   = [
								 

								
									          + {
								 

								
									              + storage_name = (sensitive value)
								 

								
									              + superseded   = false
								 

								
									            },
								 

								
									        ]
								 

								
									      + temporary_storage         = [
								 

								
									          + {
								 

								
									              + storage_name = (sensitive value)
								 

								
									              + superseded   = false
								 

								
									            },
								 

								
									        ]
								 

								
									      + use_local_storage_caching = false
								 

								
									    }
								 

								
									 
								 

								
									Plan: 2 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 precisely these actions if
								 

								
									you run "terraform apply" now.
								 

								
									PS C:\_TACG\_CCRLOnvSphere\CCOnvSphere - CCStuff&gt; terraform apply
								 

								
									data.local_file.LoadZoneID: Reading...
								 

								
									data.local_file.LoadZoneID: Read complete after 0s [id=3d42f4003172f5acf56185f33af0709ff33f7f27]
								 

								
									data.vsphere_datacenter.datacenter: Reading...
								 

								
									data.vsphere_datacenter.datacenter: Read complete after 0s [id=datacenter-3]
								 

								
									data.vsphere_compute_cluster.cluster: Reading...
								 

								
									data.vsphere_network.network: Reading...
								 

								
									data.vsphere_datastore.datastore: Reading...
								 

								
									data.vsphere_host.host: Reading...
								 

								
									data.vsphere_network.network: Read complete after 0s [id=network-12]
								 

								
									data.vsphere_datastore.datastore: Read complete after 0s [id=datastore-11]
								 

								
									data.vsphere_host.host: Read complete after 0s [id=host-8]
								 

								
									data.vsphere_compute_cluster.cluster: Read complete after 0s [id=domain-c17]
								 

								
									 
								 

								
									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_vsphere_hypervisor.CreateHypervisorConnection will be created
								 

								
									  + resource "citrix_vsphere_hypervisor" "CreateHypervisorConnection" {
								 

								
									      + addresses                                = [
								 

								
									          + "https://vcxxxxx.xxxxxxxx.xxxx/sdk",
								 

								
									        ]
								 

								
									      + id                                       = (known after apply)
								 

								
									      + max_absolute_active_actions              = 40
								 

								
									      + max_absolute_new_actions_per_minute      = 10
								 

								
									      + max_power_actions_percentage_of_machines = 20
								 

								
									      + name                                     = "TACG-TF-ONP-vSphere-HypConn"
								 

								
									      + password                                 = (sensitive)
								 

								
									      + password_format                          = "PlainText"
								 

								
									      + ssl_thumbprints                          = [
								 

								
									          + "0377BE89D96540C66402F6301611A93B7221F14E",
								 

								
									        ]
								 

								
									      + username                                 = "(sensitive)"
								 

								
									      + zone                                     = "73fde083-a652-4db5-a150-238a8bed2dcc"
								 

								
									    }
								 

								
									 
								 

								
									  # citrix_vsphere_hypervisor_resource_pool.CreateHypervisorPool will be created
								 

								
									  + resource "citrix_vsphere_hypervisor_resource_pool" "CreateHypervisorPool" {
								 

								
									      + cluster                   = {
								 

								
									          + cluster_name = "TACG"
								 

								
									          + datacenter   = (sensitive value)
								 

								
									        }
								 

								
									      + hypervisor                = (known after apply)
								 

								
									      + id                        = (known after apply)
								 

								
									      + name                      = "TACG-TF-ONP-vSphere-HypConnPool"
								 

								
									      + networks                  = [
								 

								
									          + (sensitive value),
								 

								
									        ]
								 

								
									      + storage                   = [
								 

								
									          + {
								 

								
									              + storage_name = (sensitive value)
								 

								
									              + superseded   = false
								 

								
									            },
								 

								
									        ]
								 

								
									      + temporary_storage         = [
								 

								
									          + {
								 

								
									              + storage_name = (sensitive value)
								 

								
									              + superseded   = false
								 

								
									            },
								 

								
									        ]
								 

								
									      + use_local_storage_caching = false
								 

								
									    }
								 

								
									 
								 

								
									Plan: 2 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_vsphere_hypervisor.CreateHypervisorConnection: Creating...
								 

								
									citrix_vsphere_hypervisor.CreateHypervisorConnection: Still creating... [10s elapsed]
								 

								
									citrix_vsphere_hypervisor.CreateHypervisorConnection: Still creating... [20s elapsed]
								 

								
									citrix_vsphere_hypervisor.CreateHypervisorConnection: Still creating... [30s elapsed]
								 

								
									citrix_vsphere_hypervisor.CreateHypervisorConnection: Still creating... [40s elapsed]
								 

								
									citrix_vsphere_hypervisor.CreateHypervisorConnection: Still creating... [50s elapsed]
								 

								
									citrix_vsphere_hypervisor.CreateHypervisorConnection: Still creating... [1m0s elapsed]
								 

								
									citrix_vsphere_hypervisor.CreateHypervisorConnection: Still creating... [1m10s elapsed]
								 

								
									citrix_vsphere_hypervisor.CreateHypervisorConnection: Creation complete after 1m11s [id=46ac750c-3dcc-44ac-8418-021a787175c2]
								 

								
									citrix_vsphere_hypervisor_resource_pool.CreateHypervisorPool: Creating...
								 

								
									citrix_vsphere_hypervisor_resource_pool.CreateHypervisorPool: Still creating... [10s elapsed]
								 

								
									citrix_vsphere_hypervisor_resource_pool.CreateHypervisorPool: Creation complete after 16s [id=64a80085-3b8e-4191-8c79-4e33c02a7157]
								 

								
									 
								 

								
									Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
								 

								
									PS C:\_TACG\_CCRLOnvSphere\CCOnvSphere - CCStuff&gt;
								 
							
						
					
				
			

			
				Terraform successfully created the Hosting Connection and a Hosting Connection Pool associated with the Hosting Connection: 
				
			 

			
				 
				This Terraform workflow completes the creation and configuration of all initial resources:
			 

			
				
					Creating a Hosting Connection in Citrix Cloud
				
				
					Creating a Hosting Connection Resource Pool in Citrix Cloud
				
			

			
				 
				That concludes our guide "Using Terraform to deploy a Citrix DaaS Resource Location on vSphere 8".
			 

			
				You can find the related guide "Using Terraform to deploy Citrix Virtual Apps and Desktops 2402 LTSR on vSphere 8" on Citrix Tech Zone: https://community.citrix.com/tech-zone/build/deployment-guides/terraform-cvad-vsphere8
			 

			
				 
			 

			
				Appendix
			

			
				
					Disclaimer
				 

				
					EXCEPT WHERE EXPRESSLY PROVIDED OTHERWISE BY CLOUD SOFTWARE GROUP, THE CONTENT IN THIS DOCUMENT IS PROVIDED “AS IS” AND CLOUD SOFTWARE GROUP HEREBY DISCLAIMS ALL EXPRESS OR IMPLIED REPRESENTATIONS, WARRANTIES, GUARANTIES, AND CONDITIONS, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES OR CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD TO BE LEGALLY INVALID. CLOUD SOFTWARE GROUP MAKES NO REPRESENTATIONS, WARRANTIES, GUARANTIES, OR CONDITIONS AS TO THE QUALITY, SUITABILITY, TRUTH, ACCURACY, OR COMPLETENESS OF ANY OF THE CONTENT CONTAINED ON THE WEBSITE.
				 

				
					The above disclaimer applies to any damages, liability, or injuries caused by any failure of performance, error, omission, interruption, deletion, defect, delay in operation or transmission, computer virus, communication line failure, theft or destruction of or unauthorized access to, alteration of, or use, whether for breach of contract, tort, negligence or any other cause of action. 
					 
				 
			

			
				Module 1
			

			
				CCOnvSphere-Creation-provider.tf
			 

			
				
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}				
				
					
						
							
								
									
										
											# Terraform deployment of a Citrix Cloud Resource Location on VMware vSphere
										

										
											## Definition of all required Terraform providers
										
										 

										
											terraform {
										

										
											    required_version = "&gt;= 1.7.3"
										
										 

										
											  required_providers {
										

										
											    vsphere = {
										

										
											      source  = "hashicorp/vsphere"
										

										
											      version = "&gt;= 2.7.0"
										

										
											    }
										
										 

										
											    restapi = {
										

										
											      source = "Mastercard/restapi"
										

										
											      version = "1.18.2"
										

										
											    }
										
										 

										
											    citrix = {
										

										
											      source  = "citrix/citrix"
										

										
											      version = "=0.6.0"
										

										
											    }
										
										 

										
											  }
										

										
											}
										
										 

										
											provider "citrix" {
										

										
											  customer_id   = "${var.CC_CustomerID}"  
										

										
											  client_id     = "${var.CC_APIKey-ClientID}"  
										

										
											  client_secret = "${var.CC_APIKey-ClientSecret}"  
										

										
											}
										
										 

										
											provider "vsphere" {
										

										
											  user                 = var.vsphere_user
										

										
											  password             = var.vsphere_pw
										

										
											  vsphere_server       = var.vsphere_url
										

										
											  vim_keep_alive       = var.vsphere_vim_keep_alive
										

										
											  api_timeout          = var.vsphere_api_timeout
										

										
											  allow_unverified_ssl = true
										

										
											}
										
									
								
							
						
					

					
						 
					 

					
						CCOnvSphere-Creation-Create.tf
					 

					
						
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}						
						
							
								
									
										
											
												
													# Terraform deployment of a Citrix Cloud Resource Location on VMware vSphere
												

												
													## Creation of all required entities - Networking
												
												 

												
													### Get all relevant vSphere entitiy data
												

												
													data "vsphere_datacenter" "datacenter" {
												

												
													  name          = var.vsphere_datacenter
												

												
													}
												
												 

												
													data "vsphere_datastore" "datastore" {
												

												
													  name          = var.vsphere_datastore
												

												
													  datacenter_id = data.vsphere_datacenter.datacenter.id
												

												
													}
												
												 

												
													data "vsphere_network" "network" {
												

												
													  name          = var.vsphere_network
												

												
													  datacenter_id = data.vsphere_datacenter.datacenter.id
												

												
													}
												
												 

												
													data "vsphere_host" "host" {
												

												
													  name          = var.vsphere_host
												

												
													  datacenter_id = data.vsphere_datacenter.datacenter.id
												

												
													}
												
												 

												
													### If a cluster is available, uncomment this block
												

												
													data "vsphere_compute_cluster" "cluster" {
												

												
													name          = "TACG"
												

												
													datacenter_id = data.vsphere_datacenter.datacenter.id
												

												
													}
												
												 

												
													### If no cluster is available (stand-alone host), use this block
												

												
													#data "vsphere_resource_pool" "pool" {
												

												
													#  name            = join("",[data.vsphere_host.host.name,"/Resources"])
												

												
													#  datacenter_id   = data.vsphere_datacenter.datacenter.id
												

												
													#}
												
												 

												
													### Create a Content Library for storage of all needed stuff
												

												
													resource "vsphere_content_library" "contentlibrary" {
												

												
													  name            = var.vsphere_contentlibrary_name
												

												
													  description     = var.vsphere_contentlibrary_description
												

												
													  storage_backing = [data.vsphere_datastore.datastore.id]
												

												
													}
												
												 

												
													/* #### Create a Content Library for storage of all needed stuff
												

												
													resource "vsphere_content_library_item" "contentlibraryitem_iso" {
												

												
													  name            = ""
												

												
													  library_id      = [ vsphere_content_library.contentlibrary.id ]
												

												
													}
												
												 

												
													#### Create a Content Library for storage of all needed stuff
												

												
													resource "vsphere_content_library_item" "contentlibraryitem" {
												

												
													  name            = ""
												

												
													  library_id      = [ vsphere_content_library.contentlibrary.id ]
												

												
													}  */
												
												 

												
													data "vsphere_virtual_machine" "W2K22_Template" {
												

												
													  name          = var.vsphere_W2K22_template_name
												

												
													  datacenter_id = data.vsphere_datacenter.datacenter.id
												

												
													}
												
												 

												
													### Create CC1-VM
												

												
													resource "vsphere_virtual_machine" "CC1-vm" {
												

												
													 # depends_on = [ vsphere_file.CVAD_Upload_Iso ]
												

												
													  name             = var.vsphere_CC1_vmname
												

												
													  resource_pool_id = data.vsphere_compute_cluster.cluster.resource_pool_id
												

												
													  datastore_id     = data.vsphere_datastore.datastore.id
												

												
													  num_cpus         = var.vsphere_CC1_cpus
												

												
													  memory           = var.vsphere_CC1_ram
												

												
													  guest_id         = data.vsphere_virtual_machine.W2K22_Template.guest_id
												

												
													  scsi_type        = data.vsphere_virtual_machine.W2K22_Template.scsi_type
												

												
													  firmware         = "efi"
												

												
													  
												

												
													  
												

												
													  network_interface {
												

												
													    network_id   = data.vsphere_network.network.id
												

												
													    adapter_type = data.vsphere_virtual_machine.W2K22_Template.network_interface_types[0]
												

												
													  }
												
												 

												
													  disk {
												

												
													    label             = var.vsphere_CC1_disk_name
												
												 

												
													    ### If you want to use the same disk size and type as hte template, uncomment this block
												

												
													    size             = data.vsphere_virtual_machine.W2K22_Template.disks.0.size
												

												
													    thin_provisioned = data.vsphere_virtual_machine.W2K22_Template.disks.0.thin_provisioned
												

												
													    
												

												
													    ### If you want to override the disk size and disk provisioning type, uncomment this block and set the corresponding variables
												

												
													    #size             = var.vsphere_CC1_disk_size
												

												
													    #thin_provisioned = var.vsphere_CC1_disk_provtype
												

												
													  }
												
												 

												
													    cdrom {
												

												
													    datastore_id              = data.vsphere_datastore.datastore.id
												

												
													    path                      = var.vsphere_cvad_iso_destination_path
												

												
													  }
												
												 

												
													  clone {
												

												
													    template_uuid = data.vsphere_virtual_machine.W2K22_Template.id
												
												 

												
													    customize {
												

												
													      
												

												
													      ### If you want to use the provider variables to customize the server, uncomment this block
												

												
													      windows_options {
												

												
													        computer_name               = var.vsphere_CC1_hostname
												

												
													        admin_password              = var.vsphere_local_admin_pw
												

												
													        join_domain                 = var.vsphere_domainname
												

												
													        domain_admin_user           = var.vsphere_domain_admin_user
												

												
													        domain_admin_password       = var.vsphere_domain_admin_pw
												

												
													        full_name                   = var.vsphere_orgowner
												

												
													        organization_name           = var.vsphere_orgname
												

												
													        run_once_command_list       = var.vsphere_CC1_command_list
												

												
													        auto_logon                  = var.vsphere_autologon
												

												
													        auto_logon_count            = var.vsphere_autologon_count
												

												
													      }
												
												 

												
													      ### If you want to use a Sysprep file, uncomment this block
												

												
													      # windows_sysprep_text = file("${path.module}/sysprep.xml")
												
												 

												
													      network_interface {
												

												
													        ipv4_address        = var.vsphere_CC1_ipv4
												

												
													        ipv4_netmask        = var.vsphere_CC_ipv4netmask #24
												

												
													        dns_server_list     = var.vsphere_CC_dns
												

												
													      }
												

												
													      ipv4_gateway          = var.vsphere_CC_ipv4_gateway
												

												
													      timeout               = 60
												

												
													    }
												

												
													  }
												
												 

												
													}
												
												 

												
													### Create CC2-VM
												

												
													resource "vsphere_virtual_machine" "CC2-vm" {
												

												
													  # depends_on = [ vsphere_file.CVAD_Upload_Iso ]
												

												
													  name             = var.vsphere_CC2_vmname
												

												
													  resource_pool_id = data.vsphere_compute_cluster.cluster.resource_pool_id
												

												
													  datastore_id     = data.vsphere_datastore.datastore.id
												

												
													  num_cpus         = var.vsphere_CC2_cpus
												

												
													  memory           = var.vsphere_CC2_ram
												

												
													  guest_id         = data.vsphere_virtual_machine.W2K22_Template.guest_id
												

												
													  scsi_type        = data.vsphere_virtual_machine.W2K22_Template.scsi_type
												

												
													  firmware         = "efi"
												

												
													  
												

												
													  
												

												
													  network_interface {
												

												
													    network_id   = data.vsphere_network.network.id
												

												
													    adapter_type = data.vsphere_virtual_machine.W2K22_Template.network_interface_types[0]
												

												
													  }
												
												 

												
													  disk {
												

												
													    label             = var.vsphere_CC2_disk_name
												
												 

												
													    ### If you want to use the same disk size and type as hte template, uncomment this block
												

												
													    size             = data.vsphere_virtual_machine.W2K22_Template.disks.0.size
												

												
													    thin_provisioned = data.vsphere_virtual_machine.W2K22_Template.disks.0.thin_provisioned
												

												
													    
												

												
													    ### If you want to override the disk size and disk provisioning type, uncomment this block and set the corresponding variables
												

												
													    #size             = var.vsphere_CC2_disk_size
												

												
													    #thin_provisioned = var.vsphere_CC2_disk_provtype
												

												
													  }
												

												
													  cdrom {
												

												
													    datastore_id              = data.vsphere_datastore.datastore.id
												

												
													    path                      = var.vsphere_cvad_iso_destination_path
												

												
													  }
												
												 

												
													  clone {
												

												
													    template_uuid = data.vsphere_virtual_machine.W2K22_Template.id
												
												 

												
													    customize {
												

												
													      
												

												
													      ### If you want to use the provider variables to customize the server, uncomment this block
												

												
													      windows_options {
												

												
													        computer_name               = var.vsphere_CC2_hostname
												

												
													        join_domain                 = var.vsphere_domainname
												

												
													        admin_password              = var.vsphere_local_admin_pw
												

												
													        domain_admin_user           = var.vsphere_domain_admin_user
												

												
													        domain_admin_password       = var.vsphere_domain_admin_pw
												

												
													        full_name                   = var.vsphere_orgowner
												

												
													        organization_name           = var.vsphere_orgname
												

												
													        run_once_command_list       = var.vsphere_CC2_command_list
												

												
													        auto_logon                  = var.vsphere_autologon
												

												
													        auto_logon_count            = var.vsphere_autologon_count
												

												
													      }
												
												 

												
													      ### If you want to use a Sysprep file, uncomment this block
												

												
													      # windows_sysprep_text = file("${path.module}/sysprep.xml")
												
												 

												
													      network_interface {
												

												
													        ipv4_address        = var.vsphere_CC2_ipv4
												

												
													        ipv4_netmask        = var.vsphere_CC_ipv4netmask #24
												

												
													        dns_server_list     = var.vsphere_CC_dns
												

												
													      }
												

												
													      ipv4_gateway          = var.vsphere_CC_ipv4_gateway
												

												
													      timeout               = 60
												

												
													    }
												

												
													  }
												
												 

												
													}
												
												 

												
													### Create ADMIN-VM
												

												
													resource "vsphere_virtual_machine" "AVM-vm" {
												

												
													  # depends_on = [ vsphere_file.CVAD_Upload_Iso ]
												

												
													  name             = var.vsphere_AVM_vmname
												

												
													  resource_pool_id = data.vsphere_compute_cluster.cluster.resource_pool_id
												

												
													  datastore_id     = data.vsphere_datastore.datastore.id
												

												
													  num_cpus         = var.vsphere_AVM_cpus
												

												
													  memory           = var.vsphere_AVM_ram
												

												
													  guest_id         = data.vsphere_virtual_machine.W2K22_Template.guest_id
												

												
													  scsi_type        = data.vsphere_virtual_machine.W2K22_Template.scsi_type
												

												
													  firmware         = "efi"
												

												
													  
												

												
													  
												

												
													  network_interface {
												

												
													    network_id   = data.vsphere_network.network.id
												

												
													    adapter_type = data.vsphere_virtual_machine.W2K22_Template.network_interface_types[0]
												

												
													  }
												
												 

												
													  disk {
												

												
													    label             = var.vsphere_AVM_disk_name
												
												 

												
													    ### If you want to use the same disk size and type as hte template, uncomment this block
												

												
													    size             = data.vsphere_virtual_machine.W2K22_Template.disks.0.size
												

												
													    thin_provisioned = data.vsphere_virtual_machine.W2K22_Template.disks.0.thin_provisioned
												

												
													    
												

												
													    ### If you want to override the disk size and disk provisioning type, uncomment this block and set the corresponding variables
												

												
													    #size             = var.vsphere_CC2_disk_size
												

												
													    #thin_provisioned = var.vsphere_CC2_disk_provtype
												

												
													  }
												

												
													  cdrom {
												

												
													    datastore_id              = data.vsphere_datastore.datastore.id
												

												
													    path                      = var.vsphere_cvad_iso_destination_path
												

												
													  }
												
												 

												
													  clone {
												

												
													    template_uuid = data.vsphere_virtual_machine.W2K22_Template.id
												
												 

												
													    customize {
												

												
													      
												

												
													      ### If you want to use the provider variables to customize the server, uncomment this block
												

												
													      windows_options {
												

												
													        computer_name               = var.vsphere_AVM_hostname
												

												
													        join_domain                 = var.vsphere_domainname
												

												
													        admin_password              = var.vsphere_local_admin_pw
												

												
													        domain_admin_user           = var.vsphere_domain_admin_user
												

												
													        domain_admin_password       = var.vsphere_domain_admin_pw
												

												
													        full_name                   = var.vsphere_orgowner
												

												
													        organization_name           = var.vsphere_orgname
												

												
													        run_once_command_list       = var.vsphere_AVM_command_list
												

												
													        auto_logon                  = var.vsphere_autologon
												

												
													        auto_logon_count            = var.vsphere_autologon_count
												

												
													      }
												
												 

												
													      ### If you want to use a Sysprep file, uncomment this block
												

												
													      # windows_sysprep_text = file("${path.module}/sysprep.xml")
												
												 

												
													      network_interface {
												

												
													        ipv4_address        = var.vsphere_AVM_ipv4
												

												
													        ipv4_netmask        = var.vsphere_CC_ipv4netmask #24
												

												
													        dns_server_list     = var.vsphere_CC_dns
												

												
													      }
												

												
													      ipv4_gateway          = var.vsphere_CC_ipv4_gateway
												

												
													      timeout               = 60
												

												
													    }
												

												
													  }
												

												
													 
												
											
										
									
								
							

							
								 
								Module 2
							

							
								CCOnvSphere-Install-provider.tf
							 

							
								
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}								
								
									
										
											
												
													
														
															
																
																	# Terraform deployment of a Citrix Cloud Resource Location on VMware vSphere
																

																
																	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"
																

																
																	} 
																
																 

																
																	### Create PowerShell file for installing the Citrix Remote PoSH SDK on AVM 
																

																
																	resource "local_file" "InstallPoSHSDKOnAVM" {
																

																
																	content  = &lt;&lt;-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 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  = &lt;&lt;-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  = &lt;&lt;-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  = &lt;&lt;-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  = &lt;&lt;-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  = &lt;&lt;-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_corr.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 valid CWC.JSON file
																

																
																	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  = &lt;&lt;-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_corr.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  = &lt;&lt;-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 = [ terraform_data.ExecuteCreateValidCWCOnAVM ]
																

																
																	 connection {
																

																
																	    type            = var.Provisioner_Type
																

																
																	    user            = var.Provisioner_Admin-Username
																

																
																	    password        = var.Provisioner_Admin-Password
																

																
																	    host            = var.Provisioner_CC1-IP
																

																
																	    timeout         = var.Provisioner_Timeout
																
																 

																
																	  }
																
																 

																
																	###### Upload Cloud Connector configuration file to CC1
																

																
																	  provisioner "file" {
																

																
																	    source      = "${var.CC_Install_LogPath}/DATA/cwc_corr.json"
																

																
																	    destination = "${var.CC_Install_LogPath}/DATA/cwc_corr.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            = var.Provisioner_CC1-IP
																

																
																	    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 = [ terraform_data.ExecuteCreateValidCWCOnAVM ]
																

																
																	 connection {
																

																
																	    type            = var.Provisioner_Type
																

																
																	    user            = var.Provisioner_Admin-Username
																

																
																	    password        = var.Provisioner_Admin-Password
																

																
																	    host            = var.Provisioner_CC2-IP
																

																
																	    timeout         = var.Provisioner_Timeout
																
																 

																
																	  }
																
																 

																
																	###### Upload Cloud Connector configuration file to CC2
																

																
																	  provisioner "file" {
																

																
																	    source      = "${var.CC_Install_LogPath}/DATA/cwc_corr.json"
																

																
																	    destination = "${var.CC_Install_LogPath}/DATA/cwc_corr.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            = var.Provisioner_CC2-IP
																

																
																	    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            = var.Provisioner_CC1-IP
																

																
																	    timeout         = var.Provisioner_Timeout
																
																 

																
																	  }
																

																
																	 
																

																
																	  provisioner "remote-exec" {
																

																
																	    inline = [
																

																
																	      "powershell -File ${var.CC_Install_LogPath}/DATA/RestartCC.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            = var.Provisioner_CC2-IP
																

																
																	    timeout         = var.Provisioner_Timeout
																
																 

																
																	  }
																

																
																	 
																

																
																	  provisioner "remote-exec" {
																

																
																	    inline = [
																

																
																	      "powershell -File ${var.CC_Install_LogPath}/DATA/RestartCC.ps1"
																

																
																	    ]
																

																
																	  } 
																

																
																	}  
																
															
														
													
												
											
										
									

									
										 
										Module 3
									

									
										CCOnvSphere-CCStuff-CreateCCEntities.tf
									 

									
										
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}										
										
											
												
													
														
															
																
																	
																		# 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"
																	

																	
																		}
																	
																	 

																	
																		### Get all relevant vSphere entitiy data
																	

																	
																		data "vsphere_datacenter" "datacenter" {
																	

																	
																		  name          = var.vsphere_datacenter
																	

																	
																		}
																	
																	 

																	
																		data "vsphere_datastore" "datastore" {
																	

																	
																		  name          = var.vsphere_datastore
																	

																	
																		  datacenter_id = data.vsphere_datacenter.datacenter.id
																	

																	
																		}
																	
																	 

																	
																		data "vsphere_network" "network" {
																	

																	
																		  name          = var.vsphere_network
																	

																	
																		  datacenter_id = data.vsphere_datacenter.datacenter.id
																	

																	
																		}
																	
																	 

																	
																		data "vsphere_host" "host" {
																	

																	
																		  name          = var.vsphere_host
																	

																	
																		  datacenter_id = data.vsphere_datacenter.datacenter.id
																	

																	
																		}
																	
																	 

																	
																		### If a cluster is available, uncomment this block
																	

																	
																		data "vsphere_compute_cluster" "cluster" {
																	

																	
																		name          = "TACG"
																	

																	
																		datacenter_id = data.vsphere_datacenter.datacenter.id
																	

																	
																		}
																	
																	 

																	
																		#### Creating the Hypervisor Connection
																	

																	
																		resource "citrix_vsphere_hypervisor" "CreateHypervisorConnection" {
																	

																	
																		  depends_on = [ data.local_file.LoadZoneID ]
																	

																	
																		    name                        = "${var.CC_vSphere-HypConn-Name}"
																	

																	
																		    zone                        = data.local_file.LoadZoneID.content
																	

																	
																		    username                    = "${var.CC_vSphere-HypConn-UN}"
																	

																	
																		    password                    = "${var.CC_vSphere-HypConn-PW}"
																	

																	
																		    password_format             = "PlainText"
																	

																	
																		    addresses                   = [ "https://XXXXXX.XXXXXXXXXXXX/sdk", ]
																	

																	
																		    ssl_thumbprints             = [ "03XXXXXXXXXXXX4E", ]
																	

																	
																		} 
																	
																	 

																	
																		#### Creating the Hypervisor Resource Pool
																	

																	
																		resource "citrix_vsphere_hypervisor_resource_pool" "CreateHypervisorPool" {
																	

																	
																		  depends_on = [ citrix_vsphere_hypervisor.CreateHypervisorConnection ]
																	

																	
																		    name                        = "${var.CC_vSphere-HypConnPool-Name}"
																	

																	
																		    hypervisor                  = citrix_vsphere_hypervisor.CreateHypervisorConnection.id
																	

																	
																		    cluster                     = {
																	

																	
																		                  datacenter    = data.vsphere_datacenter.datacenter.name
																	

																	
																		                  cluster_name  = data.vsphere_compute_cluster.cluster.name
																	

																	
																		 
																	

																	
																		    }
																	

																	
																		    networks                    = [ data.vsphere_network.network.name, ]
																	

																	
																		    storage                     = [
																	

																	
																		                                    {
																	

																	
																		                                    storage_name = data.vsphere_datastore.datastore.name
																	

																	
																		                                    }
																	

																	
																		                                  ]
																	

																	
																		    temporary_storage           = [ 
																	

																	
																		                                    {
																	

																	
																		                                    storage_name = data.vsphere_datastore.datastore.name
																	

																	
																		                                    }
																	

																	
																		                                  ]                     
																	

																	
																		 
																	
																
															
														
													
												
											

											
												 
												Example of Variable definitions:
											

											
												
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}												
												
													
														
															
																
																	
																		
																			
																				
																					# Variables for Citrix Cloud Resource Location on vSphere 8
																				

																				
																					## Definition of Global Variables
																				

																				
																					variable "CC_Install_LogPath" {
																				

																				
																					  type        = string
																				

																				
																					  description = "REQUIRED: Path where Installation should happen"
																				

																				
																					}
																				
																				 

																				
																					## Definition of Hypervisoe and Hypervisor Connection Variables 
																				

																				
																					### Reference https://github.com/citrix/terraform-provider-citrix
																				

																				
																					variable "CC_vSphere-HypConn-Name" {
																				

																				
																					  type        = string
																				

																				
																					  description = "REQUIRED: Name of the Hypervisor Connection"
																				

																				
																					}
																				
																				 

																				
																					variable "CC_vSphere-HypConn-UN" {
																				

																				
																					  type        = string

																					
																						
																							  sensitive   = true
																						
																					
																				

																				
																					  description = "REQUIRED: Credentials of the Hypervisor Connection"
																				

																				
																					}
																				
																				 

																				
																					variable "CC_vSphere-HypConn-PW" {
																				

																				
																					  type        = string

																					
																						
																							  sensitive   = true
																						
																					
																				

																				
																					  description = "REQUIRED: Credentials of the Hypervisor Connection"
																				

																				
																					}
																				
																				 

																				
																					variable "CC_vSphere-HypConn-IP" {
																				

																				
																					  type        = list(string)
																				

																				
																					  description = "REQUIRED: IPs for the Hypervisor Connection"
																				

																				
																					}
																				
																				 

																				
																					variable "CC_vSphere-HypConnPool-Name" {
																				

																				
																					  type        = string
																				

																				
																					  description = "REQUIRED: Name of the Hypervisor Resource Pool"
																				

																				
																					}
																				
																				 

																				
																					variable "CC_vSphere-HypConnPool-Host" {
																				

																				
																					  type        = string
																				

																				
																					  description = "REQUIRED: Name of the Hypervisor Resource Pool"
																				

																				
																					}
																				
																			
																		
																	
																
															
														
													
												
											

											
												 
												Example of setting the Variables:
											

											
												
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}												
												
													
														
															
																
																	
																		
																			
																				
																					... 
																					"CC_Install_LogPath":"c:/temp/xdinst",
																				

																				
																					"CC_vSphere-HypConn-Name":"TACG-TF-ONP-vSphere-HypConn",
																				

																				
																					"CC_vSphere-HypConn-UN":"XXXXXXXXXX",
																				

																				
																					"CC_vSphere-HypConn-PW":"XXXXXXXXXX",
																				

																				
																					"CC_vSphere-HypConn-IP": ["https://XXXXXXXXXX/sdk"],
																				

																				
																					"CC_vSphere-HypConnPool-Host":"10.10.110.12",
																				

																				
																					"CC_vSphere-HypConnPool-Name":"TACG-TF-ONP-vSphere-HypConnPool", 
																					...]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_07/image.png.8ef6bd443904fafe699c5c5f7b548a21.png" length="59890" type="image/png"/><pubDate>Mon, 01 Jul 2024 11:48:00 +0000</pubDate></item><item><title>Deployment Guide: Creating a Windows 11-based Machine Catalog using Citrix Machine Creation Services on vSphere 8</title><link>https://community.stage.citrix.com/tech-zone/build/deployment-guides/w11-mcs-vsphere8/</link><description><![CDATA[Creating a Windows 11-based Machine Catalog using Citrix Machine Creation Services on vSphere 8 
	 



	Overview



	This guide should help to deploy a Windows 11-based Machine Catalog using Citrix Machine Creation Services on vSphere 8.
 


	We will show deployments using the following:
 


	
		Citrix Virtual Apps and Desktop´s WebStudio (version 2402 LTSR)
	
	
		Citrix Cloud Studio
	
	
		PowerShell
	
	
		REST-API
	
	
		Terraform
	



	 
 


	Prerequisites on vSphere 8



	Deploying Windows 11-based Virtual Machines on vSphere 8 requires some prerequisites on vSphere 8. 
	 
 


	IMPORTANT: 
	 
	Windows 11 requires TPM 2.0 for installation. 
	Running Windows 11 as a virtual machine requires a virtual Trusted Platform Module (vTPM) to be present. 
	 
	Find more details on Windows 11 requirements at https://docs.microsoft.com/en-us/windows/whats-new/windows-11-requirements. 
	 
	Please find more details about the Trusted Platform Module (TPM) and how Windows uses it for access control and Authentication at https://learn.microsoft.com/en-us/windows/security/hardware-security/tpm/trusted-platform-module-overview.  
	 
 


	 
 


	What Is a Trusted Platform Module (TPM)



	The Trusted Platform Module (TPM) technology is designed to provide hardware-based, security-related functions. A TPM chip is a secure crypto-processor intended to carry out cryptographic operations. 
	A TPM is also used for system integrity measurements. These can be used as evidence of how a system started and to ensure that a TPM-based key was used when the correct software was used to boot the system. One feature provides anti-malware software with a trusted (resistant to spoofing and tampering) log of all boot components. 
	 
 


	Virtual TPM support on vSphere 8



	Virtual TPM devices require vSphere with a configured and ready-to-use Key Provider.  
	This is a prerequisite before creating a VM with a vTPM device or adding one to an existing VM.  
	In vSphere 8, this can be a Native Key Provider or an external third-party key provider. 
	 
 


	IMPORTANT: 
	 
	Adding a vTPM device requires a Key Provider, and the virtual machine “home” files (memory, swap, and NVRAM files) are encrypted. Encrypting the virtual machine disk files is not required. vTPM and full VM Encryption are separate features.  
	 
	A vTPM does not require a physical Trusted Platform Module (TPM) 2.0 chip to be present on the ESXi host. 
	Find more details about vTPM and vSphere 8 at https://core.vmware.com/vtpm.  
	 
 


	 
 


	Configuring a Native Key Provider on vSphere 8



	Using a Native Key Provider on the vSphere 8 environment requires some preparation.  
	After configuration is complete, vTPMs can be added to the Virtual Machines.
 


	The Native Key Provider is included in all vSphere editions and does not require an external key server.
 


	To configure a Native Key Provider, you must:
 


	
		Ensure the vCenter Server system and ESXi hosts run vSphere 8 or later.
	
	
		Configure the ESXi hosts in a cluster. 
	
	
		Configure the vCenter Server file-based Backup and Restore.
	



	 
 


	IMPORTANT: 
	 
	Please be aware that deploying a Key Provider needs a configured vSphere 8 cluster! 
	 
 


	 
 


	1. Log in to the vCenter Server with the vSphere Client: 
	
 


	 
 


	2. Select the vCenter instance and click on "Configure": 
	
 


	 
 


	3. Go to the "Security" part and click on "Key Providers": 
	
 


	 
 


	4. Click on "Add" to add a new Key Provider: 
	
 


	 
 


	5. Choose "Add Native Key Provider" to add a Native Key Provider: 
	
 


	 
 


	6. Give it a name: 
	
 


	 
 


	CAUTION: 
	 
	Check "Use key provider only with TPM-protected ESXi hosts" only if your physical server has a TPM installed! 
	 
 


	 
	7. The Key Provider has been created but must be backed up before you can use it. 
	
 


	 
 


	8. Choose the Key Provider and click on "Back-Up": 
	
 


	 
 


	9. Choose to password-protect the .p12 file to have maximum security: 
	
 


	 
 


	10. After the successful backup of the Key Provider and its replication to all ESXi hosts in the cluster, it is ready to use: 
	
 


	 
 


	WARNING: 
	 
	If the status of the just-created Key Provider is "Warning," that indicates that the vCenter Server is still replicating the information to all the ESXi hosts in the Cluster, and the Key Provider cannot be used on all Cluster members until replication is complete! 
	 
 


	 
	11. If you create a Windows 11-based VM, a usable vTPM is automatically added by the creation wizard: 
	
 


	 
 


	12. After successful VM creation, vSphere shows the used vTPM in the VM details. 
	
 


	 
 


	We now have created a vSphere 8-compatible Windows 11-based Virtual Machine.
 


	 
 


	NOTE: 
	 
	Before we create a Machine Catalog, let´s assume that you have already configured this Virtual Machine as the Master Image and installed all the needed software on it. 
	 
 


	 
	 
 


	Creating a Windows 11-based Machine Catalog on vSphere 8



	Creating a Windows 11-based Machine Catalog with vTPM requires a Machine Profile.
 


	As Windows 11 is installed on the Master Image, it is a requirement to have vTPM enabled for the Master Image.
 


	The source of the Machine Profile input is a vSphere 8 template.  
	The Machine Profile captures the hardware properties from the template and applies them to the newly provisioned VMs in the catalog.
 


	 
 


	NOTE: 
	 
	vTPM must be enabled on both the Master Image source (a Snapshot) and the Machine Profile source (a vSphere template). 
	 
	As the Master Image has vTPM enabled, the vSphere template can only come from the same VM source as the Master Image. 
	 
 


	 
 


	CAUTION: 
	 
	The vSphere template in the Machine Profile must exist during the catalog lifecycle to allow the provisioning of VMs to the catalog.  
	 
	Without a vSphere template, you cannot provision new VMs.  
	 
	When the vSphere template gets deleted, you must supply a new template using the Set-ProvScheme command. 
	 
 


	 
	 
	Creation of a Machine Catalog using CVAD WebStudio
 


	We assume that you have done all the needed prerequisites before starting the creation of a Machine Catalog:
 


	
		Creation of a Zone
	
	
		Creation of a Hypervisor Connection
	
	
		Creation of a Hypervisor Connection Resource Pool
	



	 
 


	Before creating a Machine Catalog, we must be sure that a compatible vSphere template is available:
 


	 
	Use the created Master Image VM (with vTPM enabled) and clone it into a template for further use: 
	Our template is called "W11" - vTPM is active.
 


	
 


	 
 


	
		Now we can start creating the Machine Catalog: 
		 
		 
	
	
		Choose the correct Machine Type - as this is a Windows 11-VM on-premise, it needs to be "Single-session OS": 
		
	



	 
 


	
		Choose the correct Image Management type - we will use power-managed and Citrix Machine Creation Services-provisioned VMs: 
		
	



	 
 


	
		Choose the suitable "Desktop Experience" type: 
		
	



	 
 


	
		Choose the suitable "Image" type – we recommend you look at our new Image Management feature. 
		Find more information about it in our Tech Zone guide: 
		https://community.citrix.com/tech-zone/build/deployment-guides/image-management-a-new-way-of-deploying-machine-catalogs-and-reducing-master-image-complexity-r288/. 
		
	



	 
 


	
		Select the "Image" you want to use: 
		
	



	 
 


	
		When deploying Windows 11-based VMs, you must use a Machine Profile.  
		The wizard automatically checks the checkbox "Use a machine profile". 
		 
	



	IMPORTANT: 
	 
	Please make sure that the checkbox is checked before proceeding! 
	 
 


	 
 


	Hovering above the Info icon provides some explanations:
 


	
 


	 
 


	
		The wizard queries all available and compatible vSphere templates and lists them. 
		Choose the correct Machine Template.  
		
	



	 
 


	
		Check all Image-related properties before continuing. 
		
	



	 
 


	
		All further steps of the wizard are omitted - they are not different from other Machine Catalog creation steps. 
	



	 
 


	Take a final review of all settings before starting the creation:
 


	
 


	 
 


	The Citrix Virtual Apps and Desktop WebStudio wizard should now create a Machine Catalog based on a Windows 11 Master Image.
 


	 
 


	Creation of a Machine Catalog using Citrix Cloud Studio



	We assume that you have done all the needed prerequisites before starting the creation of a Machine Catalog:
 


	
		Creation of a Zone
	
	
		Creation of a Hypervisor Connection
	
	
		Creation of a Hypervisor Connection Resource Pool
	



	 
 


	Before creating a Machine Catalog, we must be sure that a compatible vSphere template is available:
 


	Use the created Master Image VM (with vTPM enabled) and convert it into a template for further use:
 


	Our template is called "W11" - vTPM is active.
 


	
 


	 
	 
	 
 


	
		Now we can start creating the Machine Catalog: 
		 
		 
	
	
		Choose the correct Machine Type - as this is a Windows 11-VM on-premise, it needs to be "Single-session OS": 
		 
		 
	
	
		Choose the correct Image Management type – we will use power-managed and Citrix Machine Creation Services-provisioned VMs: 
		 
		 
	
	
		Choose the suitable "Desktop Experience" type: 
		
	



	 
 


	
		Choose the suitable "Image" type - we recommend looking at our new Image Management feature. 
		Find more information about it in our Tech Zone guide: 
		https://community.citrix.com/tech-zone/build/deployment-guides/image-management-a-new-way-of-deploying-machine-catalogs-and-reducing-master-image-complexity-r288/. 
		
	



	 
 


	
		Select the "Image" you want to use: 
		
	



	 
 


	
		Using a Machine Profile is required when deploying Windows 11-based VMs. 
		The wizard automatically checks the checkbox "Use a machine profile".
	



	IMPORTANT: 
	 
	Please make sure that the checkbox is checked before proceeding! 
	 
 


	 
	Hovering above the Info icon provides some explanations:
 


	
 


	 
 


	
		The wizard queries all available and compatible vSphere templates and lists them. 
		Choose the correct Machine Template. 
		 
		 
	
	
		Check all Image-related properties before continuing. 
		
	



	 
 


	
		All further steps of the wizard are omitted - they are not different from other Machine Catalog creation steps. 
	



	 
	Take a final review of all settings before starting the creation:
 


	
 


	 
	The Citrix Cloud Studio wizard should now create a Machine Catalog based on a Windows 11 Master Image. 
	 
 


	Creation of a Machine Catalog using PowerShell



	Prerequisites



	We assume that you have done all the needed prerequisites before starting the creation of a Machine Catalog:
 


	
		Creation of a Zone
	
	
		Creation of a Hypervisor Connection
	
	
		Creation of a Hypervisor Connection Resource Pool
	



	 
 


	Before creating a Machine Catalog, we must be sure that a compatible vSphere template is available:
 


	Use the created Master Image VM (with vTPM enabled) and convert it into a template for further use.
 


	 
 


	We can use PowerShell to list all available VM templates and pass the correct name of the VM template to the script.
 


	 
 


	IMPORTANT: 
	 
	Before using PowerShell on a vSphere environment, we must download and install the PowerCLI software from Broadcom. 
	 
	The communication flow and the steps shown are valid for CVAD- and Citrix Cloud-based deployments.  
	 
 


	 
 


	
 


	 
 


	Open a PowerShell window with Administrative rights and install PowerCLE using the code below.
 


	After successful installation, we can use 3 cmdlets to get the names of all available templates:
 


	 
 


	Connect-VIServer is used to connect to a vSphere environment.
 


	Get-Datacenter gets the name(s) of the Datacenter(s) in this environment.
 


	Get-Template lists all available VM templates in this Datacenter. 
	 
 


	IMPORTANT: 
	 
	Choose the correct name of the template you want to use. 
	 
 


	 
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\_TACG&gt; Install-Module -Name VMware.PowerCLI -AllowClobber
					 

					
						C:\Users\Gerhard&gt;                                                                                                                                                                                
						PS C:\_TACG&gt; Set-PowerCLIConfiguration -Scope User -ParticipateInCEIP $false
					 

					
						Scope    ProxyPolicy     DefaultVIServerMode InvalidCertificateAction  DisplayDeprecationWarnings WebOperationTimeout 
						                                                                                                  Seconds 
						-----    -----------     ------------------- ------------------------  -------------------------- ------------------- 
						Session  UseSystemProxy  Multiple            Unset                     True                       300 
						User 
						AllUsers
					 

					
						 
						PS C:\_TACG&gt; 
						PS C:\_TACG&gt; Connect-VIServer -Server vcenter.the-austrian-citrix-guy.at -Protocol https -User XXXXXXXXXXXXXXXXXXXXXXXX -Password XXXXXXXXXXXXXXXXXXXXXXXXX
					 

					
						Name                           Port  User 
						----                           ----  ---- 
						vcenter.the-austrian-citrix... 443   VSPHERE.LOCAL\Administrator
					 

					
						 
						PS C:\_TACG&gt; Get-Datacenter
					 

					
						Name 
						---- 
						TACG
					 

					
						 
						PS C:\_TACG&gt; Get-Template -Name * -Location TACG
					 

					
						Name 
						---- 
						W2K22 
						W11
					 

					
						 
						PS C:\_TACG&gt;
					 
				
			
		
	



	In this example, the name of the VM template we want to use is "W11".
 


	Another essential prerequisite is the installation of the Citrix Virtual Apps and Desktops SDK or the Citrix DaaS Remote PowerShell SDK.
 


	Download the Citrix Virtual Apps and Desktops SDK here: https://developer-docs.citrix.com/en-us/citrix-virtual-apps-desktops-sdk/2402.html
 


	Download the Citrix DaaS Remote PowerShell SDK here: https://docs.citrix.com/en-us/citrix-daas/sdk-api#citrix-daas-remote-powershell-sdk
 


	 
	We need to determine further entities before creating a Machine Catalog:
 


	
		The ID of the Zone
	
	
		The Path of the Master Image Snapshot
	
	
		The Path of the Network 
	
	
		The Path of the Machine Profile
	



	PowerShell can determine all these paths. The examples provided use a Citrix DaaS deployment.
 


	
		Add the needed PowerShell cmdlets and authenticate to Citrix Cloud:

		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
			
				
					
						
							
								PS C:\_TACG&gt; asnp Citrix.* 
								PS C:\_TACG&gt; Get-XDAuthentication
							 
						
					
				
			

			
				 
			 
		
	
	
		Get all available Resource Locations with their corresponding Zone-UIds - we want to use the vSphere-based Resource Location:
		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
			
				
					
						
							
								PS C:\_TACG&gt; Get-ConfigZone | FT Name, UId
							 

							
								 
								Name                                          Uid 
								----                                          --- 
								Initial Zone                                  00000000-0000-0000-0000-000000000000 
								TACG-REST-RL1                                 XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX 
								TACG-RL-AWS                                   XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX 
								TACG-RL-GCP                                   XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX 
								TACG-TF-RL-ONP-vSphere                        XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX 
								TACG-TMP-RL-NTX                               XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX 
								The Austrian Citrix Guy - AWS                 XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX 
								The Austrian Citrix Guy - AzGPU               XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX 
								The Austrian Citrix Guy - AzHib               XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX 
								The Austrian Citrix Guy - AzStHCI local       XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX 
								The Austrian Citrix Guy - Azure Connectorless XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX 
								The Austrian Citrix Guy - On-Prem             XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX 
								The Austrian Citrix Guy - vSphere             d57aXXXX-XXXX-XXXX-XXXX-XXXXXXXX108d
							 

							
								 
								PS C:\_TACG&gt;
							 
						
					
				
			
		
		  
		In this case, the UId we want to use is d57aXXXX-XXXX-XXXX-XXXX-XXXXXXXX108d. 
		 
	
	
		Now, we need to determine the different paths: 
		We use the UId as a filter when opening the XDHyp:\Connections path: 
		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
			
				
					
						
							
								PS C:\_TACG&gt; dir xdhyp:\Connections | Where-Object {$_.ZoneUid -eq "d57aXXXX-XXXX-XXXX-XXXX-XXXXXXXX108d"}
							 

							
								 
								PSPath                          : Citrix.Host.Admin.V2\Citrix.Hypervisor::XDHyp:\Connections\TACG-ONP-vSphere 
								PSParentPath                    : Citrix.Host.Admin.V2\Citrix.Hypervisor::XDHyp:\Connections 
								PSChildName                     : TACG-ONP-vSphere 
								PSDrive                         : XDHyp 
								PSProvider                      : Citrix.Host.Admin.V2\Citrix.Hypervisor 
								PSIsContainer                   : True 
								ConnectionType                  : VCenter 
								HypervisorAddress               : {https://vcenter.the-austrian-citrix-guy.at/sdk} 
								HypervisorConnectionName        : TACG-ONP-vSphere 
								HypervisorConnectionUid         : 7f98XXXX-XXXX-XXXX-XXXX-XXXXXXXXae17 
								MaintenanceMode                 : False 
								Metadata                        : {Citrix_Orchestration_Hypervisor_Secret_Allow_Edit = false, Citrix_Broker_MaxAbsoluteActiveActions = 40, Citrix_Broker_MaxAbsoluteNewActionsPerMinute = 10, 
								                                  Citrix_Broker_MaxPowerActionsPercentageOfDesktops = 20...} 
								MetadataMap                     : {[Citrix_Orchestration_Hypervisor_Secret_Allow_Edit, false], [Citrix_Broker_MaxAbsoluteActiveActions, 40], [Citrix_Broker_MaxAbsoluteNewActionsPerMinute, 10], 
								                                  [Citrix_Broker_MaxPowerActionsPercentageOfDesktops, 20]...} 
								Persistent                      : True 
								PluginId                        : VmwareFactory 
								Revision                        : 81f7XXXX-XXXX-XXXX-XXXX-XXXXXXXXa59b 
								SslThumbprints                  : {0377BE89D96540C66402F6301611A93B7221F14E} 
								SupportsLocalStorageCaching     : False 
								SupportsPvsVMs                  : True 
								Unentitled                      : False 
								UserName                        : administrator@vsphere.local 
								Scopes                          : 
								UsesCloudInfrastructure         : False 
								ConnectionTypeName              : VCenter 
								CustomProperties                : 
								FullPath                        : XDHyp:\Connections\TACG-ONP-vSphere 
								Capabilities                    : {CreateVmWithNetworkDisconnected, ReportsExplicitStorageRequirements, RequiresExplicitStorage, RequiresDiskDetachingBeforeDelete...} 
								ConfigurationObjectCapabilities : {Vm = {CpuCount, MemoryMB, MinMemoryMB, HardDiskSizeGB...}} 
								ZoneName                        : The Austrian Citrix Guy - vSphere 
								ZoneUid                         : d57aXXXX-XXXX-XXXX-XXXX-XXXXXXXX108d
							 

							
								 
								PS C:\_TACG&gt;
							 
						
					
				
			

			
				 
			 
		
	
	
		Using the HypervisorConnection property of the previous output, we now can list all properties of the adjacent Hypervisor Connection pool:
		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
			
				
					
						
							
								PS C:\_TACG&gt; dir xdhyp:\HostingUnits | Where-Object {$_.HypervisorConnection -like "TACG-ONP-vSphere"}
							 

							
								 
								PSPath                 : Citrix.Host.Admin.V2\Citrix.Hypervisor::XDHyp:\HostingUnits\TACG-ONP-VSPHERE-RESOURCES 
								PSParentPath           : Citrix.Host.Admin.V2\Citrix.Hypervisor::XDHyp:\HostingUnits 
								PSChildName            : TACG-ONP-VSPHERE-RESOURCES 
								PSDrive                : XDHyp 
								PSProvider             : Citrix.Host.Admin.V2\Citrix.Hypervisor 
								PSIsContainer          : True 
								HostingUnitName        : TACG-ONP-VSPHERE-RESOURCES 
								HostingUnitUid         : 0f98XXXX-XXXX-XXXX-XXXX-XXXXXXXX4dbe 
								HypervisorConnection   : TACG-ONP-vSphere 
								Metadata               : {} 
								MetadataMap            : {} 
								NetworkId              : Network:network-12 
								NetworkPath            : XDHyp:\Connections\TACG-ONP-vSphere\TACG.datacenter\TACG.cluster\VM Network.network 
								PermittedNetworks      : {XDHyp:\Connections\TACG-ONP-vSphere\TACG.datacenter\TACG.cluster\VM Network.network} 
								RootId                 : domain-c17 
								RootPath               : XDHyp:\Connections\TACG-ONP-vSphere\TACG.datacenter\TACG.cluster 
								Storage                : {XDHyp:\Connections\TACG-ONP-vSphere\TACG.datacenter\TACG.cluster\datastore1.storage} 
								VMTaggingEnabled       : True 
								UseLocalStorageCaching : False 
								PersonalvDiskStorage   : {} 
								GpuType                : {} 
								AvailabilityZones      : {} 
								AdditionalStorage      : {TemporaryStorage} 
								CustomProperties       : 
								FullPath               :
							 

							
								 
								PS C:\_TACG&gt; 
								 
							 
						
					
				
			

			
				 
			 
		
	
	
		We can now determine the Snapshot path:
		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
			
				
					
						
							
								PS C:\_TACG&gt; Get-HypInventoryItem -Literalpath "XDHyp:\HostingUnits\TACG-ONP-vSphere-Resources" -ResourceType snapshot
							 

							
								 
								AdditionalData : {[GuestOsId, windows11_64Guest], [GuestOsName, Microsoft Windows 11 (64-bit)], [Firmware, efi], 
								                 [VTpmEnabled, True]...} 
								FullName       : vsp-main.snapshot 
								FullPath       : XDHyp:\HostingUnits\TACG-ONP-vSphere-Resources\TACG.cluster\TACG-VSP-W11-M.vm\vsp-main.snapshot 
								Id             : vm-26:snapshot-70 
								IsContainer    : True 
								IsMachine      : False 
								IsSnapshotable : False 
								IsSymLink      : False 
								Name           : vsp-main 
								ObjectPath     : /TACG.datacenter/TACG.cluster/TACG-VSP-W11-M.vm/vsp-main.snapshot 
								ObjectType     : Snapshot 
								ObjectTypeName : snapshot
							 

							
								PS C:\_TACG&gt;
							 
						
					
				
			
			 

			
				We now have all the needed paths/properties to create a Machine Catalog using a Machine Template: 
				 
			 

			
				IMPORTANT: 
				 
				Please change all properties to your needs - the mentioned properties are only for demonstration purposes! 
				 
			 

			
				 
			 
		
	



	
		Let´s create a new IdentityPool:

		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
			
				
					
						
							
								PS C:\_TACG&gt; $identityPool = New-AcctIdentityPool -IdentityPoolName "AcctIdentPool-TACG-Test" -NamingScheme "VM-TEST-###" -NamingSchemeType Numeric -Domain "the-austrian-citrix-guy.at" -ZoneUid "d57aXXXX-XXXX-XXXX-XXXX-XXXXXXXX108d"  -Scope @() 
								PS C:\_TACG&gt; 
								PS C:\_TACG&gt; $identitypool 
								AvailableAccounts    : 0 
								DeviceManagementType : None 
								Domain               : THE-AUSTRIAN-CITRIX-GUY.AT 
								ErrorAccounts        : 0 
								IdentityContent      : 
								IdentityPoolName     : AcctIdentPool-TACG-Test 
								IdentityPoolUid      : 8241XXXX-XXXX-XXXX-XXXX-XXXXXXXX4ef2 
								IdentityType         : ActiveDirectory 
								InUseAccounts        : 0 
								Lock                 : False 
								MetadataMap          : {} 
								NamingScheme         : VM-TEST-### 
								NamingSchemeType     : Numeric 
								OU                   : 
								ResourceLocationId   : cfd2XXXX-XXXX-XXXX-XXXX-XXXXXXXX26b6 
								ServiceAccountUid    : {} 
								StartCount           : 1 
								TaintedAccounts      : 0 
								WorkgroupMachine     : False 
								ZoneUid              : d57aXXXX-XXXX-XXXX-XXXX-XXXXXXXX108d 
								Scopes               : {} 
								TenantId             :
							 

							
								 
								PS C:\_TACG&gt;
							 
						
					
				
			

			
				 
			 
		
	
	
		Let´s create a new ProvisioningScheme:
		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
			
				
					
						
							
								PS C:\_TACG&gt;  $provScheme =New-ProvScheme -CleanOnBoot -HostingUnitName "TACG-ONP-VSPHERE-RESOURCES" -IdentityPoolName "AcctIdentPool-TACG-Test" -InitialBatchSizeHint 1 -MasterImageVM "XDHyp:\HostingUnits\TACG-ONP-VSPHERE-RESOURCES\TACG-VSP-W11-M.vm\vsp-main.snapshot" -ProvisioningSchemeName "ProvScheme-TACG-Test" -Scope @() -VMCpuCount 2 -VMMemoryMB 8192 -MachineProfile "XDHyp:\HostingUnits\TACG-ONP-VSPHERE-RESOURCES\W11.template" -TenancyType Shared -FunctionalLevel "L7_20" 
								PS C:\_TACG&gt; 
								PS C:\_TACG&gt; $provscheme
							 

							
								 
								TaskId                       : c341XXXX-XXXX-XXXX-XXXX-XXXXXXXXea8b 
								Active                       : False 
								Host                         : EA102823-15-1 
								DateStarted                  : 24.06.2024 10:49:22 
								Metadata                     : {} 
								Storage                      : {/TACG.datacenter/TACG.cluster/datastore1.storage} 
								ProvisioningSchemeName       : ProvScheme-TACG-Test 
								MasterImage                  : XDHyp:\HostingUnits\TACG-ONP-VSPHERE-RESOURCES\TACG-VSP-W11-M.vm\vsp-main.snapshot 
								ImageDefinitionName          : 
								ImageVersionNumber           : 
								ImageVersionSpecUid          : 
								MachineProfile               : /TACG.datacenter/TACG.cluster/W11.template 
								IdentityPoolName             : AcctIdentPool-TACG-Test 
								IdentityPoolUid              : 8241XXXX-XXXX-XXXX-XXXX-XXXXXXXX4ef2 
								HostingUnitName              : TACG-ONP-VSPHERE-RESOURCES 
								HostingUnitUid               : 0f98XXXX-XXXX-XXXX-XXXX-XXXXXXXX4dbe 
								CustomProperties             : 
								InitialBatchSizeHint         : 1 
								ProvisioningSchemeUid        : 5820XXXX-XXXX-XXXX-XXXX-XXXXXXXXd5af 
								DiskSize                     : 60 
								MasterImageNote              : 
								WriteBackCacheDiskSize       : 0 
								WriteBackCacheMemorySize     : 0 
								Scopes                       : 
								NetworkMaps                  : {} 
								ProvisioningSchemeMetadata   : {[ImageManagementPrep_DoImagePreparation, True], [ImageManagementPrep_Excluded_Steps, ], [ImageManagementPrep_NoAutoShutdown, False]} 
								ServiceOffering              : 
								SecurityGroups               : {} 
								DedicatedTenancy             : False 
								ResetAdministratorPasswords  : False 
								StatusMessageSubstitutions   : 
								GpuTypeId                    : 
								UseFullDiskCloneProvisioning : False 
								ProvisioningSchemeType       : Citrix Machine Creation Services 
								PvsSite                      : 
								PvsVDisk                     : 
								Warnings                     : {} 
								ForceCreate                  : False 
								TaskState                    : Finished 
								TaskType                     : NewProvisioningScheme 
								LastUpdateTime               : 24.06.2024 10:50:49 
								CurrentOperation             : 
								TerminatingError             : 
								TaskProgress                 : 100 
								WorkFlowStatus               : Completed 
								DateFinished                 : 24.06.2024 10:50:49 
								TaskExpectedCompletion       : 24.06.2024 10:50:49 
								ActiveElapsedTime            : 87 
								Status                       : Finished 
								TaskStateInformation         : Completed 
								Type                         : NewProvisioningScheme
							 

							
								 
								PS C:\_TACG&gt;
							 
						
					
				
			

			
				 
			 
		
	
	
		Let´s create a new BrokerCatalog:
		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
			
				
					
						
							
								PS C:\_TACG&gt; Set-BrokerCatalog -Name "MC-TACG-Test" -ProvisioningSchemeId $provScheme.ProvisioningSchemeUid.Guid 
								PS C:\_TACG&gt; 
								PS C:\_TACG&gt; Get-BrokerCatalog -Name "MC-TACG-Test"
							 

							
								 
								AdminFolderName                 : 
								AdminFolderUid                  : 0 
								AllocationType                  : Random 
								AppDnaAnalysisState             : 
								AssignedCount                   : 0 
								AvailableAssignedCount          : 0 
								AvailableCount                  : 0 
								AvailableUnassignedCount        : 0 
								CatalogName                     : MC-TACG-Test 
								Description                     : MC-TACG-Test 
								HypervisorConnectionUid         : 52 
								IsRemotePC                      : False 
								MachinesArePhysical             : False 
								MdmEnrollment                   : None 
								MetadataMap                     : {} 
								MinimumFunctionalLevel          : L7_20 
								Name                            : MC-TACG-Test 
								PersistUserChanges              : Discard 
								ProvisioningSchemeId            : 5820XXXX-XXXX-XXXX-XXXX-XXXXXXXXd5af 
								ProvisioningType                : Citrix Machine Creation Services 
								PvsAddress                      : 
								PvsDomain                       : 
								RemotePCDesktopGroupPriorities  : {} 
								RemotePCDesktopGroupUids        : {} 
								RemotePCHypervisorConnectionUid : 
								Scopes                          : 
								SessionSupport                  : SingleSession 
								Tags                            : {} 
								TenantId                        : 
								TimeZone                        : 
								UUID                            : 4cebXXXX-XXXX-XXXX-XXXX-XXXXXXXX7bb5 
								Uid                             : 81 
								UnassignedCount                 : 0 
								UsedCount                       : 0 
								ZoneHealthy                     : True 
								ZoneName                        : The Austrian Citrix Guy - vSphere 
								ZoneUid                         : d57aXXXX-XXXX-XXXX-XXXX-XXXXXXXX108d
							 

							
								 
								PS C:\_TACG&gt;
							 
						
					
				
			

			
				 
			 
		
	
	
		Let´s create a VM:
		
			
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}			
			
				
					
						
							
								PS C:\_TACG&gt; New-AcctADAccount -Count 1 -IdentityPoolUid $IdentityPool.IdentityPoolUid -ADUsername 'TACG\XXXXXXXXXX'
							 

							
								 
								SuccessfulAccounts SuccessfulAccountsCount FailedAccountsCount FailedAccounts 
								------------------ ----------------------- ------------------- -------------- 
								{TACG\VM-TEST-001}                       1                   0 {}
							 

							
								 
								PS C:\_TACG&gt; New-ProvVM -ADAccountName @('TACG\VM-TEST-001\$') -ProvisioningSchemeName $provscheme.ProvisioningSchemeName
							 

							
								 
								TaskId                             : 7699XXXX-XXXX-XXXX-XXXX-XXXXXXXXa173 
								Active                             : False 
								Host                               : EA102823-15-1 
								DateStarted                        : 24.06.2024 11:04:15 
								Metadata                           : {} 
								MasterImage                        : 
								MasterImageId                      : vm-26:snapshot-70 
								DataImageId                        : 
								ImageVersionSpecUid                : 
								ProvisioningSchemeName             : ProvScheme-TACG-Test 
								ProvisioningSchemeUid              : 5820XXXX-XXXX-XXXX-XXXX-XXXXXXXXd5af 
								ProvisioningSchemeVersion          : 1 
								StatusMessageSubstitutions         : 
								TaskStateInformation               : Completed 
								HostingConnectionName              : TACG-ONP-vSphere 
								HostingUnitUid                     : 0f98XXXX-XXXX-XXXX-XXXX-XXXXXXXX4dbe 
								HostingUnitName                    : TACG-ONP-VSPHERE-RESOURCES 
								IdentityPoolUid                    : 8241XXXX-XXXX-XXXX-XXXX-XXXXXXXX4ef2 
								IdentityPoolName                   : AcctIdentPool-TACG-Test 
								VirtualMachinesToCreateCount       : 0 
								VirtualMachinesCreatedCount        : 0 
								VirtualMachinesCreationFailedCount : 1 
								CreatedVirtualMachines             : {} 
								FailedVirtualMachines              : {TACG\VM-TEST-001\$} 
								ByAdAccountSid                     : False 
								TaskState                          : Finished 
								TaskType                           : NewVirtualMachine 
								LastUpdateTime                     : 24.06.2024 11:04:20 
								CurrentOperation                   : 
								TerminatingError                   : 
								TaskProgress                       : 100 
								WorkFlowStatus                     : Completed 
								DateFinished                       : 24.06.2024 11:04:20 
								TaskExpectedCompletion             : 24.06.2024 11:04:20 
								ActiveElapsedTime                  : 4 
								Status                             : Finished 
								Type                               : NewVirtualMachine
							 

							
								 
								PS C:\_TACG&gt;
							 
						
					
				
			
		
	



	We have successfully created a Machine Catalog using a Machine Template:
 


	
 


	
 


	
 


	 
 


	 
 


	Creation of a Machine Catalog using REST-API calls



	 
	Prerequisites 
	 



	IMPORTANT: 
	 
	Please find the needed prerequisites and how to determine all properties of the required entities in the previous chapter. 
	 
	The communication flow and the steps shown are valid for CVAD- and Citrix Cloud-based deployments.  
	 
 


	 
 


	In this example, we use the Postman application for all REST-API-based communication.
 


	You need additional prerequisites before REST-API calls can be used:
 


	
		Ensure that you have created a Secure Client for Authentication to Citrix Cloud 
	
	
		Ensure that you know your Citrix Cloud ID
	
	
		Ensure that you have a valid Bearer token 
	
	
		Ensure that you have the SiteID
	
	
		Ensure you have created a Hosting Connection and a Hosting Connection Resource Pool.
	
	
		Ensure you have a domain administrator account encoded in UTF-8 and encoded as Base64 string. You must set the X-AdminCredential using the encoded credentials.  
		You can use online tools for the different encodings (e.g. https://mixedanalytics.com/tools/basic-authentication-generator/) 
		 
	



	Creating a Secure Client in Citrix Cloud
 


	The Secure Client in Citrix Cloud is the same as the SPN in Azure. 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 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. 
 


	
		Select the Identity and Access Management option from the menu to create an API client.  
		If this option does not appear, you do not have adequate permissions to create an API client. Contact your administrator to get the required permissions. 
		 
		 
	
	
		Click API Access, Secure Clients, and put a name in the textbox adjacent to the button Create Client. After entering a name, click Create Client: 
		 
		
	
	
		Write down the ID and the Secret.
	



	 
	Determine the Citrix Cloud-ID
 


	
		Just log on to your Citrix Cloud environment. 
		You will find your CCID on the top-right corner: 
		 
		 
	
	
		Write down your CCID.
	



	 
	Create a valid Bearer token for Authentication
 


	The Bearer Token is needed to authorize REST-API calls in Citrix Cloud.
 


	It is essential 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 CCID 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] 
	 
 


	
		The body of the REST-API call needs 3 key/value pairs:
	



	grant_type:  client_credentials
 


	client_id:  use the ID of the Secure Client you previously created 
 


	client_secret: use the Secret of the Secure Client you previously created 
	 
 


	
 


	
		Paste the correct URI into Postman´s address bar and select POST as the method.  
		 
	
	
		Verify the API call settings are correct. 
		
	



	 
 


	
		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 usually valid for 3600 seconds. 
		 
	
	
		Copy the value of the access-token field; you will need it in all further REST-API calls!
	



	 
	 



	Get the Site-ID



	The SiteID is an essential parameter for determining which Site/Tenant of Citrix Cloud will be used:
 


	It is essential to set the URI to call and the required parameters correct:  
	The generic URI is: 
	https://api-eu.cloud.com/cvad/manage/me
 


	
		The headers of the REST-API call contain all needed key/value pairs, the body of this type of call is empty:
	



	Citrix-CustomerId: use the CCID 
 


	Authorization:  use "CWSAuth bearer=" and add the BearerToken you previously created 
 


	Content-Type:  use "application/json" 
 


	 
 


	
		Paste the correct URI into Postman´s address bar and select GET as the method.  
		 
	
	
		Verify the API call settings are correct. 
		
	
	
		If everything is set correctly, Postman shows a Response containing a JSON-formatted file containing the Site ID in the field Sites - Id: The token is usually valid for 3600 seconds. 
		 
	
	
		Copy the value of the Sites - Id field. You will need it in later REST-API calls!
	



	 
 


	Create the Machine Catalog using a REST-API call



	It is essential to set the URI to call and the required parameters correct:  
	The generic URI is: 
	https://api-eu.cloud.com/cvad/manage/MachineCatalogs?async=true
 


	
		The header of the REST-API call contains all needed key/value pairs:
	



	Citrix-CustomerId: use the CCID 
 


	Authorization:  use "CWSAuth bearer=" and add the BearerToken you previously created 
 


	Content-Type:  use "application/json" 
 


	Citrix-InstanceID: use the SiteId you previously obtained
 


	X-AdminCredential: use "Basic " and add the Base64-encoded credentials you previously created 
	
 


	 
 


	CAUTION: 
	 
	The Body of the REST-API call contains all the parameters and properties necessary for creating the Machine Catalog. 
	 
	Beware of the delicate syntax! 
	 
	Backslashes ("\") must be encoded as double backslashes ("\\") due to the JSON syntax! 
	 
 


	As mentioned, you can read all necessary parameters and properties using PowerShell. 
	We assume you have read all the needed parameters and properties.
 


	
		Paste the correct URI into Postman´s address bar and select POST as the method.  
		 
	
	
		Verify the API call settings are correct. 
		This call is done asynchronously, so we only get a “202 Accepted” response.  
		We need to call the Job progress periodically to determine the progress or if an error occurred.
	



	 
 


	Example of a REST-API-Call to create a Machine Catalog:
 


	POST 
	https://api-eu.cloud.com/cvad/manage/MachineCatalogs?async=true 
	 
	Authorization: Bearer {{Bearer-Token-Value}} 
	Citrix-CustomerId: {{Citrix-CustomerID}} 
	Citrix-InstanceID: {{Citrix-SiteID}} 
	X-AdminCredential: Basic MjFceDIx… 
	 
	Body: 
	{ 
	"Name": "MC-W11-REST-API-TEST", 
	"AllocationType":"Random", 
	"MinimumFunctionalLevel":"L7_20", 
	"PersistUserChanges":"Discard", 
	"ProvisioningType":"Citrix Machine Creation Services", 
	"IsPowerManaged": true, 
	"SessionSupport":"SingleSession", 
	"Scopes":["00000000-0000-0000-0000-000000000000"], 
	"Tenants":[], 
	"Zone":"The Austrian Citrix Guy - vSphere", 
	"VdaUpgradeType":"NotSet", 
	"ProvisioningScheme":{ 
	"MasterImagePath":"XDHyp:\\HostingUnits\\TACG-ONP-VSPHERE-RESOURCES\\TACG-VSP-W11-M.vm\\vsp-main.snapshot", 
	"MachineProfilePath":"XDHyp:\\HostingUnits\\TACG-ONP-VSPHERE-RESOURCES\\W11.template", 
	"CpuCount":2, 
	"MemoryMB":4096, 
	"UseWriteBackCache":false, 
	"NumTotalMachines":1, 
	"IdentityType":"ActiveDirectory", 
	"MachineAccountCreationRules":{ 
	"NamingScheme":"Citrix Machine Creation Services-REST-W11-#","NamingSchemeType":"Numeric","Domain":"the-austrian-citrix-guy.at"}, 
	"PrepareImage":true, 
	"DedicatedTenancy":false, 
	"SecurityGroups":null, 
	"UseFullDiskCloneProvisioning":false, 
	"AdminFolder":"0" 
	}
 


	
 


	
		If everything is set correctly, Postman shows a 202 - Accepted response. 
		 
	
	
		As this is an asynchronous job, we need to determine the status by calling the Job progress: 
		The generic URI is: 
		https://api-eu.cloud.com/cvad/manage/Jobs
	



	The header of the REST-API call contains all needed key/value pairs:
 


	Citrix-CustomerId: use the CCID 
 


	Authorization:  use "CWSAuth bearer=" and add the BearerToken you previously created 
 


	Content-Type:  use "application/json"  
	Citrix-InstanceID: use the SiteId you previously obtained 
	 
	 
 


	
 


	 
 


	If everything is set correctly, Postman shows a Response containing a JSON-formatted file containing the Job information - it is quite a lengthy file…
 


	An "OverallProgressPercent" of 100 shows that the Machine Catalog was created.
 


	
 


	 
	WebStudio also shows the successfully created Machine Catalog:
 


	
 


	
 


	 
 


	 
 


	Creation of a Machine Catalog using Terraform



	 



	Prerequisites



	 
 


	IMPORTANT: 
	 
	Please find the prerequisites needed and the steps for determining all properties of the entities in the previous chapter. 
	 
	The communication flow and steps shown are valid for CVAD- and Citrix Cloud-based deployments; some alterations are needed. 
	 
 


	 
	Using Terraform to deploy a Machine Catalog is easy, as the syntax is relatively easy.
 


	You must define the required variables and IDs before running the Terraform script.
 


	
.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;
}	
	
		
			
				
					
						
							Snippet from our Terraform for CVAD on vSphere 8-Guide:
						 

						
							### Create the Machine Catalog 
							resource "citrix_machine_catalog" "MC-TACG-TF-ONP-VSP" { 
							 depends_on = [ citrix_vsphere_hypervisor_resource_pool.TACG-TF-HypConnPool ] 
							  name                                = "${var.CVAD_MC-Name}" 
							  description                         = "${var.CVAD_MC-Description}" 
							  zone                                = citrix_zone.TACG-TF-HYP-Zone.id 
							  allocation_type                     = "${var.CVAD_MC-AllocationType}" # "Random" 
							  session_support                     = "${var.CVAD_MC-SessionType}" # "SingleSession" 
							  is_power_managed                    = true 
							 
							  provisioning_type                   = "Citrix Machine Creation Services"  
							 
							  provisioning_scheme                 = { 
							      identity_type                   = "ActiveDirectory" 
							       hypervisor                     = citrix_vsphere_hypervisor.TACG-TF-HypConn.id 
							       hypervisor_resource_pool       = citrix_vsphere_hypervisor_resource_pool.TACG-TF-HypConnPool.id 
							 
							       machine_domain_identity = { 
							            domain                    = "${var.vsphere_domainname}" 
							            service_account           = "${var.vsphere_domain_admin_userwithdomain}" 
							            service_account_password  = "${var.vsphere_domain_admin_pw}" 
							        } 
							 
							    vsphere_machine_config            =  { 
							           master_image_vm                  = "${var.CVAD_MC-MasterImageName}" 
							           cpu_count                        = "${var.CVAD_MC-CpuCount}" 
							           memory_mb                        = "${var.CVAD_MC-MemorySize}" 
							           image_snapshot                   = "${var.CVAD_MC-MasterImageSnapshot}" 
							                                         } 
							 
							    number_of_total_machines          = "${var.CVAD_MC-MachineCount}" 
							 
							    machine_account_creation_rules    =  { 
							      naming_scheme                          = "${var.CVAD_MC-NamingSchemeName}" 
							      naming_scheme_type                     = "${var.CVAD_MC-NamingSchemeType}"  
							      domain                                 = "${var.CVAD_MC-DomainName}" 
							      #main_ou                        = "${var.CVAD_MC-domain-ou} 
							                                         }      
							                                              
							    os_type                           = "${var.CVAD_MC-OSType}"  
							    #storage_type                     = "${var.cc-mc-storage_type}"  
							    #use_managed_disks               = "${var.cc-mc-use_managed_disks}"  
							} 
							} 
						 
					
				
			
		
	



	 
	It seems that the Terraform provider reads the necessary configuration directly from the Master Image. 
	We will provide a definitive clarification soon.  
	 
 


	You can find a more detailed description of using Terraform on CVAD and DaaS in our Citrix Tech Zone articles:
 


	Citrix CVAD and Terraform on vSphere 8 - Automatic Deployment of a complete Citrix Virtual Apps and Desktops 2403 LTSR site on vSphere 8 using Terraform - Learn how to use Terraform to automatically create a complete Citrix Virtual Apps and Desktops 2403 LTSR site on vSphere 8 including the deployment of all necessary VMs, a Machine Catalog, and a Delivery Group.
 


	Citrix DaaS and Terraform on vSphere 8 - Automatic Deployment of a Resource Location on vSphere 8 using Terraform - Learn how to use Terraform to create a new Resource Location on vSphere 8 automatically.
 


	Citrix DaaS and Terraform - Automatic Deployment of a Resource Location on Amazon EC2 - Learn how to use Terraform to automatically create a new Resource Location on Amazon EC2, including deploying all necessary VMs, a Machine Catalog, and a Delivery Group.
 


	Citrix DaaS and Terraform - Automatic Deployment of a Resource Location on Google Cloud Platform (GCP) - Learn how to use Terraform to automatically create a new Resource Location on Google Cloud Platform (GCP), including the deployment of all necessary VMs, a Machine Catalog, and a Delivery Group.
 


	Citrix DaaS and Terraform - Automatic Deployment of a Resource Location on Microsoft Azure - Learn how to use Terraform to automatically create a new Resource Location on Microsoft Azure, including the deployment of all necessary VMs, a new domain, a Machine Catalog, and a Delivery Group.
 


	 
 


	IMPORTANT: 
	 
	Stay tuned for the following parts of the guides in this series: 
	 
	Upgrading a Win10-based Machine Catalog to Windows 11 with vTPM support 
	 
	Updating the certificates/keys in vTPM after they expire without the need to redeploy all VMs in the Machine Catalog]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_06/ccid.jpg.f20b77ffff6b6350eeb7d09b631d67ad.jpg" length="14669" type="image/jpeg"/><pubDate>Mon, 01 Jul 2024 11:47:35 +0000</pubDate></item><item><title>Deployment Guide: Installing Terraform and Configuring the Citrix Terraform Provider</title><link>https://community.stage.citrix.com/tech-zone/automation/terraform-install-and-config/</link><description><![CDATA[Installing Terraform and Configuring the Citrix Terraform Provider 
	 



	Overview



	 
	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.
 


	
 


	You can find more information about Terraform 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 td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\_TACG&gt; 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')) 
					 

					
						PS C:\_TACG&gt;
					 
				
			
		
	



	 
	Chocolatey downloads and installs all necessary components automatically:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\TACG&gt; 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&gt;
					 
				
			
		
	



	 
	Run choco --v to check if Chocolatey installed successfully:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\TACG&gt; choco --v 
						 Chocolatey v2.2.2
					 

					
						 
						PS C:\TACG&gt;
					 
				
			
		
	



	 
 


	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 td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\TACG&gt; choco install terraform 
						 Chocolatey v2.2.2 
						 Installing the following packages: 
						 terraform 
						 By installing, you accept licenses for the packages. 
						 Progress: Downloading terraform 1.8.4... 100%
					 

					
						 
						 terraform v1.8.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.8.4/terraform_1.8.4_windows_amd64.zip' 
						 Progress: 100% - Completed download of C:\TACG\AppData\Local\Temp\chocolatey\terraform\1.8.4\terraform_1.8.4_windows_amd64.zip (25.05 MB). 
						 Download of terraform_1.8.4_windows_amd64.zip (25.05 MB) completed. 
						 Hashes match. 
						  
						Extracting C:\TACG\AppData\Local\Temp\chocolatey\terraform\1.8.4\terraform_1.8.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&gt;
					 
				
			
		
	



	 
	Run terraform -version to check if Terraform installed successfully:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\TACG&gt; terraform -version 
						 Terraform v1.8.4 on windows_amd64 
						  
						PS C:\TACG&gt;
					 
				
			
		
	



	 
	You just completed the installation of Terraform.
 


	 
	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 builds, updates, or destroys based on the existing infrastructure and your configuration. 
	It is a declarative language - you "tell" Terraform what you need, and Terraform creates all necessary configuration steps between the current state and the desired state.
 


	Apply: 
	On approval, Terraform does the proposed operations in the correct order, respecting any resource dependencies.
 


	Terraform adds complete configurations and allows you to change previously added configurations. 
	For example, changing the DNS servers of a VM's NIC does not require redeploying the whole configuration - Terraform only alters the needed resources.
 


	 
	Terraform Provider for Citrix



	Citrix has developed a custom Terraform provider to automate Citrix product deployments and configurations. 
	 
 


	NOTE: 
	 
	The Citrix Terraform provider is currently in Tech Preview! 
	This guide is updated when the provider has reached RTM.
 


	You can use Terraform with Citrix's provider to manage your Citrix products via Infrastructure as Code. Terraform provides higher efficiency and consistency in infrastructure management and better reusability in infrastructure configuration.
 


	The provider defines individual infrastructure units and supports Citrix Virtual Apps and Desktops (CVAD) and Citrix Desktop as a Service (DaaS) solutions. 
	You can automate the creation of a site setup that includes host connections, machine catalogs, and delivery groups. 
	You can deploy resources in Amazon EC2, AWS, and GCP, as well as supported on-premises Hypervisors like vSphere, Nutanix, and XenServer. 
	 
 


	NOTE: 
	 
	You can find all the information about the Citrix Terraform provider on GitHub: https://github.com/citrix/terraform-provider-citrix.
 


	 
 


	
 


	 
 


	Terraform expects to be invoked from a working directory that contains configuration files written in the Terraform language. 
	Terraform uses configuration content from this directory to store settings, cached plug-ins and modules, and state data.
 


	You must initialize a working directory before Terraform can do any operations. 
	Initialize the working directory by using the command:
 


	
.tg  {border-collapse:collapse;border-spacing:0;width:100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}	
	
		
			
				
					
						PS C:\TACG&gt; 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.6.0"... 
						 - Installing citrix/citrix v0.6.0... 
						 - Installed citrix/citrix v0.6.0 (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&gt;
					 
				
			
		
	



	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 different configuration:
 


	For a Citrix Cloud site, configure the provider like this – you can find a guide for the creation of a Secure Client later in this document: 
 


	
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}	
	
		
			
				
					
						
							terraform { 
							    required_version = "&gt;= 1.7.4" 
							  
							  required_providers { 
							    citrix = { 
							      source  = "citrix/citrix" 
							      version = "=0.6.1" 
							    } 
							  } 
							} 
							 
						 

						
							# Configure the Citrix Cloud Provider 
							provider "citrix" { 
							cvad_config = { 
							  hostname      = "${var.CC_CustomerID}" 
							  client_id     = "${var.CC_APIKey-ClientID}"  
							  client_secret = "${var.CC_APIKey-ClientSecret}" 
							} 
							}
						 
					
				
			
		
	



	 
	For a Citrix On-Premises site, configure the provider like this:
 


	
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #000000;
    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 #000000;
    background-color: #000000;
    padding: 10px;
}

.table_component td {
    border: 1px solid #000000;
    background-color: #000000;
    padding: 10px;
    font-family: Courier New;
	font-size:11px;
}	
	
		
			
				
					
						
							onpremise
						 

						
							terraform { 
							    required_version = "&gt;= 1.7.4"
						 

						
							  
							  required_providers { 
							    citrix = { 
							      source  = "citrix/citrix" 
							      version = "=0.6.1" 
							    } 
							  } 
							}
						 

						
							  
							# Configure the Citrix On-Premises Provider 
							provider "citrix" { 
							cvad_config = { 
							  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):   
		For Citrix Cloud customers: Use this variable to specify the Secure Client´s Client-ID. 
		For Citrix On-Premises customers: Use this variable to specify the Domain-Admin Username. 
		You can set it via the Environment Variable CITRIX_CLIENT_ID. 
		 
	
	
		client_secret (String, Sensitive):   
		For Citrix Cloud customers: Use this variable to specify the Secure Client´s Client-Secret. 
		For Citrix On-Premises customers: Use this variable to specify the Domain-Admin Password. 
		You can set it via the Environment Variable CITRIX_CLIENT_SECRET. 
		 
	



	
		environment (String):   
		Only applicable for Citrix Cloud customers.  
		Available options: Production, Staging, Japan, JapanStaging. 
		You can set it via the Environment Variable CITRIX_ENVIRONMENT. 
		 
	



	
		disable_ssl_verification (Boolean):  Disable SSL verification against the target DDC 
		Only applicable to on-premises customers.  
		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 ensure your provider config is set for a known DDC hostname. 
		Configuring a valid certificate for the target DDC is recommended. 
		You can set it via the Environment Variable CITRIX_DISABLE_SSL_VERIFICATION. 
		 
	
	
		hostname (String) | Hostname/base URL of Citrix Delivery Controller    
		For Citrix Cloud customers (optional): This variable overrides the Citrix DaaS Service hostname. 
		For Citrix On-Premises customers (Required): Use this variable to specify the Delivery Controller hostname. 
		You can set it via the Environment Variable CITRIX_HOSTNAME.
	



	 
 


	IMPORTANT: 
	 
	The Citrix Terraform provider is currently in Tech Preview! 
	 
	All guides will be updated when the provider has reached RTM.
 


	 
 


	You can find all Terraform configuration files later on GitHub. We will update this guide when the GitHub repository is ready.
 


	 
 


	Creating a Secure Client in Citrix Cloud



	The Secure Client in Citrix Cloud is the same as the SPN in Azure. 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 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. 
 


	 
 


	Select the Identity and Access Management option from the menu to create an API client. 
 


	If this option does not appear, you do not have adequate permissions to create an API client.
 


	Contact your administrator to get the required permissions.
 


	
 


	 
	 
	 
 


	
		Click API Access, Secure Clients, and put a name in the textbox adjacent to the button Create Client. After entering a name, click Create Client:
	



	
 


	 
 


	
 


	
		Write down the ID and the Secret.
	



	You have now successfully installed Terraform and created the main prerequisites for using It in conjunction with Citrix DaaS and Citrix Virtual Apps and Desktops.
 

.tg  {border-collapse:collapse;border-spacing:0;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}

.tg  {border-collapse:collapse;border-spacing:0;width="100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top;width="100%;}

.tg  {border-collapse:collapse;border-spacing:0;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}

.tg  {border-collapse:collapse;border-spacing:0;width=100%;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:12px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-5jvq{width=100%;background-color:#0000c0;color:#ffffff;font-family:Consolas, "Courier New", monospace !important;font-size:12px;
  text-align:left;vertical-align:top}]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_06/deployment-guides-CCOnAWS-terraform-ec2-scheme.png.a7cbed4fdbb7ff4e4f485dcba68d8067.png" length="66790" type="image/png"/><pubDate>Mon, 01 Jul 2024 11:47:00 +0000</pubDate></item><item><title>Deployment Guide: Simplify new Teams deployment with Citrix</title><link>https://community.stage.citrix.com/tech-zone/build/deployment-guides/simplify-new-teams-deployment-with-citrix/</link><description><![CDATA[Overview



	The classic Teams for VDI will reach the end of support on October 1, 2024, and end of availability on July 1, 2025. 
 


	New Teams is provided in the MSIX package format, which can benefit organizations by improving security, streamlining application deployment, enhancing compatibility, optimizing resource utilization, and taking advantage of modern deployment features. Organizations can adopt MSIX as their preferred application packaging and distribution format.
 


	Citrix has worked with Microsoft to bring the new MSIX format to Citrix DaaS and Citrix Virtual Apps and Desktops so organizations can leverage this modern application deployment technology from a single interface. 
 


	 
 


	
		Note: 
	 

	
		You must install the Citrix Personalization component on VDA machines to support the delivery of MSIX and MSIX app attach packages. See Install the Citrix Personalization component on VDA machines for details.
	 



	To deploy new Teams, admins can utilize the App Packages node in the Citrix Web Studio console in Citrix DaaS or Citrix Virtual Apps and Desktops to perform the following quick and easy steps.
 


	 
 


	Download new Teams MSIX package



	Download the MSIX package for new Teams from the Microsoft website or another trusted source. Ensure you have the correct version that is compatible with your organization's requirements.
 


	
		Browse to the web page ‘Bulk upgrade to the new Microsoft Teams client’ https://learn.microsoft.com/en-us/microsoftteams/new-teams-bulk-install-client
	



	
 


	
		Under the section Option 1B: Download and install new Teams using an offline installer, select MSIX x64 to initiate the file download.
	



	
 


	 
 


	Store new Teams MSIX package on a network share



	For MSIX or MSIX app attach store the packages on a UNC, SMB network share, or an Azure File Share. 
 


	
		Copy the MSTeams-64.msix to the network share.
	



	
 


	 
 


	
		Note:
	 

	
		Make sure that the VDA has read permission on the package storage path:
	 

	
		
			If you store packages on a UNC or SMB network share in your AD domain, grant the VDA machine read permission to the storage path. To do so, you can give the machine’s AD account read permission to the share explicitly, or include the account in an AD group that has that permission.
		
		
			If you store packages on an Azure File Share, first grant a user account read permission to the storage path in Azure. Next, configure ctxAppVService running on the VDA machine to access the package storage path using that user account.
		
	



	Upload new Teams package into Citrix Web Studio



	
		In the left pane, select App Packages.
	



	
 


	
		On the Sources tab, click the Add Source button. The Add Source page appears.
	



	
 


	
		In the Name field, enter a descriptive name for the package source.
	



	
 


	
		In the Delivery group field, click Select a delivery group. Next, select a delivery group that meets the requirements stated in Preparation and then click OK.
	



	
 


	
		In the Location type field, select  Network share, and then complete the corresponding settings:
	



	
		Enter the UNC path of the network share. Example: \\ds-fs-01
	
	
		Select the package types that you want to upload. Select MSIX and MSIX app attach.
	
	
		Specify whether to search subfolders for packages.
	



	
 


	
		Click Add Source.
	



	
 


	
		Note
	 

	
		The Add Source page closes, and the newly added source appears in the source list. Citrix DaaS and Citrix Virtual Apps and Desktops uploads the packages to your environment using a VDA in the delivery group.
	 



	
		After the upload is complete, the Status field will show Import successful. 
	



	
 


	
		The corresponding packages appear on the Packages tab.
	



	
 


	 
 


	
		Add the new Teams app from the package to one or more delivery groups as needed. Users associated with those delivery groups can access the new Teams.
	



	
 


	
		On the Add Delivery Groups page, leave Select Applications enabled and select Next.
	



	
 


	
		In the Assign Delivery Groups, select the Delivery Group(s) to make applications accessible, and select Next.
	



	
 


	
		On the Add Delivery Group summary, select Finish.
	



	
 


	
		When users log in again, new Teams will be installed and available.
	



	
 


	
		To verify that Citrix HDX optimizations are enabled for Microsoft Teams, select Settings &gt; About Teams. 
	



	
 


	
		Note: 
	 

	
		For new Teams to be delivered to Microsoft Windows Server 2019, side-loading must be enabled. To do so, enable the Sideload apps option on the server at Settings &gt; Update &amp; Security &gt; For developers &gt; Use developer features.]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_06/image.png.999e662e554ba4f132de2681b0466cf4.png" length="406187" type="image/png"/><pubDate>Fri, 28 Jun 2024 15:16:27 +0000</pubDate></item><item><title>Tech Brief: Local Host Cache</title><link>https://community.stage.citrix.com/tech-zone/learn/tech-briefs/local-host-cache-ha-daas/</link><description>Overview



	Local Host Cache (LHC) is a Citrix technology that can maintain user access to resources during potential service disruptions. Local Host Cache leverages Site configurations cached locally on Delivery Controllers or Cloud Connectors to continue brokering operations when services may be unhealthy. Local Host Cache is a combination of several services and components which come together to take over the brokering responsibilities until the connection to the Cloud Broker or SQL database can be reestablished. In DaaS, Local Host Cache engages when Cloud Connectors lose connectivity with Citrix Cloud for 60 seconds. For customer-managed Sites, Local Host Cache engages when Delivery Controllers lose connectivity to the Site database for 90 seconds.
 


	For customers with on-prem StoreFront (regardless if using DaaS or a customer-managed environment), Local Host Cache is the main resiliency solution. Because Local Host Cache is critical to keeping users productive during service disruptions, it must be tested to ensure it is configured and functioning correctly. This guide will discuss how Local Host Cache operates, the services involved, the necessary Local Host Cache configurations, and various capabilities to test to improve the availability of your environment no matter what happens.
 


	Local Host Cache



	This article discusses the components, behavior, configurations, and testing required for Local Host Cache.
 


	Components



	There are several components within the Citrix Cloud Connector and Delivery Controller which are required for the Local Host Cache operations
 


	
		Configuration Synchronizer Service: The Configuration Synchronizer Service (CSS) periodically checks with the Cloud Broker (every 300 seconds) or Site database (every 300 seconds) to see if any configuration changes were made. The changes can be administrator-initiated (such as changing a delivery group property) or system actions (such as machine assignments). If changes are detected, CSS synchronizes the changes from the Cloud Broker or Site database to the Cloud Connectors or Delivery Controllers.
	
	
		LocalDB: The CSS imports the configuration data into a Microsoft SQL Server Express LocalDB database. A new instance of the database is created for every sync operation. Once the sync is successfully completed, the latest DB instance replaces the prior DB instance. Note: LocalDB has specific licensing that limits it to the lesser of four cores or a single socket. This can have a significant performance effect when the physical or virtual machine has been configured with multiple sockets with only a single or dual core. A broker machine with four sockets and one core per socket will limit LocalDB to using a single core, whereas the same VM configured as a 1-socket, 4-core machine means that LocalDB can access all four cores. See CPU core and socket configuration considerations for more information.
	
	
		
			High Availability Service: The High Availability Service (HA Service) is a specialized Broker Service that provides the runtime brokering functionality during an outage. The HA Service is also referred to as the secondary broker
		 
	
	
		
			Remote Broker Provider (DaaS only): The Remote Broker Provider has several important functions: 
		 

		
			
				
					It acts as a proxy relaying communication between the Citrix Virtual Delivery Agent (VDA) and the Cloud Broker
				 
			
			
				
					It acts as a proxy relaying communication between an on-premises StoreFront or an on-premises ADC and the various Citrix Cloud services
				 
			
			
				
					It determines when to switch a Resource Location between HA mode and normal operation
				 
			
		
	



	 
 


	
 


	 
 


	High Availability Mode



	Citrix Cloud Connectors and Delivery Controllers are capable of entering or exiting HA mode automatically without administrator intervention. For Citrix DaaS, HA mode is triggered after 60 seconds of failed health checks for the following services:
 


	
		Enumerations or launch requests
	
	
		Communications between the VDA and the Cloud Broker
	
	
		Secure Ticket Authority (STA) requests
	
	
		The OutageModeForced registry key set to 1
	



	For CVAD, HA mode is triggered when the Delivery Controllers cannot communicate with the SQL server for 90 seconds.
 


	During HA mode, the HA Service takes over several important brokering functions: it enumerates resources, brokers session launches, and accepts VDA registrations. In DaaS, the HA Service also acts as an STA provider. In a Resource Location or Zone with multiple Cloud Connectors or Delivery Controllers, a single Cloud Connector or Delivery Controller per Zone performs all brokering functions. To determine which broker will take over during the HA event, the HA Services communicate with one another as part of an election process. The elected broker, which is generally chosen in alphabetical order, becomes the primary broker during LHC events. Note: Delivery Controllers and Cloud Connectors must be able to communicate with each other to perform the election. If the brokers cannot communicate, you can end up with multiple elected brokers which can result in failed launch attempts during LHC.
 


	Entering and Exiting High Availability Mode



	CVAD



	HA mode is entered when the Broker Service cannot communicate with the SQL database for 90 seconds. During the HA event, the Broker Service continues to monitor the health of the Site database while the High Availability Service brokers connection. Once the Broker Service can re-establish connectivity to the Site database, the HA event concludes and normal operations are restored. 
 


	DaaS



	The decision to transition to HA mode is dependent upon health checks that test the ability to enumerate and launch traffic through a given Cloud Connector. There are several states during the entire cycle of entering and exiting HA mode:
 


	During the Working Normally state, all components are healthy and all brokering transactions are handled by the Cloud Broker. The CSS is actively replicating the configurations from the Cloud Broker to the Cloud Connectors.
 


	If any health checks fail, Cloud Connectors transitions to the Pending HA state. When in this state, a comprehensive health check is initiated to determine the next course of action. The connectors interact with other connectors in the Resource Location/Zone to determine their health status.
 


	The decision to move from Pending HA to Initial HA is based on the health status of all the connectors in a given Resource Location. If the health checks are successful, the connectors/controllers transition back to the Working Normally state. Alternatively if the health checks continue to fail, the connectors/controllers transition to the Initial HA state.
 


	During the Initial HA state, the High Availability Service on the elected connector takes over brokering responsibilities. All VDAs in the current Resource Location that were registered before will now register with the HA Service on the connector. It can take up to 5 minutes for all VDAs to re-register with the HA Service. At the end of Initial HA, health checks are initiated. If all health checks succeed, the state transitions to Pending Recovery, otherwise the state transitions to Extended HA.
 


	Health checks continue to occur during the Extended HA period. When health checks succeed during the Extended HA, the state transitions to Pending Recovery. There is no maximum time duration for a connector to remain in the Extended HA state
 


	Pending Recovery serves as a buffer period to ensure services are fully healthy before transitioning out of HA mode. If any of the health checks fail during Pending Recovery, the state transitions back to Extended HA.
 


	If all the health checks succeed during the entirety of the 10 minute Pending Recovery period, then the state transitions to Working Normally. With this transition, HA mode has exited, and all the VDAs in the Resource Location that were registered with the secondary broker now re-register with the primary broker. This re-registration can again take up to 5 minutes. 
 


	
 


	Behavior



	Behavior within Resource Locations and/or Zones



	The primary broker is designed to have a view of the whole deployment &#x2013; across multiple Resource Locations in DaaS or Zones in CVAD. However, when in HA mode, each Resource Location/Zone becomes its own independent pod, and the elected secondary broker in each Resource Location/Zone will manage the brokering transactions only for the VDAs within that pod.
 


	This design is a critical reason to ensure that the StoreFront is configured to include all the Cloud Connectors and Delivery Controllers from all the Resource Locations and Zones that contain VDA workloads. The StoreFront can then distribute launch requests and effectively load balance users across multiple Resource Locations/Zones.
 


	Citrix recommends a minimum of 2 connectors/controllers in every Resource Location/Zone. In each zone, there is an election process constantly running to make sure the HA Services know which machine would take over brokering responsibilities if there is an interruption. This election always happens &#x2013; both during normal operations and when running in HA mode.
 


	Broker Behavior



	The CSS routinely provides the secondary broker with information about all Cloud Connectors/Delivery Controllers in the Resource Location/Zone. Having that information, each machine knows about all peer machines running in the Resource Location/Zone. The secondary brokers communicate with each other on a separate channel. Those services use an alphabetical list of FQDN names of the machines that they're running on to determine the elected secondary broker in the Resource Location/Zone if an outage occurs.
 


	When in HA mode, the elected secondary broker takes over brokering responsibilities while the other secondary brokers in the zone actively reject incoming connection and VDA registration requests. If an elected secondary broker fails during an outage, another secondary broker is elected to take over, and VDAs register with the newly elected secondary broker. During HA mode, if a connector/controller is restarted:
 


	
		If that connector/controller is not the elected secondary broker, the restart has no impact.
	
	
		If that connector/controller is the elected secondary broker, a different Cloud Connector/Delivery Controller is elected, causing VDAs to register with the new elected secondary broker. After the restarted Cloud Connector/Delivery Controller powers on, it automatically takes over brokering, which causes VDAs to register again. In this scenario, performance can be affected during the registrations.
	



	The event log provides information about elections. For more information on the associated events, review the event logs article from the product documentation.
 


	VDA Behavior



	When the outage begins, the elected secondary broker does not have current VDA registration data, but when a VDA communicates with it a registration process is triggered. During that process, the elected secondary broker also gets current session information for that VDA. The VDA communicates with the broker at least every 5 minutes. Depending on when the last heartbeat was completed, it may take a VDA up to 5 minutes to realize the change from to the elected secondary broker and trigger the registration with the elected secondary broker. It is important to note the end user experience differs for pooled desktops versus dedicated desktops. For pooled desktops, users may reconnect as soon as one VDA re-registers with the secondary broker. For dedicated desktops, users have to wait for their specific desktop to re-register, which may take up to 5 minutes.
 


	While the elected secondary broker is handling connections, the remote broker provider monitors the connection to Citrix Cloud for DaaS or the primary broker service monitors the connection to the Site database for CVAD. When the connection is restored, the remote broker provider or primary broker service instructs the elected secondary broker to stop listening for connection information, and resumes conveying brokering operations to the Cloud Broker or Site database. The next time a VDA communicates with the connector or controller, another registration process is triggered. The elected secondary broker removes any remaining VDA registrations from the previous outage. The CSS resumes synchronizing information when it learns that configuration changes have occurred in the Site database or in Citrix Cloud
 


	There are specific considerations for power-managed pooled desktop VDAs when in LHC mode. Typically, when a user logs off of a power managed pooled desktop VDA, the VDA&#x2019;s image is reset to remove any user specific data or changes on the VDA. When a site is running in HA mode, because power management is unavailable, the reset operation cannot be performed. Due to the security consideration of previous user data and changes being available on subsequent sessions, these VDAs are unavailable by default during LHC. Depending on the security needs for an implementation, this behavior can be modified by modifying the Local Host Cache setting in Studio or setting the ReuseMachinesWithoutShutdownInOutage flag with PowerShell. More information about how to override the default behavior is available in the product documentation.
 


	Behavior with Resource Locations and/or Zones



	Load balancing within a Resource Location or Zone



	The on-premises StoreFront sends a heartbeat message to all the Cloud Connectors and/or Delivery Controllers configured in its store every 60 seconds by default. Only healthy Cloud Connectors/Delivery Controllers (that respond successfully to the heartbeat) are considered for load balancing app enumeration and launch requests.
 


	Resource Locations and Zones publishing the same resources



	It is common to have multiple Resource Locations or Zones publishing duplicate resources for redundancy. For example, a deployment containing applications from a single multi-session image or pooled VDI desktops might be deployed uniformly across all Resource Locations or Zones.
 


	When such a deployment is operating in HA mode, users may be directed to any of the VDAs in the various configured Resource Locations or Zones. In this scenario, the StoreFront load balances requests to all configured Cloud Connectors/Delivery Controllers across various Resource Locations/Zones.
 


	Resource Locations and Zones publishing different resources



	A Citrix deployment may also have certain applications available only in a specific subset of Resource Locations or Zones. For example, a Japanese OS desktop may be available only on the VDAs running in Japan. Another example is with static/assigned desktops that are user specific and tied to a specific Resource Location or Zone after assignment.
 


	When such a DaaS deployment operates in HA mode, the application or desktop launch requests need to be routed to the appropriate Cloud Connector in the specific Resource Locations where the resource resides. This is because cross-zone brokering is not available in HA mode. The AdvancedHealthCheck feature offered by StoreFront 1912 LTSR Cumulative Update 5 or later facilitates such deployments as described in the following paragraph. Advanced Health Checks are enabled by default for new installs starting in StoreFront 2203 LTSR CU5 and CR 2308, and is enabled by default for upgrades starting in StoreFront 2203 CU5 and later. 
 


	When AdvancedHealthCheck is on, the StoreFront enumerates resources from Cloud Connectors in any region. The enumeration information now contains a mapping between the resource (an application or a desktop) and the Resource Locations where the resource resides. This mapping is used to direct the user launch requests to specific Resource Locations. Review the configuration steps listed in the product documentation to enable the StoreFront to use this functionality.
 


	Behavior with Citrix NetScaler for load balancing



	For architectures involving Citrix NetScaler with Resource Locations/Zones publishing different apps and desktops, the following configurations need to be performed in order to successfully broker connections to the appropriate resources during HA mode.
 


	
		Aggregate the Cloud Connectors/Delivery Controllers in each Resource Location or Zone to a unique VIP in the NetScaler load balancer
	
	
		Set up the NetScaler load balancer to monitor the Cloud Connectors/Delivery Controllers in each Resource Location/Zone via the CITRIX-XD-DDC monitor
	
	
		Add all NetScaler VIPs as a single Resource Feed to StoreFront
	
	
		Enable the StoreFront AdvancedHealthCheck feature
	



	
 


	 
 


	 
 


	Configurations



	There are a few key configurations to be aware of when using Local Host Cache. While the main configurations are highlighted below, we recommend you review Avoid Common Misconfigurations that Can Negatively Impact DaaS Resiliency to ensure other configurations don&#x2019;t negatively impact LHC performance.
 


	Site



	For on-prem environments, from XenApp and XenDesktop 7.15 LTSR forward, Local Host Cache is enabled by default. You can validate that it is enabled using Get-BrokerSite on the Citrix Delivery Controller. Check that the LocalHostCacheEnabled property is True.
 


	
 


	For DaaS environments, Local Host Cache is always on.
 


	For performance considerations during LHC mode, no more than 10,000 VDAs can be supported in a single zone in CVAD or a single Resource Location in DaaS. No more than 40,000 VDAs can be supported in a single CVAD site. You can find more information in our product documentation for CVAD and DaaS. 
 


	As mentioned previously, by default, power-managed desktop VDAs in pooled delivery groups (created by MCS or Citrix Provisioning) are unavailable for new connections during a Local Host Cache event. You can change this default to allow those desktops to be used during Local Host Cache.
 


	Sizing



	During Local Host Cache mode, the Site switches to using SQL Server Express LocalDB versus the regular SQL database. A Cloud Connector or Delivery Controller&#x2019;s CPU configuration, particularly the number of cores available to the SQL Server Express LocalDB, directly affects Local Host Cache performance. 
 


	While LocalDB can use up to 4 cores, it is limited to a single socket. Maximizing the number of cores per socket is recommended - for example, if you have 4 vCPU placing them all in one socket.
 


	For more sizing recommendations, see size and scale considerations for Cloud Connectors and for Delivery Controllers.
 


	StoreFront



	All Cloud Connectors and Delivery Controllers that provide access to resources need to be listed in the &#x2018;Manage Delivery Controllers&#x2019; section of the StoreFront console. As discussed above, if the requisite Cloud Connectors or Delivery Controllers are not listed might result in loss of capacity when the site enters HA mode.
 


	
 


	If different resources are published from different Zones or Resource Locations, AdvancedHealthCheck needs to be configured. In StoreFront 2203 LTSR CU5+ and 2308+, this is enabled by default for new installations. The feature is enabled by default for upgrades starting in StoreFront 2402. If you are using an older version, you can go to C:\\inetpub\wwwroot\Citrix\*yourstorename* to access the web.config file for StoreFront. Be sure to do this on your primary StoreFront server. Open the file, and search for AdvancedHealthCheck. Change the setting to On. Save the file, and propagate changes to the rest of the server group.
 


	
 


	This change can also be done via PowerShell. 
 


	Example:
 

$storeService = Get-STFStoreService &#x2013;VirtualPath '/Citrix/Store'
Set-STFStoreFarmConfiguration $storeService -AdvancedHealthCheck $true


	NetScaler



	As mentioned previously, there are several key configurations to ensure that launches proxied via NetScaler work correctly when in LHC mode. 
 


	First, aggregate the Cloud Connectors/Delivery Controllers in each Resource Location or Zone to a unique VIP in the NetScaler load balancer. Each load balancer should monitor the brokers using the Citrix-XD-DDC monitor.
 


	
 


	Next, add all the load balanced VIPs as Delivery Controllers into StoreFront.
 


	
 


	Monitor Local Host Cache



	There are several different methods to monitor Local Host Cache behavior and performance. 
	Most information regarding LHC can be found in the event logs of Cloud Connectors or Delivery Controllers. See Event logs (DaaS) or Event logs (Citrix Virtual Apps and Desktops) for more information on what can be monitored.  
	When it comes to monitoring LHC, three different phases should be considered: before an event, during an event, and after an event.
 


	Before an LHC Event



	Before an event, you want to make sure your environment is prepared for an LHC event. A well-prepared environment for LHC can help improve performance during a service disruption. Here&#x2019;s what you can monitor before an LHC event to improve preparedness:
 


	Misconfigurations



	Misconfigurations should be monitored to ensure your environment is ready to perform optimally during an LHC event. Unfound misconfigurations in your environment can cause launch failures during LHC and an inability to access key resources. Some of the most common and impactful misconfigurations include:
 


	
		Power-managed pooled desktops being unavailable during Local Host Cache.
	
	
		Multiple elected Delivery Controllers or Cloud Connectors.
	
	
		Delivery Controllers or Cloud Connectors having only 1 vCPU core per socket.
	
	
		Advanced Health Check not enabled (DaaS only).
	



	For DaaS customers, review Avoid Common Misconfigurations that Can Negatively Impact DaaS Resiliency. You can also check the Zones node in Citrix DaaS Full Configuration to check if any errors or warnings were detected in your deployment. 
 


	Config Syncs



	The configuration sync process plays a pivotal role in ensuring up-to-date settings and resource assignments during an LHC event. If Configuration Syncs are failing, out of date settings and resources may be presented to users during LHC or LHC may be completely unavailable. 
	Monitoring your environment for failed config syncs can help improve readiness for an LHC event. Config syncs can be monitored in two different ways:
 


	
		Event logs (DaaS and CVAD)

		
			
				A 505 event from the Config Synchronizer service on Cloud Connectors or Delivery Controllers signifies a failed config sync. If a previous config is present, the old config will be used for LHC. Configurations will be out-of-date. If no config is present, LHC will fail to be able to service enumeration and brokering requests.
			
			
				See Config Synchronizer Service for more information.
			
		
	
	
		Citrix Monitor (DaaS only)
		
			
				Citrix Monitor on DaaS has an out of the box alert for consecutive Config sync failures that administrators can configure to their preferred notification medium and recipients for quick, timely analysis. 
			
			
				Learn more on Local host cache config sync failure alert monitoring.
			
		
	



	
 


	HA mode starting



	It&#x2019;s important to be alerted that an HA event is occurring in your environment. The best way to monitor your environment for an HA event is through 3502 events from the High Availability Service on your Cloud Connector or Delivery Controller.  
	Citrix DaaS has additional ways for identifying that a Resource Location is in Local Host Cache mode. It is important to note that due to delays in reporting and the potential for DaaS Full Configuration to be unavailable, the most reliable way to monitor for an LHC event is the event logs.  
	In Citrix DaaS, there are additional alerts around HA mode starting.
 


	
		Local Host Cache banner
		
			
				When Full Configuration identifies that Connectors in a Resource Location are unavailable, it displays a banner in Full Configuration letting you know Resource Location(s) are in LHC.
			
		
	



	
 


	
		Local Host Cache zone-level alert
		
			
				If a Resource Location is unavailable, the Zones node will show a warning icon for that resource location.
			
		
	



	
 


	During an LHC Event



	During an LHC event, while your team may be working to understand and troubleshoot the root cause of why LHC mode was entered, you should also be monitoring the performance of LHC to make sure there are no issues that prevent users from accessing their apps and desktops.  
	You have limited access to the PowerShell SDK when in LHC mode. The Get-Broker* cmdlets can be a powerful tool for monitoring the status of resources during LHC. See &#x201C;What is unavailable during an outage, and other differences&#x201D; (DaaS and CVAD) for more information on enabling and using the PowerShell SDK during an LHC event. All information obtained from the PowerShell SDK is on a per-zone basis because all zones act as independent sites when in LHC mode. 
	Three main things should be monitored during an LHC event: VDA registration, session launches, and LHC state.
 


	VDA Registration



	VDAs can take up to 5 minutes to re-register when entering and exiting LHC mode. The impact of the 5 minutes can be more impactful for dedicated desktops, where users have to wait for their specific VDA to register. Multi-user and pooled desktop VDAs become available to users as soon as a VDA in the Delivery Group is available. The more VDAs in a Delivery Group, the higher the likelihood that a VDA will be available earlier on in the 5 minutes, minimizing the disruption of entering and exiting LHC. 
	To check the registration status of VDAs during the LHC event, run the following PowerShell cmdlet after enabling the PowerShell SDK for LHC:
 

Get-BrokerMachine -AdminAddress localhost:89 | Select MachineName, ContollerDNSName, DesktopGroupName, RegistrationState


	This will return a list of VDAs and their registration state.
 


	User sessions



	End users launching their apps and desktops is the most critical part of the operation. During LHC, it&#x2019;s important to monitor session launches so you know that things are running smoothly. Sessions can be monitored using the event logs or PowerShell.
 


	
		Event logs
		
			
				3507 events on the elected Cloud Connector or Delivery Controller from the High Availability service give summary information regarding the current state of the LHC event. Information includes net-new sessions launched, sessions reconnected, VDA registrations, peer broker state, and event duration.
			
			
				These events occur every 2 minutes when LHC mode is active, giving you information about the event as a whole as well as information about the last 2 minutes of the event. 
			
		
	



	
 


	
		PowerShell
		
			
				Get-BrokerSession can be used once the PowerShell SDK is enabled to learn more about active sessions in the zone.
			
		
	



	Local Host Cache States



	For Citrix DaaS, LHC goes through states before transitioning back to a Working Normally state. Understanding these states, and the implications about health checks behind those states, can make troubleshooting issues more straightforward. The states are discussed in more detail in a previous section. LHC states can be monitored using the following events from the Remote Broker Provider service on your Cloud Connectors, with these events being the most critical:
 


	
		3001: Notifies you that a health check failed and that the connector is currently in a Pending HA state. Connectors make a decision in 60 seconds after this 3001 event to determine if LHC should be entered or not based on the results of a health check. While a single 3001 event is not a cause for concern, frequent 3001 events in a short period of time can be an indicator of intermittent network issues. You may want to force LHC if your environment is experiencing user access issues due to intermittent network issues.
	
	
		3003: Notifies you of a change of HA state.
		
			
				Extended HA to Pending Recovery: Your connector passed a health check while in the Extended HA state. Your connectors will need to pass health checks for 10 minutes straight before exiting LHC.
			
			
				Pending Recovery to Extended HA: During Pending Recovery, your connectors have failed a health check. Connectors will remain in Extended HA until another health check is passed. The 10 minute recovery timer will be restarted when the connector passes a health check and re-enters the Pending Recovery state.
			
		
	



	Understanding these states can help give insight into the success of your troubleshooting efforts. If a troubleshooting step successfully mitigated the issue, you should not expect your environment to leave LHC immediately. Instead, you should see your connectors transition into Pending Recovery. 
 


	After an LHC Event



	After an LHC event you should review how LHC performed to determine if any changes need to be made to improve the performance of future events. Information regarding the performance of LHC during an event can be found in the Citrix Monitor (DaaS only) or the event logs.
 


	
		Event logs
		
			
				3508 events from the High Availability service give a summary of session launch and VDA registration information of an entire LHC event once the event is complete. This is the data that feeds the LHC Monitor dashboard.
			
		
	



	
 


	
		DaaS Monitor
		
			
				The LHC dashboard in the trends view of Monitor allows for visibility into past LHC events. Information in the dashboard includes the time of the incident, duration, impacted Zone, user sessions, and number of machine registrations.
			
			
				Learn more - Monitor - trends
			
		
	



	
 


	Test Local Host Cache



	Verify Functionality



	Before testing LHC, it is important to verify that the CSS service is operating successfully.  
 


	1. Ensure that synchronization imports are completed successfully. Check the event logs for 504 events from the Citrix Config Synchronizer Service.
 


	
 


	2. Ensure each Cloud Connector or Delivery Controller creates the Local Host Cache database. This confirms that the High Availability Service can take over if needed.
 


	     a. On Cloud Connectors or Delivery Controllers, browse to c:\Windows\ServiceProfiles\NetworkService.
 


	     b. Verify that HaDatabaseName.mdf and HaDatabaseName_log.ldf are created.
 


	
 


	3. Before going into Local Host Cache, it&#x2019;s important to understand that when LHC engages, a single Cloud Connector or Delivery Controller services all brokering requests. This Cloud Connector or Delivery Controller is referred to as the elected broker. 
 


	     a. To determine which server is acting as the elected broker, review Delivery Controllers or Cloud Connectors for 3504 events from the Citrix High Availability Service.
 


	     b. If multiple brokers are elected, VDA registrations may be split across brokers during the LHC event and launch attempts may fail. Ensure VDAs can communicate with each other over port 80 to enable the connector/controller communication required for LHC elections.
 


	
 


	Force LHC



	It&#x2019;s important to note that testing LHC can cause an intermittent service disruption in your environment due to VDAs re-registering when entering and leaving LHC mode. LHC should be tested during a maintenance window.
 


	To simulate Local Host Cache behavior, follow these steps: 
 


	1. Force an outage on the Cloud Connectors or Delivery Controllers. This step must occur on every broker in the zones that will be tested. There are two ways to do this:
 


	     a. Registry key:
 


	          i. In HKLM\Software\Citrix\DesktopServer\LHC, create and set OutageModeForced as REG_DWORD to 1. This setting instructs the Local Host Cache broker to enter HA mode, regardless of the state of the connection to Citrix Cloud or Site database.
 


	
 


	     b. PowerShell cmdlet:
 


	          i. Import the module by running the following command on the Delivery Controllers or Cloud Connectors in the zone where you&#x2019;re testing: cd C:\Program Files\Citrix\Broker\Service\ControlScripts Import-Module .\HighAvailabilityServiceControl.psm1
 


	          ii. Run the following PoSh cmdlet on the Delivery Controllers or Cloud Connectors in the zone where you&#x2019;re testing: Enable-LhcForcedOutageMode
 


	2. Validate an outage has been forced by checking for a 3502 event on the elected broker:
 


	
 


	Monitor VDA Registration



	You can verify and monitor VDA registration during LHC mode using the following steps: 
 


	1. Once in Local Host Cache mode, if you&#x2019;re running CVAD, Studio won&#x2019;t be available. If you&#x2019;re running DaaS, all VDAs will appear unregistered in Manage. Knowing your VDAs are registered is a key function for ensuring LHC works correctly. Note: VDAs can take up to 5 minutes to re-register with the elected broker.
 


	     a. To run Get-Broker* cmdlets during Local Host Cache, run the following on a machine with the remote PoSh SDK installed for DaaS or directly on the Delivery Controller for CVAD.
 


	          i. In HKLM\Software\Citrix\DesktopServer\LHC, create and set EnableCssTestMode as REG_DWORD to 1.
 


	
 


	          ii. For DaaS, run the following before running PoSh cmdlets so cmdlets are run on the Cloud Connector rather than the Cloud broker: $XDSDKAuth=&#x201D;OnPrem&#x201D;
 


	          iii. To view machine registration information, run Get-BrokerMachine -AdminAddress [elected broker FQDN]:89 | Select MachineName, ControllerDNSName, DesktopGroupName, RegistrationState. For CVAD environments, you can replace [elected broker FQDN]                 with localhost as this will be run directly on the Delivery Controller.
 


	
 


	     b. After running those commands, you can access All Get-Broker* cmdlets.
 


	     c. More general VDA registration information can be found in 3507 events on the elected broker.
 


	
 


	Test and  monitor launch behavior



	1. Test launches from StoreFront. Users should be able to authenticate, enumerate, and launch applications like normal.
 


	     a. Information regarding sessions launched during LHC can be found in 3507 events on the elected broker, which occur every two minutes.
 


	     b. Note: During the first 5 minutes of an LHC event, VDAs may be re-registering, which can result in failed launch attempts due to a lack of resources. Once all VDAs register, further launches should succeed as normal.
 


	2. Situations to test:
 


	     a. Different resource types:
 


	          i. Pooled desktops
 


	          ii. Static Desktops
 


	          iii. Hosted apps
 


	     b. Subset of zones in LHC: Test what happens if only one of your zones enters LHC mode.
 


	     c. Zone preference and machine tags
 


	          i. Zone preference and restricting launches to a single zone using tagging is not supported during LHC. Launch requests may go to other zones, resulting in a subset of launch failures. This issue can be amplified if using failover order in StoreFront rather than                   load balancing. Test this and see how it works in your environment!
 


	Disable Forced LHC



	Once you have finished testing LHC, you can disable the Forced LHC mode. 
 


	1. Disable LHC
 


	     a. Note: When exiting LHC mode, VDAs must re-register with the healthy primary broker. VDA re-registration should be completed within 5 minutes of disabling LHC.
 


	     b. Registry key:
 


	          i. In HKLM\Software\Citrix\DesktopServer\LHC, set OutageModeForced as REG_DWORD to 0. This setting instructs the Local Host Cache broker to leave HA mode.
 


	
 


	     c. PowerShell cmdlet:
 


	          i. Import the module by running the following command on the Delivery Controllers or Cloud Connectors in the zone where you&#x2019;re testing: cd C:\Program Files\Citrix\Broker\Service\ControlScripts Import-Module .\HighAvailabilityServiceControl.psm1
 


	          ii. Run the following PoSh cmdlet on the Delivery Controllers or Cloud Connectors in the zone where you&#x2019;re testing: Disable-LhcForcedOutageMode
 


	2. Review Delivery Controllers or Cloud Connectors for 3508 events to get a summary of the LHC event.
 


	
 


	Summary



	As you can see, Local Host Cache plays a critical role in your environment. By understanding how Local Host Cache operates, and testing its functionality, you can ensure that your environment is resilient and highly available even in the event of service disruptions. Ensure your users can always connect to their resources by testing your environment today!
 


	Please refer to the resources below to learn more about Local Host Cache.
 


	Resources



	Product Documentation: DaaS - Local Host Cache
 


	Product Documentation: CVAD - Local Host Cache
 


	Tech Brief: Avoid Common Misconfigurations that Can Negatively Impact DaaS Resiliency
 


	Citrix Tech Insight: Citrix DaaS High Availability with Local Host Cache</description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_02/tech-briefs_local-host-cache-ha-cvads_citrix-cloud-regular-originalres.png.382c3c78a3b8701199d0e222f604d8b2.png" length="117744" type="image/png"/><pubDate>Tue, 18 Jun 2024 18:50:00 +0000</pubDate></item><item><title>Citrix Provisioning on Azure</title><link>https://community.stage.citrix.com/tech-zone/design/reference-architectures/pvs-on-azure/</link><description><![CDATA[Audience



	Citrix customers have relied on Citrix Provisioning (PVS) technology for many years to provide best-in-class image management solutions for Citrix environments. Many of these customers have added Azure workloads to their Citrix solutions and want to integrate Citrix Provisioning on Azure with their on-premises solutions.  Many other Citrix customers who have not used Citrix Provisioning on-premises before are interested in what Citrix Provisioning on Azure will offer regarding operational agility and cost optimization.  This reference architecture is intended to provide both of these groups a blueprint for how and why to implement Citrix Provisioning on Azure.
 


	Objectives of this document



	This reference architecture provides a blueprint for the design of a Citrix Provisioning on Azure solution based on several overriding tenets or “Design Pillars”:
 


	
		Resiliency
	
	
		Security
	
	
		Cost optimization
	
	
		Operational Excellence
	
	
		Performance Efficiency
	



	Citrix Provisioning on Azure can be architected in many ways. In the following sections, an earnest attempt will be made to define an example architecture based on a set of requirements and assumptions while explaining the reasons major decisions were made and where customers can make different design decisions based on differing requirements.
 


	This document will provide information pertinent to deploying Citrix Provisioning on Azure.  It will not provide exhaustive documentation on all aspects of Citrix Provisioning, as there is a large base of information already documented for this purpose.  The following list of documentation should also be referenced for those new to Citrix Provisioning:
 


	
		Tech Brief: Citrix Provisioning
	
	
		Reference Architecture: Image Management
	
	
		Reference Architecture: Citrix DaaS – Azure
	
	
		Create Citrix Provisioning catalogs in Citrix Studio
	
	
		Official Documentation - Citrix Provisioning on Microsoft Azure
	
	
		Migrate workloads between Resource Locations using Image Portability Service
	



	Advantages of the Citrix Provisioning Architecture



	Customers love to see Citrix Provisioning in their Citrix Solutions for many reasons.  The highlights of this architecture include:
 


	
		Ability to manage non-persistent images quickly and efficiently

		
			
				Full Image Versioning
			
			
				Simple Image Replication
			
			
				Near instant rollout and rollback
			
		
	
	
		Very Highly Available solution based on Active/Active Server Technology
	
	
		Fully automatable using PowerShell
	
	
		High-Performance Storage solution using Cache in RAM technology
	
	
		Easy to integrate into a hybrid cloud model using Citrix App Layering or the Citrix Image Portability Service
	
	
		Advantages of Citrix Provisioning within Azure
		
			
				Azure provides standard, well-tested, high-performance networking on which to build the Citrix Provisioning infrastructure
			
			
				Citrix Provisioning requires less storage than most other solutions
			
			
				Citrix Provisioning provides nearly instant scaling of resources when used in Azure
			
		
	



	Citrix Provisioning on Azure Design Considerations



	As stated above, this architecture will be developed based on the above Design Pillars.  For an overview of how the architecture maps to these pillars, you can jump to the Design Pillars section of the document.
 


	To illustrate design options and recommended solutions, two different environments will be discussed: one at scale and one of more modest size.
 


	VDI at Scale



	The first design to be addressed is VDI at scale.  This architecture is based on PODs and can be used for solutions from 5K desktops to 100K desktops or more.
 


	The components of the solution include:
 


	
		One or more Azure Tenants
	
	
		An Azure Landing Zone or Hub Zone for Citrix workloads
	
	
		One Azure Subscription per 5K VDI Desktops 
	
	
		At least two Azure Availability Zones per Azure Region
	
	
		One or more Azure vNets per Azure Subscription
	
	
		vNet Peering from workload vNets to Landing Zone/Hub vNets
	
	
		One Citrix Provisioning Farm per Azure Region
	
	
		One Citrix Provisioning Site per Availability Zone
	
	
		One Citrix Provisioning server for every 1500-2000 Targets
	
	
		N+1 Citrix Provisioning Servers Per Site
	
	
		Appropriate naming conventions
	
	
		One Citrix Cloud Resource Location per 10K Desktops per Azure Region
	



	The following diagram illustrates the major components of the Citrix Provisioning on Azure architecture for VDI at scale.
 


	
 


	High-Level Citrix Provisioning on Azure Architecture for VDI at Scale
 


	In the following sections, each component of the architecture will be discussed.
 


	Azure Tenants and Subscriptions



	While using a multi-tenant Azure design for Citrix within Azure is possible, customers usually choose to manage all Citrix components within a single Azure tenant. From a Citrix Provisioning perspective, this is not a requirement.
 


	For a large-scale deployment, multiple Azure subscriptions will be required. Typically, a separate subscription is used for the Azure Landing Zone, which can be considered the hub in a hub and spoke topology where the Citrix workload subscriptions are deployed as spokes. Currently, Citrix supports up to 5K targets per Azure subscription.  These limits are based on several factors, but power management is usually the limiting factor based on the number of API calls that can be made into the Azure infrastructure at the subscription level.   Citrix is constantly working with Microsoft to increase these limits, so always check the DaaS limits page to see if this has changed.
 


	For customers that need to scale past 10K VDI desktops per Azure Region, multiple Resource Locations would be provisioned in the same region, conforming to the POD design shown here.
 


	Landing Zone/Hub Site



	The Landing Zone is where shared components will be deployed in this solution.  This includes services like:
 


	
		Citrix Cloud Connectors
	
	
		Active Directory Domain Controllers
	
	
		File Services
	
	
		Citrix Provisioning Servers
	



	The Landing Zone will be provisioned with one or more vNets.  The critical aspect of this to the Citrix design is that there should be direct vNet peering between the Citrix worker vNets and the vNets used for Citrix Provisioning servers as the Citrix Provisioning servers need direct high bandwidth low latency network connections to the targets (VDAs)
 


	Networks



	The virtual networks used for Citrix workloads could be separated by Availability Zone (AZ) or stretched between Availability Zones.  In this architecture, they are stretched to make management more effortless.  Since the design is for 5K desktops per subscription, a /19 network can be used as it can accommodate up to 8K addresses. For customers that plan to grow very large in the future, and based on the fact that Citrix keeps increasing the number of desktops that can be managed in a single subscription, it may make sense to provision a /17 vNet that can accommodate up to 32K addresses so that the solution can support future growth without being rebuilt.
 


	Within the vNet in each Availability Zone, a /20 subnet will be created to accommodate the 2.5K desktops deployed in that zone.
 


	Citrix Resource Locations



	A Citrix Resource Location defines the boundaries of a Zone in Citrix Cloud.  It is defined by a set of Cloud Connectors that act as gateways for information between the “Resource Locations” and Citrix Cloud, including the communications between VDAs and Delivery Controllers, StoreFront servers and Delivery Controllers, Citrix Cloud, and Active Directory, etc.  Cloud Connectors within the same Resource Location share the same scope of resources used to provide Local Host Cache functionality, which offers high availability services if the cloud connectors are disconnected from the Citrix Cloud Resources.  It is the Local Host Cache services that govern the limit of the number of desktops that can be provisioned within a single Resource Location.  This number is 10K VDI desktops or 1K virtual app servers with up to 25K multisession sessions.
 


	Therefore, for every 10K desktops required in an Azure Region one Citrix Cloud Resource Location will be provisioned.  For more information on these limits, see the local host cache documentation.
 


	The number of Cloud Connectors to provision in a Resource Location will depend on several factors, including:
 


	
		Access Method

		
			
				Gateway/StoreFront
			
			
				Gateway Service/Workspace
			
		
	
	
		Connection Method
		
			
				Cloud Connectors
			
			
				Rendezvous 
			
		
	
	
		Risk Tolerance
	



	Three Cloud Connectors per Resource Location are recommended, along with using Rendezvous for Gateway Service customers.  Three connectors allow for a failed upgrade without losing high availability for the Resource Location. If Gateway Service will be used and Rendezvous will not be used, then more Cloud Connectors may be required to handle the load of routing the HDX traffic from the Gateway Service to the VDAs.
 


	Citrix Provisioning Farms, Sites and Servers



	One of the primary considerations for this architecture is the Citrix Provisioning Farm and Site architecture.  While it is technically possible in Azure to stream targets across availability zones, here, we do not want to do that because, of course, it is likely that Availability Zones could be disconnected from each other, and high availability is one of our core tenets.  Citrix Provisioning will be designed with a Farm per Azure Region with separate sites per Availability Zone to keep the traffic local to each Availability Zone. By having one Farm in the Primary region and one Farm in the DR Region, configurations can be updated even if one of the Farm databases is down.  Of course, Citrix Provisioning will also be configured to “Enable offline database support” so that targets will keep working even if the database is down.
 


	Citrix Provisioning is fairly flexible when designing the number of servers.  The most significant load on Citrix Provisioning servers is when many targets are booted at the same time.  If there is more load, some targets must wait while others boot.  Therefore, the tradeoff is between the cost of servers and the time it takes to boot targets at scale.  A good rule of thumb is to limit Citrix Provisioning server to between 1500 and 2000 targets per server.  In this design, we have included four servers for 5K desktops.  This allows for the typical case where there will be 1,250 targets per server; if there is a server failure, there will be 1,667 targets per server.  
 


	The servers here will be configured with four cores and 32 GB of RAM.  The amount of RAM required is based on the number of vDisks that will be streamed.  The general rule of thumb is to provision 2 GB for the OS and 2 GB for every vDisk to be streamed.  That means that an Azure instance that is 4x32 can handle streaming up to 15 vDisks. This is more than enough for most Citrix Provisioning on Azure deployments.
 


	Of course, in Azure, choosing the best instance type for your Citrix Provisioning server is critical. It is essential to look at not only CPU and memory but, more importantly, network and disk throughput. Citrix Provisioning servers on Azure require premium storage, so only instance types that support it should be considered.
 


	If we compare the following two instance types, they have 4x16 instances and the same pricing, but the D4s_v5 has better network throughput.
 


	
 


	
 


	 
 


	If more memory is required, Azure has memory-optimized images, and the same is true here, where the newer instance type has more network throughput. 
 


	 
 


	
		Note:
	 

	
		When compared to Pay-As-You-Go Pricing on the day this document was written, these memory-optimized instances were about $70 more expensive per month.
	 



	 
 


	
 


	
 


	It’s worth noting that the pricing increases linearly in Azure as you go up in CPU and memory.  In our examples, the 8x32 instances cost exactly double what the 4x16 instances cost. 
 


	To decide on an instance type, determine how much memory your Citrix Provisioning servers will need based on the number of vDisks to stream. Then, find an appropriate instance with the best network performance. The following table lists memory requirements based on the number of vDisks streamed. These instances were chosen based on memory, price, and network throughput.
 


	
		
			
				
					#vDisks Required &lt;=
				 

				
					 
				 
			
			
				
					Mem Req’d (GB)
				 

				
					 
				 
			
			
				
					Instance
				 

				
					 
				 
			
			
				
					Price Per Month*
				 

				
					 
				 
			
			
				
					Net Throughput (Mb/s)
				 

				
					 
				 
			
		
		
			
				
					7
				 
			
			
				
					16
				 
			
			
				
					D4s_v5
				 
			
			
				
					$51.86
				 
			
			
				
					12500
				 
			
		
		
			
				
					15
				 
			
			
				
					32
				 
			
			
				
					E4ds_v5
				 
			
			
				
					$79.89
				 
			
			
				
					12500
				 
			
		
		
			
				
					31
				 
			
			
				
					64
				 
			
			
				
					E8ds_v5
				 
			
			
				
					$159.78
				 
			
			
				
					12500
				 
			
		
	



	* Pricing based on 3-year reserved pricing in East US
 


	Azure pricing can change based on the day and the Region.  All of these instances would be suitable for Citrix Provisioning servers, and checking for pricing and availability should be mandatory when provisioning your Citrix Provisioning servers. Pricing should be checked periodically throughout the life of the Citrix Provisioning services.
 


	Storage costs are another factor to consider when designing the number of servers based on CPU and Memory.  Storage costs will depend on the type of storage used and the number of stores required.  See the storage section for more details.
 


	Citrix Provisioning vDisk Stores



	Citrix Provisioning supports vDisk storage on a local file system or a network share.  In this design, we recommend using disk stores deployed on local SSD disks attached to each Citrix Provisioning server.  This provides a very high level of redundancy because each server has its own copy of the vDisk store.  If a network share is used, then that share becomes a single point of failure for the Citrix Provisioning site.  While Citrix Provisioning supports Azure Files and Azure Netapp Files, both of which have good to very good redundancy, a vDisk could still be corrupted or deleted by accident, causing an outage.  This is less likely to be a significant issue if replicated local storage is used for the Citrix Provisioning stores.  If shared storage is used, it should be split into availability zones so there is at least redundancy between AZs in the design.
 


	Citrix Provisioning vDisk Replication



	In the section above, using local vDisk stores was recommended.  If that choice is followed, then the vDisks in the stores added to each Citrix Provisioning server must be synchronized.  Many articles and processes have been developed to synchronize vDisks over the years.  This includes using DFS commercial replication solutions and any number of scripted solutions that have primarily relied on Microsoft's robocopy utility to synchronize the stores.  The vDisk Replication Utility documented here can replicate vDisks between servers within the same site or farm and across sites and farms.  It will also add the vDisk into Citrix Provisioning in a different Farm.  That said, the various solutions can replicate vDisks between local Stores.
 


	Smaller Deployment Site Options



	What if only one to two thousand VDAs are required? 
 


	There are several options here.  The first option is to follow the same design as the large-scale design above, just lowering the number of Citrix Provisioning servers required for each Zone.
 


	
 


	This provides a high degree of redundancy but with a higher cost as more Citrix Provisioning servers are provisioned than required for the number of VDAs to maintain high availability within each Availability Zone.
 


	Another option is to use a single Citrix Provisioning site across Availability Zones with a single Citrix Provisioning server per Availability Zone. This option trades off the localization of network access against the cost of additional Citrix Provisioning servers.
 


	
 


	The cost of the Citrix Provisioning servers when using an E4ds_v5 instance with 2TB of Standard SSD storage is approximately $340 per month, so adding two servers would add $680 per month to the solution.
 


	Supporting a single Citrix Provisioning site will also be easier operationally than supporting multiple, and each organization must decide how much redundancy is required to meet its high availability needs.
 


	Citrix Provisioning Database Options



	There are many options for providing SQL services to Citrix Provisioning within Azure. While designing for SQL, remember that Citrix Provisioning has an option to “enable offline database support.” This allows the servers to keep streaming vDisks if the database goes down. It is still a best practice to provide a highly available database for Citrix Provisioning to maintain management operations using an HA design.  
 


	The following SQL choices are available when using Citrix Provisioning in Azure.
 


	
		Azure SQL Database
	
	
		Azure SQL Managed Instance
	
	
		Microsoft SQL Server 2017, 2019, and 2022.
		
			
				Standalone
			
			
				Database mirroring
			
			
				Always on failover with or without multi-subnet failover
			
		
	



	Most of these are well documented elsewhere, but if your organization wants to move to a more cloud service-centric approach, Azure SQL Database may be a good choice for Citrix Provisioning on Azure as it can provide a cost-effective way to provision a highly available, local SQL instance that adds minimal requirements for maintenance and upkeep.
 


	Azure SQL comes in two forms—one where you pay by cores and one by transactions (DTU).  If only the Citrix Provisioning database were provisioned in Azure, the solution that would make the most sense would be an Azure SQL Database, which provides a fully Microsoft-managed, highly available service for a single database.  Also, if you only use Azure SQL for Citrix Provisioning, using the DTU model for the service will probably make sense.  Check with your DBA team to determine the best approach for using Azure SQL in your organization.
 


	Citrix License Server



	Citrix Provisioning currently relies on an on-premises license and requires a Citrix License Server. If your organization already has an on-premises license server that will remain in the environment, it can be used for licensing in Azure.  Citrix Provisioning servers will continue to work without interruption for 30 days if they cannot contact the license server for any reason.  If the license server is only used in Azure and only for Citrix Provisioning, then the virtual machine instance used for the license server can be configured modestly.  Consider something like the D2_v4 instance type, a 2-core 4 GB RAM instance with a minimal monthly cost.
 


	Always use the latest version of the license server.  Open port 27000 from the Citrix Provisioning servers to the license server.  The license server requires telemetry to be enabled so that the license server will require internet access open to port 443 for https://cis.citrix.com.
 


	The license server should be backed up or replicated for recovery. If the server fails, there is a 30-day grace period for recovery.
 


	Migration Options



	Many organizations moving to Azure are already using Citrix Provisioning on-premises.  Depending on the technology you use to deploy Citrix Provisioning images, Citrix has a solution to ease the migration to Azure.  Several options exist for architecting the new solution in Azure if Citrix App Layering is used.  See the blog App Layering in Azure 2023 and Beyond for more details.
 


	If you use a different method of managing Citrix Provisioning images, the Citrix Image Portability Service (IPS) can convert an on-premises image into an image appropriate for Citrix Provisioning in Azure. IPS is a scripted process that can be used for a one-time migration of vDisks into Azure or as a means to synchronize on-premises images to Azure regularly. See the standard documentation in the Image Portability Service and the APIs to Manage the Image Portability Service.
 


	General Notes



	Many pieces of information are essential to know when designing and deploying Citrix Provisioning on Azure that did not apply to the architecture sections of this document, but they are nevertheless critical.  These will be listed here:
 


	
		Citrix Provisioning on Azure Limitations (Not Supported)

		
			
				Supported with 2203 LTSR, 2402 LTSR, Current Release
			
			
				Windows Server 2012R2 and earlier Targets are not supported
			
			
				Generation 1 VMs are not supported
			
			
				vDisk Update Management
			
			
				Printer Management
			
			
				When Citrix Provisioning is integrated with a customer-managed Delivery Controller, removing VMs, catalogs, or AD accounts from the console is not supported.
			
			
				Trusted Launch (Secure Boot and vTPM)
			
		
	
	
		Only BDM Boot Disks disks are supported
		
			
				No PXE or ISO
			
		
	
	
		An Azure Feature Flag for “ReserveMacOnCreateNic” is required to be set on the Azure subscription.  This feature tells Azure to bind the Mac Address to the VM sooner.
	
	
		The Azure machine size used when creating the master VM must be compatible with that used when creating target VMs. Only Generation 2 VMs are supported. This includes the following:
		
			
				The presence or absence of a temporary disk must be the same
			
			
				The presence or absence of a GPU must be the same
			
		
	
	
		When using a Citrix Provisioning wizard to create targets, if the VMs have a temporary disk, creating new targets can use batch sizes of 200 VMs created in parallel.  However, if no temp disk is available on the targets, only 20 targets can be created simultaneously.  If no temp disk is available, set the following REG_DWORD to 10
		
			
				HKCU\Software\Citrix\ProvisioningServices\VdiWizard\MAX_VM_CREATE_THREADS
			
		
	
	
		For targets provisioned using the new Studio provisioning, the default deployment size will be 10 targets in parallel, but this will soon be increased to 500.
	
	
		Master Image cannot include “Plan Information”
		
			
				Meaning that a Citrix Provisioning Image cannot be made from a third-party Marketplace image
			
		
	
	
		If using Hybrid Entra ID/AD targets, also use Studio provisioning; otherwise, targets must wait up to 90 minutes to join the Entra ID after boot.  Studio provisioning will store the Entra ID join in the Identity Disk, making it available immediately.
	
	
		The Citrix Provisioning console only supports Active Directory integration for Admins.
	
	
		Both IPv4 and IPv6 are supported.  However, IPv6 is supported on 2311 and is newer for streaming only, and it can’t be used yet with studio provisioning.
	
	
		Accelerated Networking should be used on both the Citrix Provisioning server and Targets
	



	Virtual Machine Costs Comparison



	To help customers understand the differences in storage requirements between Citrix Provisioning in Azure and various configurations of MCS, we have outlined the options below for different types of provisioned systems.  The costs defined here were obtained using the Azure pricing when this document was written.  The costs below are based on 3-year reserved and pay-as-you-go pricing in Azure US East that can change at any time, but it should provide an understanding of the cost categories involved in the overall solution.  Since SQL would not be required if using MCS, we have also included base pricing for Azure SQL, assuming the least costly DTU-based pricing plan will be used. 
 


	
 


	The model compares VDAs deployed using different settings in Citrix Provisioning and MCS. Next, we will explain the main assumptions and overall differences between each deployment type. This Storage Cost Analysis V2.xlsxspreadsheet can be downloaded and used to perform what-if analysis by changing the assumptions.
 


	Main Assumptions



	
		Pricing assumes Windows costs are paid using the Hybrid Rights Benefit
	
	
		Compute Gallery Images will use 80 GB of disk space
	
	
		70% of VDAs will use 3-Year Reserved Instance Pricing while 30% will use Pay as you Go Pricing
	
	
		VDAs will be either D4D_V4 or D4_V4
	
	
		Citrix Provisioning Sites with 2K or fewer targets will use a single Citrix Provisioning site spread across Availability Zones
	
	
		Citrix Provisioning Servers use 3-Year Reserved Instance Pricing with E4ds_V5 Instance
	
	
		Citrix Provisioning servers will be provisioned with a 2 TB store
	
	
		A Citrix Provisioning server is deployed for every 1,667 targets.  This number is rounded up, and then one server is added per site for higher availability.
	



	Deployment Types



	The following sections discuss the differences between provisioning options.
 


	MCS without Ephemeral Disk or MCSIO



	No disk optimization solution is used in this MCS option, meaning the OS disk must be at least a Standard SSD, as defined in the model. MCS also provides an identity disk for each VDA that is always present and an E1 disk is used because it is the least expensive option, as standard disks have a minimum size of 32 GB versus 4 GB for SSD.
 


	We assume that Compute Gallery disks are used for disk hydration at a 40-1 ratio and that the VDAs will be used for an average of 10 hours per day and shut down by Autoscale at other times.
 


	The Compute instance defined here is the D4s_v4, which is the same price as the D4_v4 but allows SSD disks to be attached. From a performance perspective, the disk performance here will not be as good as that with ephemeral disks or MCSIO, but the cost is a little less than that of using ephemeral disks and about the same as MCSIO.
 


	MCS with Ephemeral Disk



	In this MCS option, an ephemeral disk is used for the OS Disk to increase performance and lower the storage costs. Ephemeral disks are on-host SSDs that provide very high IOPS for VDA storage. However, the instances that support ephemeral disks must provide temporary storage big enough to store the VDA's full image. Generally, these instance types are more expensive than instances that don’t have this requirement.  In our example, the instance that can support ephemeral disks costs $9 a month more than the same configuration without this support.
 


	As mentioned earlier, we also have the identity (E1) and Compute Gallery disks. We are likewise assuming here that the VDAs will be used for an average of 10 hours per day and shut down by Autoscale at other times.
 


	The Compute instance defined here is the D4d_v4, which is the same price as the D4_v4, but it has a 150GB temp disk and supports ephemeral disks.
 


	MCS with MCSIO



	In this option, we use MCSIO to provide excellent disk performance using less expensive storage in conjunction with a RAM cache.  Remember that this option will use 1-2 GB of RAM to obtain the higher level of performance provided.  Since the RAM cache works so well, standard HDD storage can be used for the OS and cache disks.  As with all the MCS options, this option uses an identity disk and the Compute Gallery for disk hydration.
 


	Citrix Provisioning Provisioned by Citrix Provisioning



	The cost makeup of Citrix Provisioning has a few more items to include in the analysis.  When deployed using the Citrix Provisioning process (meaning not using MCS as in the next section), there are:
 


	
		OS/Boot Disk (E1) – A 1 GB managed disk used to boot the VDA.  This disk is always provisioned whether the VDA is running or not.
	
	
		Cache Disk (E4)—A 32 GB managed disk used as a disk cache for the RAM cache with overflow to disk. This disk is always provisioned whether the VDA is running or not. Currently, the RAM cache uses Standard SDD, which is not configurable like it is in MCS.
	
	
		Citrix Provisioning Servers – See main assumptions
	
	
		Azure SQL
	



	Citrix Provisioning, like MCSIO, uses cache in RAM with overflow to disk technology to increase the solution's overall performance.  In this model, the Citrix Provisioning servers are amortized over the cost of the VDAs.  We are using a 1667:1 ratio for Citrix Provisioning targets to servers with HA built into each Citrix Provisioning site.  If you have a very low VDA count environment, the cost ratio for the Citrix Provisioning server infrastructure will be higher.
 


	Citrix Provisioning Provisioned by Studio



	The makeup of costs when using MCS to deploy Citrix Provisioning is slightly different:
 


	
		Identity Disk (E1)—This disk stores the machine's identity and Domain Trust information. It is always provisioned, whether the VDA is running or not.
	
	
		OS/Boot Disk (E1) – A 1 GB managed disk used to boot the VDA.  This disk is always provisioned whether the VDA is running or not.
	
	
		Cache Disk (S4)—A 32 GB managed disk used as a disk cache for the RAM cache with overflow to the disk. This disk is always provisioned, whether the VDA is running or not.
	
	
		Citrix Provisioning Servers – See main assumptions
	
	
		Azure SQL
	



	You can see here that there is a new disk to capture the machine identity, and MCS allows us to create a cache disk that is Standard HDD rather than Standard SSD.  These changes allow for a slightly lower overall cost.
 


	Cost Comparison Summary



	The main takeaway from this analysis is that the overall costs of the different available Citrix provisioning solutions are relatively similar. 
 


	
 


	Excluding the solution that uses ephemeral disks, the cost estimates range from $56.72 to $59 a month per VDA.  Compute makes up more than 93% of the cost, while the non-compute portion is relatively low in all cases, ranging from $2.12 to $4.40 per VDA per month.
 


	The least costly option at scale is Citrix Provisioning, provisioned by Studio. However, all the options are similar at scale except for using ephemeral disks, which are less expensive for disks but cost $9 a month more for computing.
 


	This does not mean you should not consider MCS with ephemeral disks, as it is a very high-performance solution that should still be considered if performance is critical.
 


	Design Pillars



	Citrix reference architectures are designed with respect to the following critical tenets or “Pillars”:
 


	
		Resiliency
	
	
		Security
	
	
		Cost optimization
	
	
		Operational Excellence
	
	
		Performance Efficiency
	



	The options chosen to support the pillars within the design are discussed in the next several sections.
 


	Resiliency



	Resiliency is the cornerstone of any architecture that supports mission-critical operations.  Each infrastructure component is designed to support local and regional failure in this design. Incorporating multiple Availability Zones per region means that both VDAs and Citrix Provisioning servers are spread across physical data centers within the region. If desired, this configuration can be replicated to another Azure region.  Even within each Availability Zone, multiple Citrix Provisioning servers are deployed, providing N+1 availability.  Citrix Provisioning supports failing overall streaming targets automatically, from one server to another, if one fails without downtime to targets/VDAs.  The SQL database provided using Azure SQL is highly redundant, and Citrix Provisioning is configured to provide “offline database support” if the database becomes unavailable.  This feature allows vDisk streaming to continue if the SQL database is unavailable by caching the database locally, but management is not possible when running offline.
 


	Citrix Provisioning requires storage for vDisks that must be accessed by the Citrix Provisioning servers. In this design, we recommend local storage for Citrix Provisioning because it becomes very highly available. Every server keeps a copy of all the vDisks, and there are then no shared components required to stream the vDisk. This requires more management as the vDisks must be replicated between servers, but the redundancy is worth the extra management.
 


	Security



	Using Citrix Provisioning in Azure allows organizations to use the more secure desktop computing model where non-persistent images are used for end-user workloads.  The advantage of this model is that the deployments are centralized and highly managed, and upon reboot, the workload desktops or servers are reset back to the known good configuration. Azure-specific security practices can be implemented at the subnet level to ensure that the desktop can only communicate from the “Citrix Subnets” up to the landing zone and not traverse vertically amongst the other workload desktops/servers.
 


	Cost optimization



	When migrating to Azure, designing the Citrix infrastructure with cost in mind is important.  For example, it’s typically much less expensive to deploy hosted shared desktops on Windows Servers than non-persistent VDI desktops or persistent VDI desktops, which tend to be the most costly choice.  In the financial modeling within the cost comparison section, we showed some decisions that significantly affect the overall cost of the solution.  These include:
 


	
		Using 3-year reserved instances for Citrix Provisioning infrastructure servers that will run 24/7
	
	
		Using 3-year reserved instances for a percentage of targets that will always be used, then using pay-as-you-go instances with Citrix Autoscale to manage the variable usage that can change from day to day or month to month.  In our model, this was a 70/30 split, but since reserved instances are, say, 60% cheaper than pay-as-you-go, the breakeven point in hours per day is approximately 9.5 hours of usage a day.
	
	
		Using Citrix Provisioning is less costly from a storage perspective than other designs because there is no large OS disk required while the target is running.  A smaller cache disk is used to provide higher performance at a lower cost.
	
	
		See the next section as operational excellence has an associated effect on cost
	



	Operational Excellence



	Organizations dedicated to using Citrix Provisioning say that the most significant benefit of using the technology is the speed and flexibility of changing the environment.  Citrix Provisioning allows for updates to an existing image or deployment of a new image very rapidly.  Targets must be rebooted to apply changes with the new vDisk or vDisk version.  On a similar note, adding targets to an environment is also a very quick and easy task, as in Citrix Provisioning, a target is just a deployment VM with an associated boot disk.  Also, Citrix Provisioning has been used for many years, and there is a lot of knowledge capital in the industry on managing it.
 


	Performance Efficiency



	Performance efficiency is the ability of a solution to adapt to changes in demand. Citrix Provisioning in Azure, along with Citrix AutoScale, provides a scalable solution that is difficult to match with an on-premises deployment, where capacity increases normally require capital purchases and very long lead times.
 


	Conclusion



	Citrix supports several different provisioning methods when using Azure workloads, including Citrix Provisioning (Citrix Provisioning), Machine Creation Services (MCS), and what we would call “manual” provisioning, which means using a provisioning method outside of Citrix where the VDA gets installed using a separate manual or automated workflow.  These are all valid ways to manage Citrix environments.  For those customers who love and see the many advantages of Citrix Provisioning, this reference architecture provides a primer on how to architect a Citrix Provisioning solution in Azure based on the five critical Design Pillars, and it intends to help customers deploy a secure, robust, available, performant environment to delivery Citrix workloads in Azure.]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_06/image.png.37862635408a90d150b17171a9bcc5c7.png" length="215774" type="image/png"/><pubDate>Mon, 17 Jun 2024 13:08:00 +0000</pubDate></item><item><title>Improving ICA File Security</title><link>https://community.stage.citrix.com/tech-zone/build/tech-papers/secure-ica-file/</link><description>Overview



	Citrix Independent Computing Architecture (ICA) information is a critical part of your Citrix deployment. It allows your internal and external users to securely connect to applications and desktops. This information is commonly found in a .ICA file, which the Citrix Workspace app or browser uses to complete the connection.
 


	Because the information in the ICA file provides access to your systems, it is important to implement security best practices. This paper will cover the various launch methods available for ICA files, information recommendations, and leading practices for hardening this critical part of your infrastructure. 
 


	 
 


	What is an ICA file?



	When a user launches an application or desktop, StoreFront generates ICA information, which contains instructions to the client on how to connect to the VDA. This is a text document with the same format as an INI file. This document refers to this as an ICA &#x2018;file&#x2019; regardless of whether it is a file stored on disk or data held in memory.
 


	StoreFront generates this ICA file based on the information it receives from Citrix Virtual Apps and Desktop or Citrix Desktop as a Service (DaaS), along with its own configuration and logic.
 


	
 


	If you open an ICA file, you&#x2019;ll see name-value pairs, including the following:
 


	
		
			
				Field
			
			
				Description
			
		
		
			
				Address
			
			
				
					For internal access, the IP address of the VDA to connect to. When a resource is accessed via a Gateway, this contains the Secure Ticket Authority (STA) ticket.
				 
			
		
		
			
				SSLProxyHost
			
			
				If the resource is accessed via a Gateway, this contains the address of the Gateway.
			
		
		
			
				LaunchReference
			
			
				This contains the launch reference that is used to retrieve ancillary session data. This is a one-time ticket with a limited lifespan.
			
		
		
			
				LogonTicket
			
			
				This contains the logon ticket used to validate that the VDA is launching the connection that the DDC prepared. This is a one-time ticket with a limited lifespan.
			
		
	



	Note that the ICA file includes a field called ClearPassword. Despite its name, this is a legacy field that does not contain a password. Current versions of the Citrix Workspace app do not use this field.
 


	Client access and launch methods



	There are three ways for a user to view their store and connect to their resources:
 


	
		Users can open the store in the Citrix Workspace app. This provides the best and most secure experience. It avoids using unmanaged web browsers, securely handles ICA files, and allows the use of anti-keylogging protection during authentication.
	
	
		Users can open their store and connect to resources within a web browser. This provides an option for situations where the Citrix Workspace app cannot be installed. ICA information is stored in memory within the browser.
	
	
		Users can open the store in their web browsers but launch resources in their locally installed Citrix Workspace app. This is known as a &#x201C;hybrid launch&#x201D;. This requires handing ICA information between the browser and the locally installed app. There are a number of mechanisms in this work.
	



	For more information on how your users can access their stores and launch resources, see StoreFront documentation.
 


	 
 


	Hybrid Launch mechanism details



	This section describes how hybrid launches can transfer data between the browser and locally installed Citrix app.
 


	Hybrid Launch using Citrix Workspace web extensions



	Users can install Citrix Workspace web extensions on Chrome, Edge, and Safari. If the website detects this, it uses it to communicate securely with the locally installed Citrix Workspace app to launch applications. The user does not have the option to download ICA files to disk.
 


	
 


	Hybrid Launch using Citrix Workspace launcher



	Citrix Workspace app for Windows, Mac, and Linux includes a component called the Workspace app launcher. When the user launches a resource, the browser signals to the locally installed Citrix Workspace app to retrieve the ICA file from the StoreFront server.
 


	
 


	Hybrid Launch using ICA download



	If the browser cannot detect a locally installed Citrix Workspace via either Web Extensions or the Workspace launcher, then when a user launches a resource, it downloads an ICA file. The user must then open this file Citrix Workspace app.
 


	
 


	Launch via Gateway



	Whichever launch method is used, StoreFront contacts a Secure Ticket Authority to get a ticket when you launch a resource via a Gateway. If using Citrix Virtual Apps and Desktops, this is normally the Delivery Controller. If using DaaS, this is normally the Cloud Connector, which proxies requests to the cloud ticketing authority. This ticket entitles the caller to connect to the resource for a limited period of time and is only valid for a single use.
 


	When the HDX client sees an SSLProxyHost in the ICA file, it contacts the Gateway, providing the STA ticket. The Gateway, in turn, contacts the STA server to check whether the ticket is still valid and to get the address of the VDA to which it should forward the traffic. The Gateway acts as a reverse proxy between the HDX client and the VDA.
 


	Recommendations



	It is important that ICA files are managed securely as they provide direct access to connect and log into a VDA.
 


	Deploy the latest Citrix Workspace app



	A locally installed Citrix Workspace app is the best and most secure experience for launching VDAs, as the launch process is entirely encapsulated within the app. Therefore, we recommend deploying the Citrix Workspace app and configuring it to connect to the store wherever possible. Ensure you keep it updated so you always have the latest improvements.
 


	Enable in-memory hybrid launches



	You must enable the setting &#x2018;Allow users to download HDX engine (plug-in)&#x2019; to allow hybrid launches using Citrix Workspace launcher and Citrix Web Extensions. If this setting is disabled, users will all download ICA files. You must enable this setting even if you use other mechanisms to deploy the Citrix Workspace app and have no need to provide download links in this way.
 


	
 


	On advanced settings, ensure that &#x201C;Enable protocol handler&#x201D; is not unticked (it is ticked by default). This is required for both protocol handler and web extension launch methods.
 


	
 


	Deploy Citrix Workspace Web Extensions



	Where native launch is not possible, and you need to allow hybrid launches, we recommend deploying Citrix Workspace web extensions to your users. This provides the most reliable and secure hand-off between the browser and the locally installed Citrix Workspace app.
 


	 
 


	Encrypt communications using TLS



	Ensure that ICA files are encrypted over the network by enabling HTTPS on your NetScaler Gateway, load balancer and StoreFront server.
 


	Disable ICA file downloads



	In the case of a hybrid launch, where neither Citrix Workspace Web Extensions nor Citrix Workspace launcher are used, the browser downloads an ICA file to disk. The file is not immediately launched and instead relies on the user opening the file. This allows a user to email the file to someone else or malicious software could access it and send it directly to the attacker. Therefore, we recommend disabling ICA file downloads for most deployments and only use launch methods where the ICA file is transferred in-memory and immediately launched.
 


	StoreFront 2402



	StoreFront has new settings to control whether ICA downloads are allowed.
 


	The first applies only to platforms where Citrix Workspace launcher is supported (Windows, macOS, Linux). Normally, when users first open their store in a browser, it prompts the user to open the Citrix Workspace launcher. Once they&#x2019;ve done this, the Citrix Workspace launcher is used for subsequent app launches. However, the users can press instead &#x2018;Already installed&#x2019; which falls back to download ica downloads. You can remove this option by clearing &#x201C;Show &#x2018;Already installed&#x2019; link on the client detection page. 
 


	
 


	 
 


	Citrix Workspace app for iOS and Android does not support Workspace launcher, so hybrid launches always download ICA files. You can disable these downloads by ticking &#x201C;Prevent ICA downloads on all platforms.&#x201D; In this case, users should add their store directly to the Citrix Workspace app for iOS and Android to use native launches.
 


	
 


	For more information, see StoreFront documentation
 


	Earlier versions of StoreFront



	You can achieve the same results in earlier versions of StoreFront by applying the customization described in CTX584240.
 


	Citrix Workspace



	Citrix Workspace has a similar setting to hide the &#x201C;Already installed&#x201D; link. This is called &#x201C;disallowIcaDownload&#x201D;. This removes the &#x201C;Already installed&#x201D; link on Windows and Mac and does not impact other operating systems. We recommend opening the store in a locally installed Citrix Workspace app for other operating systems. 
 


	You can configure Citrix Workspace to enforce protocol detection using the Workspace powershell module. This does not force users who have previously clicked &#x201C;Already installed&#x201D; to use the Citrix Workspace launcher; therefore, we recommend prompting your users to clear their cookies after enabling this setting.
 


	Reduce the lifetime of launch reference and logon tickets



	Reducing session validity and launch times decreases an attacker's opportunity window if the system has been compromised.
 


	This launch reference and logon tickets are valid for 200s by default, but you can use the PowerShell Set-BrokerServiceConfigurationData  command to configure them with the MaxSessionEstablishmentTimeSecs setting. For example:
 


	
		Set-BrokerServiceConfigurationData Core.MaxSessionEstablishmentTimeSecs -SettingValue 100
	 



	Ensure you carefully test your configuration in the worst-case performance scenarios&#x2014;if the ticket life is too low, it will expire before connecting to the VDA, and the launch will fail.
 


	Prevent launching of unauthorized ICA files



	Another concern is that a threat actor could provide a user with a malicious ICA file, for example, as part of a phishing email. This ICA file could, for instance, connect to a VDA-enabled local drive mapping and pull data off the drives.
 


	 
 


	Prevent launching of ICA files from disk



	As long as you&#x2019;ve enabled in-memory ICA launches for your own systems, we recommend that you configure your Windows clients to refuse to launch ICA files from disk. This prevents users from launching ICA files they have been sent via email or other messaging systems. See Citrix Workspace app for Windows documentation.
 


	 
 


	ICA Signing



	ICA signing is a more comprehensive way to block users from opening ICA files from unauthorized sources in Windows. This involves configuring StoreFront to add a signature to the ICA file and configuring the Citrix Workspace app to only allow launches where that specific signature is present. You can configure Workspace apps to either block unsigned launches completely or to prompt the user to allow unsigned launches. This can apply to all unauthorized launches, whether from disk or in-memory. This feature is not available in Citrix Workspace.
 


	For more details, see StoreFront documentation and Citrix Workspace app for Windows documentation. 
 


	Summary



	As you can see, there are multiple ways to improve ICA security. Some of these may not apply to your organization, but you should use a layered and defense-in-depth approach to apply as many as you can. Start testing these recommendations today to harden your environment!
 


	Resources



	Product Documentation: StoreFront 2402 Long Term Service Release Overview
 


	Product Documentation: Secure your StoreFront deployment
 


	Product Documentation: Citrix Workspace
 


	Tech Paper: Security best practices for Citrix Virtual Apps and Desktops
 


	Tech Paper: Citrix VDA Operating System Hardening Guide</description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_06/image.png.ee3abef3e2be679e6485e603d06eda7c.png" length="130635" type="image/png"/><pubDate>Thu, 06 Jun 2024 15:23:00 +0000</pubDate></item><item><title>PoC Guide: Secure Access to Internal Web Applications and Citrix Secure Private Access</title><link>https://community.stage.citrix.com/tech-zone/learn/poc-guides/ztna-private-web-apps/</link><description>Overview



	With remote work, users need access to internal web-based applications. Providing a better experience means avoiding a VPN deployment model, which often results in the following challenges:
 


	
		VPN Risk 1: Are difficult to install and configure
	
	
		VPN Risk 2: Require users to install VPN software on endpoint devices, which might utilize an unsupported operating system
	
	
		VPN Risk 3: Require the configuration of complex policies to prevent an untrusted endpoint device from having unrestricted access to the corporate network, resources, and data
	
	
		VPN Risk 4: Difficult to keep security policies synchronized between VPN infrastructure and on-premises infrastructure
	



	To improve the overall user experience, organizations must be able to unify all sanctioned apps and simplify user login operations while still enforcing authentication standards.
 


	
 


	Organizations must deliver and secure SaaS, web, Windows, Linux applications, and desktops even though some of these resources exist beyond the confines of the data center and can access resources outside of the data center. Citrix Secure Private Access service provides organizations with secure, VPN-less access to user-authorized resources through Citrix Workspace.
 


	In this proof of concept scenario, a user authenticates to Citrix Workspace using Active Directory, Azure Active Directory, Okta, Google, Citrix Gateway, or a SAML 2.0 provider of their choice as the primary user directory. Citrix Workspace provides single sign-on services for a defined set of enterprise web applications.
 


	
 


	 
 


	If the Citrix Secure Private Access service is assigned to the Citrix subscription, enhanced security policies, ranging from applying screen-based watermarks, restricting printing/downloading actions, screen grabbing restrictions, and keyboard obfuscation are applied on top of the web applications.
 


	The animation shows a user accessing an internal Sharepoint web application with Citrix-provided SSO and secured with Citrix Secure Private Access service.
 


	 
	 
		This demonstration shows a flow where the user launches the application from Citrix Workspace, which uses the VPN-less connection to the data center. Because the user accesses an internal web application from an external device, the access request must come from within Citrix Workspace.
	 



	
 


	This proof of concept guide demonstrates how to:
 


	
		Setup Citrix Workspace
	
	
		Integrate a primary user directory
	
	
		Incorporate Single sign-on for a Sharepoint web application, which is located within the data center
	
	
		Validate the configuration
	



	Setup Citrix Workspace



	The initial steps for setting up the environment is to get Citrix Workspace prepared for the organization, which includes
 


	
		Once you establish Citrix Secure Private Access service entitlement with your Citrix account team, you will find the Citrix Secure Private Access icon under My Services. For more information, see.
	
	
		Set up the Workspace URL
	



	Set Workspace URL



	
		Connect to Citrix cloud and log in as your administrator account
	
	
		Within Citrix Workspace, access Workspace Configuration from the upper-left menu
	
	
		From the Access tab, enter a unique URL for the organization and select Enabled
	



	
 


	Integrate a Primary User Directory



	Before users can authenticate to Workspace, a primary user directory must be configured. The primary user directory is the only identity the user requires as all requests for apps within Workspace utilize single sign-on to secondary identities.
 


	 
 


	An organization can use any one of the following primary user directories:
 


	
		Active Directory: To enable Active Directory authentication, a cloud connector must be deployed within the same data center as an Active Directory domain controller by following the Cloud Connector Installation guide.
	
	
		Active Directory with Time-Based One Time Password: Active Directory-based authentication can also include multifactor authentication with a Time-based One Time Password (TOTP). This guide details the required steps to enable this authentication option.
	
	
		Azure Active Directory: Users can authenticate to Citrix Workspace with an Azure Active Directory identity. This guide provides details on configuring this option.
	
	
		Citrix Gateway: Organizations can utilize an on-premises Citrix Gateway to act as an identity provider for Citrix Workspace. This guide provides details on the integration.
	
	
		Okta: Organizations can use Okta as the primary user directory for Citrix Workspace. This guide provides instructions for configuring this option.
	
	
		SAML 2.0: Organizations can use the SAML 2.0 provider of choice with their on-premises Active Directory (AD). This guide provides instructions for configuring this option.
	



	Configure Single Sign-on



	To successfully integrate web apps with Citrix Workspace, the administrator needs to do the following:
 


	 
 


	
		Deploy Connector Appliance
	
	
		Configure Web app
	
	
		Authorize Web App and configure enhanced security
	



	Deploy Connector Appliance



	
		Within Citrix cloud, select Resource Locations from the menu bar
	



	
 


	
		Within the resource location associated with the site containing the web app, select Connector Appliances
	



	
 


	
		In the Add a Connector Appliance dialog, download the image associated with the appropriate hypervisor and leave this browser window open
	
	
		 
	
	
		Once downloaded, import the image into the hypervisor
	



	
 


	
		When the image starts, it will provide the URL to use to access the console
	



	
 


	
		Log into the Connector and change the admin password, and set the network IP address
	
	
		Give the appliance a name and login to the domain for that resource location
	
	
		Select Register and copy the registration code
	



	
 


	
		Return to the Citrix Cloud page and submit the registration code to complete the Connector Appliance setup
	



	
 


	Configure Web app



	
		Within Citrix cloud, select Manage from the Secure Private Access tile
	
	
		Select Applications followed by Add an App
	
	
		In the Choose a template wizard, select Skip
	



	
 


	
		In the App details window, select Inside my corporate network
	
	
		Specify HTTP/HTTPS as the App Type
	
	
		Provide a App name and description for the application
	
	
		(optionally) Select Direct Access to enable access directly via an FQDN resolvable via the public Internet. If selected, you will need to upload the pertinent SSL certificate in .pfx or .pem format. Also, you will need to map a CNAME record for the site FQDN to the Citrix Gateway service
	
	
		Enter the URL for the web application
	
	
		Add additional related domains as necessary for the web application
	
	
		Add a custom App icon if desired
	
	
		Select Next
	



	
 


	
		In the Single Sign-On window, select the appropriate SSO option for the web application. 
		This often requires help from the web app owner. 
		You will have 5 SSO choices to choose from:
	



	
		Basic: If your backend server presents you with a basic-401 challenge, choose Basic SSO
	
	
		Kerberos: If your backend server presents you with negotiate-401 challenge, choose Kerberos SSO
	
	
		Form-Based: If your backend server presents you with an HTML form for authentication, choose Form-based SSO
	
	
		SAML: Choose SAML for SAML-based SSO into web applications. Enter the configuration details for SAML SSO type.
	
	
		No SSO: Use no SSO option when you do not need to authenticate the user on the backend server
	



	
		Select Basic SSO and User Principal Name (UPN) as username format
	
	
		Select Next
	



	
 


	
		In the App Connectivity window, select the Resource Location, where you installed the Connector Appliance earlier, for the URL and Related Domain entries (and verify that your connectors are up)
	
	
		Select Save
	



	
 


	
		Select Finish
	



	Authorize Web App and configure enhanced security



	
		Within Secure Private Access menu, select Access Policies
	
	
		In the Access Policy section, select Create policy
	
	
		Enter the Policy name and a brief Policy description
	
	
		In the Applications drop-down list, search for &#x201C;The Hub (Intranet)&#x201D; and select it
	



	
		NOTE
	 

	
		You can create multiple access rules and configure different access conditions for different users or user groups within a single policy. These rules can be applied separately for HTTP/HTTPS and TCP/UDP applications. For more information on multiple access rules, see Configure an access policy with multiple rules.
	 



	
 


	
		Select Create Rule to create rules for the policy
	
	
		Enter the rule name and a brief description of the rule, and select Next
	



	
 


	
		Add the appropriate users/groups who are authorized to launch the app, and select Next
	



	
		NOTE
	 

	
		Click + to add multiple conditions based on the context.
	 



	
 


	
		Specify if the HTTP/HTTPS app can be accessed with or without restrictions 
		The below screenshot has all restrictions configured. 
		Please note that Anti Key-logging and Anti Screen-Capturing require Citrix Workspace desktop clients.
	
	
		Specify the TCP/UDP apps action 
		The below screenshot denies access to TCP/UDP apps.
	
	
		Select Next
	



	
 


	
		The Summary page displays the policy rule details 
		Verify the details and select Finish
	



	
 


	
		In the Create policy dialog, verify that Enable policy on save is checked, and select Save.
	



	
 


	Validate



	
		Log into Citrix Workspace as a user
	
	
		Select the configured web application
	
	
		The user automatically signs in to the app
	
	
		The appropriate enhanced security policies are applied
	



	
 


	Troubleshooting



	Enhanced Security Policies Failing



	Users might experience the enhanced security policies (watermark, printing, or clipboard access) fail. Typically, this happens because the web application uses multiple domain names. Within the application configuration settings for the web app, there was an entry for Related Domains.
 


	
 


	The enhanced security policies are applied onto those related domains. To identify missing domain names, an administrator can access the web app with a local browser and do the following:
 


	
		Navigate to the section of the app where the policies fail.
	
	
		In Google Chrome and Microsoft Edge (Chromium version), select the three dots in the upper right side of the browser to show a menu screen.
	
	
		Select More Tools.
	
	
		Select Developer Tools
	
	
		Within the developer tools, select Sources. This provides a list of access domain names for that application section. To enable the enhanced security policies for this portion of the app, those domain names must be entered into the related domains field within the app configuration. Related domains are added like the following *.domain.com</description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_06/image.png.1713024f8e8bd4bb809c60e89e9ab7c5.png" length="144702" type="image/png"/><pubDate>Mon, 03 Jun 2024 12:31:06 +0000</pubDate></item><item><title>Simplifying the move to 2402 LTSR with the Automated Configuration Tool</title><link>https://community.stage.citrix.com/tech-zone/build/deployment-guides/act-ltsr/</link><description><![CDATA[Overview



	On April 15th, 2024, Citrix officially released the Citrix Virtual Apps and Desktops (CVAD) 2402 Long Term Service Release (LTSR). This release should excite Citrix LTSR customers as it will unlock cutting-edge features and enhancements previously exclusive to the current release track. 
 


	The excitement of a new LTSR release also brings sad news — an older LTSR will soon be put out to pasture. Citrix CVAD 1912 LTSR was officially released in December 2019 and has served its duties well. On 18th December this year, CVAD 1912 LTSR will go end of life, and loyal LTSR customers will need to look to the future and plan for the migration to 2402 LTSR.
 


	Upgrading



	Upgrading to a new LTSR requires careful planning and consideration and thoroughly evaluating the upgrade paths available. Luckily, we have several resources available to assist you in successfully upgrading, including a  Citrix Master Class, which outlines the options available when upgrading to CVAD 2402 LTSR. 
 


	To summarize, when upgrading from 1912 LTSR to 2402 LTSR, there are two primary upgrade paths that a Citrix administrator can take depending on their organizational needs and infrastructural complexities. Firstly, customers can opt for an in-place upgrade, which involves updating existing components to the latest version without requiring a complete reinstallation. This option minimizes disruption and downtime, making it ideal for environments where continuous availability is paramount. Alternatively, customers may choose a parallel deployment approach, wherein a new environment running CVAD 2402 LTSR is built alongside the existing one, allowing for meticulous testing and validation before migration. This method offers greater control and flexibility, enabling organizations to fine-tune configurations and address potential compatibility issues before transitioning fully. It is the preferred option, as recommended by Citrix Consulting. 
 


	This article highlights and demonstrates how the Citrix Automated Configuration tool (ACT) can simplify migrating your CVAD site configuration when completing a parallel upgrade from CVAD 1912 LTSR to CVAD 2402 LTSR.  
 


	For years, manually migrating your CVAD site configuration to a new site has scared even the bravest of Citrix administrators. However, instead of wrestling with a tangled web of configurations and settings, you can unleash the power of automation with the Automated Configuration Tool.
 


	 
 


	What is the Automated Configuration tool?



	The Automated Configuration tool helps automate the migration of CVAD configuration (policies, applications, catalogs, admin roles, scopes, and others) from one on-premises site to another or from an on-premises site to Citrix DaaS hosted on Citrix Cloud. It can also migrate information between different Cloud regions or tenants. The tool uses PowerShell cmdlets and the existing Citrix PowerShell SDK to perform the migration activities. 
 


	
		The Automated Configuration tool must be installed on a domain-joined machine running .NET 4.7.2 or higher. The machine must also have the Citrix PowerShell SDK, which is automatically installed on the Citrix Delivery Controller (DDC).
	 



	Please refer to the following article for more information on the Automated Configuration Tool Tech Brief: Citrix Automated Configuration Tool.
 


	 
	Essential Considerations for the Different Citrix Provisioning Types



	Before beginning the process of migrating your Citrix Machine Catalogs, it is essential to understand the different considerations to be taken into account, depending on the provisioning method you use:
 


	MCS: Pooled VDI multi-session (Random) and RDS Machine Catalogs



	
		The ACT will import the configuration and the golden image. However, the virtual machines in these catalogs have yet to be migrated.
	
	
		The MCS catalog import process can take a couple of hours, depending on the size of the master image. Therefore, the import/merge command within the tool only starts the MCS catalog creation and does not wait for it to finish.
	
	
		After completing the import, you can monitor the catalog creation progress via Studio in the 2402 LTSR site.
	
	
		After creating the master image, you can provision machines. 
		Consider your hypervisor's existing capacity since you have consumption from your 1912 LTSR site usage.
	
	
		All other objects (including the Delivery Group, applications, policies, and everything that uses the catalog) can be imported without waiting for the master image to be created. You can use the same commands in the tool to migrate catalogs and all other objects.
	
	
		After creating the catalog, machines can be added to the imported catalog, and users can launch their resources.
	



	MCS: Static Assigned Machines



	
		The ACT will import the configuration, master image, and the machines as well.
	
	
		The VDAs will need to be pointed at the DDCs that are part of the 2402 LTSR site so that they can register correctly.
	



	PVS



	
		No additional steps are required to import the Provisioning Services (PVS) Machine Catalogs and their corresponding applications. 
		 
	



	Migration Activities



	The following is a high-level overview of the steps that you will need to complete:
 


	
		Install ACT on the 1912 LTSR DDC.
	
	
		Export the configuration.
	
	
		Install ACT on the 2402 LTSR DDC.
	
	
		Copy the configuration files from the 1912 LTSR DDC to the 2402 LTSR DDC.
	
	
		Modify the required YAML files.
	
	
		Import the configuration, component by component.
	
	
		Validate that the resources were created successfully. 
		 
	



	Install ACT on the 1912 LTSR DDC



	
		Connect to a DDC that is part of your CVAD 1912 LTSR site.
	
	
		You can download the latest version of the Citrix Automated Configuration tool here. The newest version at the time of writing is 3.0.100. 
	
	
		Install AutoConfig_PowerShell_x64.msi. 
	
	
		Confirm that a Direct Access icon named ‘Auto Config’ has been created for the Automated Configuration Tool on the desktop. 
		 
	



	Export the Configuration from the 1912 LTSR Site



	
		Connect to the DDC where you have just installed the Citrix Automated Configuration tool.
	
	
		Open the Auto Config icon from the desktop.
	
	
		Run Export-CvadAcToFile.
	



	
 


	(Note: The Export-CvadAcToFile exports the on-premises files to YAML files. Exported files are placed in the directory %HOMEPATH%\Documents\Citrix\AutoConfig in a uniquely named Export subfolder. The folder %HOMEPATH%\Documents\Citrix\AutoConfig always contains the latest exported on-premises site configuration.)
 


	
		You will have an HTML file called ‘Automated Configuration Tool Log.’’ 
		The file will provide details of the completed export, including a summary of the components exported to YAML files. You must take note of the ‘Fixups’ section. The Fixups section of the log file shows missing dependencies if an import or merge fails.
	



	
 


	
		Verify the YAML files are now located in the folder %HOMEPATH%\Documents\Citrix\AutoConfig.
	



	
 


	
		Note: 
	 

	
		All exported files are placed in two folder locations, providing ease of use and a history of exports. Exports are always put in the root folder. Copies are placed in a subfolder named 'Export' with the date and time of the export. The root folder always contains the most current exported on-premises site configuration. Each 'Export' subfolder includes the export done on the indicated date and time, which maintains a history of exports.
	 



	 
 


	Install ACT on the 2402 LTSR DDC



	
		Connect to a DDC that is part of your CVAD 2402 LTSR site.
	
	
		You can download the latest version of the Citrix Automated Configuration tool here. The newest version at the time of writing is 3.0.100. 
	
	
		Install AutoConfig_PowerShell_x64.msi. 
	
	
		Confirm that a Direct Access icon named Auto Config has been created for the Automated Configuration Tool on the desktop.
	



	 
 


	Copy the YAML Files from the 1912 LTSR DDC to the 2402 LTSR DDC



	
		Connect to the DDC in your 2402 LTSR site, where we installed the Citrix Automated Configuration Tool.
	
	
		On the desktop, select the Auto Config icon. It will open the ACT command line window and create the Citrix Folder under %HOMEPATH%DocumentsCitrixAutoConfig
	
	
		Copy the folder %HOMEPATH%DocumentsCitrixAutoConfig on the DDC that is part of the 1912 LTSR site and paste it to %HOMEPATH%DocumentsCitrixAutoConfig on the DDC that is part of the 2402 LTSR site.
	



	 
 


	Edit the Required YAML Files



	Before importing the configuration to the new 2402 LTSR site, update the CvadAcSecurity.yml and the CustomerInfo.yml files with the required information. The export process creates the CvadAcSecurity.yml, which contains placeholders for each security item needed for the specific hypervisor type. If the site includes multiple hosting connections, update the CvadAcSecurity.yml with the connection information for each host type. If the site contains only a single host connection, update the CvadAcSecurity.yml file with the connection information for the host connection. 
 


	
		Locate the CvadAcSecurity.yml file in the following location: %HOMEPATH%\Documents\Citrix\AutoConfig.
	
	
		Edit the CvadAcSecurity.yml file with Notepad.
	
	
		Enter the correct Hypervisor password and save the file. 
	



	
 


	
		Locate the CustomerInfo.yml file in the following location: %HOMEPATH%\Documents\Citrix\AutoConfig.
	
	
		Edit the CustomerInfo.yml file with Notepad.
	
	
		Update the Environment parameter from Production to OnPrem and save the file.
	



	
 


	 
 


	Import the Configuration on the 2402 LTSR DDC



	We are ready to import the configuration to the new 2402 LTSR site. 
	You can decide to import the entire configuration in one go by running Import-CvadAcToSite or import the configuration granularly, component by component.
 


	In this article, we will import the configuration component by component.
 


	Please refer to the following article to understand how to import the configuration using the Import-CvadAcToSite.
 


	When granularly importing the configuration component by component, it is essential to migrate the components in the following order:
 


	
		Tags
	
	
		AdminRoles
	
	
		AdminScopes
	
	
		HostConnections
	
	
		MachineCatalogs
	
	
		StoreFronts
	
	
		DeliveryGroups
	
	
		ApplicationGroups
	
	
		ApplicationFolders
	
	
		Applications
	
	
		GroupPolicies
	
	
		UserZonePreference
	



	We will use the Merge-CvadAcToSite command to import the configuration. This command merges the 1912 LTSR site files to the 2402 LTSR site, preserving any changes already made on the 2402 LTSR site. Please refer to the following article for more information on the Automated configuration tool cmdlets.
 


	
		Connect to the DDC that is part of your 2402 LTSR site.
	
	
		Open the Auto Config from the icon on the desktop.
	
	
		Run Merge-CvadAcToSite -Tags and enter Yes when prompted.
	



	
 


	
		Confirm that it completes successfully.
	



	
 


	
		Run Merge-CvadAcToSite -AdminRoles and enter Yes when prompted.
	



	
 


	
		Confirm that it completes successfully.
	



	
 


	
		Run Merge-CvadAcToSite -HostConnections and enter Yes when prompted.
	



	
 


	
		Confirm that it completes successfully.
	



	
 


	
		Run Merge-CvadAcToSite -MachineCatalogs and enter Yes when prompted.
	



	
 


	
		Confirm that it completes successfully.
	



	
 


	
		Run Merge-CvadAcToSite -DeliveryGroups and enter Yes when prompted.
	



	
 


	
		Confirm that it completes successfully.
	



	
 


	
		Run Merge-CvadAcToSite -ApplicationGroups and enter Yes when prompted.
	



	
 


	
		Confirm that it completes successfully.
	



	
 


	
		Run Merge-CvadAcToSite -Application and enter Yes when prompted.
	



	
 


	
		Confirm that it completes successfully.
	



	
 


	
		Run Merge-CvadAcToSite -GroupPolicies and enter Yes when prompted.
	



	
 


	
		Confirm that it completes successfully.
	



	
 


	 
 


	Review Configuration Created in the 2402 LTSR Site



	We will now validate that the configuration and resources have been created as expected in our Citrix CVAD 2402 LTSR site and compare it against the configuration and resources from our Citrix CVAD 1912 LTSR site.
 


	
		Connect to the DDC that is part of your Citrix 2402 LTSR site.
	
	
		Connect to Citrix Web Studio.
	
	
		Select Machine Catalogs and verify the successful import of your Machine Catalogs. 
		Below, we can see that the Machine Catalog has been created, but VDAs still need to be migrated - it is expected as it is an MCS-created Machine Catalog with a user data set to discard. The golden image and the configuration in Catalogs with User data: Discard can be migrated. 
	
	
		However, the virtual machines in these catalogs have yet to be migrated since the 1912 LTSR site you are importing from is responsible for maintaining the life cycle of the virtual machine. 
	
	
		For more information on the considerations for the different provisioning types, refer to the Important Considerations for the Different Provisioning Types section of this article.
	



	
 


	 
 


	Below is the 1912 LTSR site configuration for comparison:
 


	
 


	 
	 
 


	
		Select Delivery Groups and verify that your Delivery Groups have been imported.
	



	
 


	The Delivery Group has been created, but it is important to note that it is in maintenance mode. The Delivery Groups must be removed from maintenance mode before you begin migrating users to your new 2402 LTSR site.
 


	Below is the 1912 LTSR site configuration for comparison:
 


	 
	 
 


	
		Select Applications and verify your Applications have been imported.
	



	
 


	Below is the 1912 LTSR site configuration for comparison:
 


	 
	 
 


	
		Select Policies and verify your Policies have been imported.
	



	
 


	Below is the 1912 LTSR site configuration for comparison:
 


	
 


	 
 


	Helpful Information



	If you want to test your Merge/Import command before implementing it, use the CheckMode parameter. The CheckMode parameter performs the Merge/Import operation but makes no changes. We can use this command to test our Import/Merge before it occurs. 
 


	Below is an example of using the CheckMode parameter to test merging the Host Connections. The following command was run: Merge-CvadAcToSite -HostConnections -CheckMode
 


	
 


	Below is the output:
 


	
 


	 
 


	Troubleshooting Tips



	General information for troubleshooting:



	
		Refer to the Automated Configuration Tool Troubleshooting FAQ article.
	
	
		Prior to opening a support ticket with Citrix, collect all log and *.yml files into a single zip by running 
	



	
		New-CvadAcZipInfoForSupport 
	 



	
		No customer security information is included. Forward the zip file at the following location: 
	



	
		%HOMEPATH%\Documents\Citrix\AutoConfig\CvadAcSupport_yyyy_mm_dd_hh_mm_ss.zip
	 



	
		Running any cmdlet creates a log file and an entry in the master history log file. The entries contain the date, operation, result, backup, and log file locations of the execution. This log provides potential solutions and fixes to common errors.
	
	
		The master history log is located in: 
	



	
		%HOMEPATH%\Documents\Citrix\AutoConfig in the file History.Log.* 
	 



	
		All operation log files are placed in a backup folder.
	
	
		All log file names begin with CitrixLog. They then show the auto-config operation and the date and timestamp of the cmdlet's execution.
	
	
		Logs do not auto-delete.
	
	
		You can suppress Console logging by using the -quiet parameter.
	



	 
 


	Conclusion



	Using the Citrix Automated Configuration tool when migrating to 2402 LTSR will simplify the migration process, minimize errors, and ensure a seamless transition. The tool automates intricate tasks, ensuring consistency and accuracy, which enhances system reliability and performance. By leveraging this tool, administrators can save time, reduce downtime, and maintain high operational efficiency throughout the migration.
 


	 
 


	PowerShell snippets used:



	Export-CvadAcToFile:
 


	
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #0200B0;
    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 #0200B0;
    background-color: #0200B0;
    color: #ffffff;
    padding: 10px;
}

.table_component td {
    border: 1px solid #0200B0;
    background-color: #0200B0;
    color: #ffffff;
    padding: 10px;
    font-family: Consolas,Courier New;
	font-size:12px;
}	
	
		
			
				
					
						
							*** Welcome to Citrix Virtual Apps and Desktops Automated Configuration ***
						 

						
							            *************************************************************************** 
							            *** To solve issues:        Show-CvadAcDocument                         *** 
							            ***************************************************************************
						 

						
							PS C:\Users\Niallf\Documents\Citrix\AutoConfig&gt; Export-CvadAcToFile 
							TASK [Auto-Config: Initializing for Export] **************************************** 
							 &gt;&gt;&gt; Ok
						 

						
							TASK [User Zone Preferences: Read On-Prem] ***************************************** 
							 &gt;&gt;&gt; Ok
						 

						
							TASK [User Zone Preferences: Write to file] **************************************** 
							 &gt;&gt;&gt; Ok: 0 components exported
						 

						
							TASK [Site: Read On-Prem] ********************************************************** 
							 &gt;&gt;&gt; Ok
						 

						
							TASK [Site: Write to file] ********************************************************* 
							 &gt;&gt;&gt; Ok: 1 components exported
						 

						
							TASK [Group Policy: Read On-Prem] ************************************************** 
							 &gt;&gt;&gt; Ok
						 

						
							TASK [Group Policy: Write to file] ************************************************* 
							 &gt;&gt;&gt; Ok: 4 components exported
						 

						
							TASK [Application: Read On-Prem] *************************************************** 
							 &gt;&gt;&gt; Ok
						 

						
							TASK [Application: Write to file] ************************************************** 
							 &gt;&gt;&gt; Ok: 7 components exported
						 

						
							TASK [Application Folder: Read On-Prem] ******************************************** 
							 &gt;&gt;&gt; Ok
						 

						
							TASK [Application Folder: Write to file] ******************************************* 
							 &gt;&gt;&gt; Ok: 0 components exported
						 

						
							TASK [Application Group: Read On-Prem] ********************************************* 
							 &gt;&gt;&gt; Ok
						 

						
							TASK [Application Group: Write to file] ******************************************** 
							 &gt;&gt;&gt; Ok: 1 components exported
						 

						
							TASK [Delivery Group: Read On-Prem] ************************************************ 
							.... &gt;&gt;&gt; Ok
						 

						
							TASK [Delivery Group: Write to file] *********************************************** 
							 &gt;&gt;&gt; Ok: 1 components exported
						 

						
							TASK [StoreFront: Read On-Prem] **************************************************** 
							 &gt;&gt;&gt; Ok
						 

						
							TASK [StoreFront: Write to file] *************************************************** 
							 &gt;&gt;&gt; Ok: 0 components exported
						 

						
							TASK [Machine Catalog: Read On-Prem] *********************************************** 
							 &gt;&gt;&gt; Ok
						 

						
							TASK [Machine Catalog: Write to file] ********************************************** 
							 &gt;&gt;&gt; Ok: 1 components exported
						 

						
							TASK [Admin Folders: Read On-Prem] ************************************************* 
							 &gt;&gt;&gt; Ok
						 

						
							TASK [Admin Folders: Write to file] ************************************************ 
							 &gt;&gt;&gt; Ok: 0 components exported
						 

						
							TASK [Icon: Read On-Prem] ********************************************************** 
							 &gt;&gt;&gt; Ok
						 

						
							TASK [Icon: Write to file] ********************************************************* 
							 &gt;&gt;&gt; Ok: 8 components exported
						 

						
							TASK [Host Connection: Read On-Prem] *********************************************** 
							 &gt;&gt;&gt; Ok
						 

						
							TASK [Host Connection: Write to file] ********************************************** 
							 &gt;&gt;&gt; Ok: 1 components exported
						 

						
							TASK [Admin Administrators: Read On-Prem] ****************************************** 
							 &gt;&gt;&gt; Ok
						 

						
							TASK [Admin Administrators: Write to file] ***************************************** 
							 &gt;&gt;&gt; Ok: 1 components exported
						 

						
							TASK [Admin Roles: Read On-Prem] *************************************************** 
							 &gt;&gt;&gt; Ok
						 

						
							TASK [Admin Roles: Write to file] ************************************************** 
							 &gt;&gt;&gt; Ok: 1 components exported
						 

						
							TASK [Admin Scopes: Read On-Prem] ************************************************** 
							 &gt;&gt;&gt; Ok
						 

						
							TASK [Admin Scopes: Write to file] ************************************************* 
							 &gt;&gt;&gt; Ok: 0 components exported
						 

						
							TASK [Tag: Read On-Prem] *********************************************************** 
							 &gt;&gt;&gt; Ok
						 

						
							TASK [Tag: Write to file] ********************************************************** 
							 &gt;&gt;&gt; Ok: 1 components exported
						 

						
							TASK [Zones: Read On-Prem] ********************************************************* 
							 &gt;&gt;&gt; Ok
						 

						
							TASK [Zones: Write to file] ******************************************************** 
							 &gt;&gt;&gt; Ok: 1 components exported
						 

						
							TASK [LogFile: Location] *********************************************************** 
							C:\Users\Niallf\Documents\Citrix\AutoConfig\Export_2024_05_30_02_12_54\CitrixLog_Export_2024_05_30_02_12_54.txt 
							    Fixups Count:            2
						 

						
							Be sure to update the following files before accessing the cloud  (as needed) : 
							  - CustomerInfo.yml 
							  - ZoneMapping.yml 
							  - CvadAcSecurity.yml
						 

						
							Automated Configuration version: 3.0.100.0
						 

						
							 
							Overall_Success    : True 
							Individual_Success : {[Zones, Successful], [Tags, Successful], [AdminScopes, Successful], [AdminRoles, Successful]...} 
							CustomResult       : 
							FileSpecification  : 
							FileName           : 
							Duration           : 0 
							 
						 
					
				
			
		
	



	 
 


	Merge-CvadAcToSite -Tags:
 


	
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #0200B0;
    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 #0200B0;
    background-color: #0200B0;
    color: #ffffff;
    padding: 10px;
}

.table_component td {
    border: 1px solid #0200B0;
    background-color: #0200B0;
    color: #ffffff;
    padding: 10px;
    font-family: Consolas,Courier New;
	font-size:12px;
}	
	
		
			
				
					
						
							*** Welcome to Citrix Virtual Apps and Desktops Automated Configuration ***
						 

						
							            *************************************************************************** 
							            *** To solve issues:        Show-CvadAcDocument                         *** 
							            ***************************************************************************
						 

						
							PS C:\Users\Niallf\Documents\Citrix\AutoConfig&gt; Merge-CvadAcToSite -Tags 
							TASK [Auto-Config: Customer Id: CitrixOnPremises] ******
						 

						
							The action you have selected will change the cloud site configuration; are you sure you want to proceed?  Type the full word response [Yes] for continue, any other key(s) to stop. 
							Yes 
							TASK [Auto-Config: Initializing for Merge] ************* 
							 &gt;&gt;&gt; Ok
						 

						
							TASK [Tag: Read and process expected components] ******* 
							 &gt;&gt;&gt; Ok
						 

						
							TASK [Tag: Read Cloud components] ********************** 
							. &gt;&gt;&gt; Ok
						 

						
							TASK [Tag: Process Cloud components] ******************* 
							 &gt;&gt;&gt; Ok
						 

						
							TASK [Tag: Backup Tag] ********************************* 
							 &gt;&gt;&gt; Ok
						 

						
							TASK [Tag: NO CHANGE: Number of components not changed: 1 ] ******************************************************* 
							 &gt;&gt;&gt; No Change
						 

						
							TASK [Tag: Verify Components] ************************** 
							 &gt;&gt;&gt; Verified
						 

						
							TASK [LogFile: Location] ******************************* 
							C:\Users\Niallf\Documents\Citrix\AutoConfig\Merge_2024_05_30_02_51_34\CitrixLog_Merge_2024_05_30_02_51_34.txt
						 

						
							Automated Configuration version: 3.0.100.0
						 

						
							 
							Overall_Success    : True 
							Individual_Success : {[Zones, No Action], [Tags, Successful], [AdminScopes, No Action], [AdminRoles, No Action]...} 
							CustomResult       : Citrix.AutoConfig.Common.ComponentCommon.EvaluationResultData 
							FileSpecification  : 
							Filename           : 
							Duration           : 0
						 
					
				
			
		
	



	 
 


	Merge-CvadAcToSite -AdminRoles:
 


	
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #0200B0;
    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 #0200B0;
    background-color: #0200B0;
    color: #ffffff;
    padding: 10px;
}

.table_component td {
    border: 1px solid #0200B0;
    background-color: #0200B0;
    color: #ffffff;
    padding: 10px;
    font-family: Consolas,Courier New;
	font-size:12px;
}	
	
		
			
				
					
						
							            *** Welcome to Citrix Virtual Apps and Desktops Automated Configuration ***
						 

						
							            *************************************************************************** 
							            *** To solve issues:        Show-CvadAcDocument                         *** 
							            ***************************************************************************
						 

						
							PS C:\Users\Niallf\Documents\Citrix\AutoConfig&gt; Merge-CvadAcToSite -AdminRoles 
							TASK [Auto-Config: Customer Id: CitrixOnPremises] ********************************************************************** 
							The action you have selected will change the cloud site configuration; are you sure you want to proceed?  Type the full word response [Yes] for continue, any other key(s) to stop. 
							Yes 
							TASK [Auto-Config: Initializing for Merge] ***************************************************************************** 
							 &gt;&gt;&gt; Ok
						 

						
							TASK [Admin Roles: Read and process expected components] ******************************************************************************************* 
							 &gt;&gt;&gt; Ok
						 

						
							TASK [Admin Roles: Read Cloud components] ************** 
							. &gt;&gt;&gt; Ok
						 

						
							TASK [Admin Roles: Process Cloud components] *********** 
							...... &gt;&gt;&gt; Ok
						 

						
							TASK [Admin Roles: Backup Admin Roles] ***************** 
							 &gt;&gt;&gt; Ok
						 

						
							TASK [Admin Roles (1 of 1): Add Component: Contractor] * 
							. &gt;&gt;&gt; Added
						 

						
							TASK [Admin Roles: Read Cloud components] ************** 
							. &gt;&gt;&gt; Ok
						 

						
							TASK [Admin Roles: Process Cloud components] *********** 
							....... &gt;&gt;&gt; Ok
						 

						
							TASK [Admin Roles: Backup Admin Roles] ***************** 
							 &gt;&gt;&gt; Ok
						 

						
							TASK [Admin Roles: Verify Components] ****************** 
							 &gt;&gt;&gt; Verified
						 

						
							TASK [LogFile: Location] ******************************* 
							C:\Users\Niallf\Documents\Citrix\AutoConfig\Merge_2024_05_30_02_57_58\CitrixLog_Merge_2024_05_30_02_57_58.txt
						 

						
							Automated Configuration version: 3.0.100.0
						 

						
							 
							Overall_Success    : True 
							Individual_Success : {[Zones, No Action], [Tags, No Action], [AdminScopes, No Action], [AdminRoles, Successful]...} 
							CustomResult       : Citrix.AutoConfig.Common.ComponentCommon.EvaluationResultData 
							FileSpecification  : 
							FileName           : 
							Duration           : 0
						 

						
							 
						 
					
				
			
		
	



	 
 


	Merge-CvadAcToSite -HostConnections:
 


	
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #0200B0;
    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 #0200B0;
    background-color: #0200B0;
    color: #ffffff;
    padding: 10px;
}

.table_component td {
    border: 1px solid #0200B0;
    background-color: #0200B0;
    color: #ffffff;
    padding: 10px;
    font-family: Consolas,Courier New;
	font-size:12px;
}	
	
		
			
				
					
						
							 
							            *************************************************************************** 
							            *** To solve issues:        Show-CvadAcDocument                         *** 
							            ***************************************************************************
						 

						
							PS C:\Users\Niallf\Documents\Citrix\AutoConfig&gt; Merge-CvadAcToSite -HostConnections 
							TASK [Auto-Config: Customer Id: CitrixOnPremises] ******************
						 

						
							The action you have selected will change the cloud site configuration; are you sure you want to proceed?  Type the full word response [Yes] for continue, any other key(s) to stop. 
							yes 
							TASK [Auto-Config: Initializing for Merge] ************************* 
							 &gt;&gt;&gt; Ok
						 

						
							TASK [Host Connection: Read and process expected components] ******* 
							.. &gt;&gt;&gt; Ok
						 

						
							TASK [Host Connection: Read Cloud components] ********************** 
							 &gt;&gt;&gt; Ok
						 

						
							TASK [Host Connection: Process Cloud components] ******************* 
							. &gt;&gt;&gt; Ok
						 

						
							TASK [Host Connection: Backup Host Connection] ********************* 
							 &gt;&gt;&gt; Ok
						 

						
							TASK [Host Connection: NO CHANGE: Number of components not changed: 1 ] **************************************************************************** 
							 &gt;&gt;&gt; No Change
						 

						
							TASK [Host Connection: Verify Components] ************************** 
							 &gt;&gt;&gt; Verified
						 

						
							TASK [LogFile: Location] ******************************************* 
							C:\Users\Niallf\Documents\Citrix\AutoConfig\Merge_2024_05_30_02_59_32\CitrixLog_Merge_2024_05_30_02_59_32.txt 
							    Fixups Count:            2
						 

						
							Automated Configuration version: 3.0.100.0
						 

						
							 
							Overall_Success    : True 
							Individual_Success : {[Zones, No Action], [Tags, No Action], [AdminScopes, No Action], [AdminRoles, No Action]...} 
							CustomResult       : Citrix.AutoConfig.Common.ComponentCommon.EvaluationResultData 
							FileSpecification  : 
							FileName           : 
							Duration           : 0 
							 
						 
					
				
			
		
	



	 
 


	Merge-CvadAcToSite -MachineCatalogs:
 


	
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #0200B0;
    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 #0200B0;
    background-color: #0200B0;
    color: #ffffff;
    padding: 10px;
}

.table_component td {
    border: 1px solid #0200B0;
    background-color: #0200B0;
    color: #ffffff;
    padding: 10px;
    font-family: Consolas,Courier New;
	font-size:12px;
}	
	
		
			
				
					
						
							*** Welcome to Citrix Virtual Apps and Desktops Automated Configuration ***
						 

						
							            *************************************************************************** 
							            *** To solve issues:        Show-CvadAcDocument                         *** 
							            ***************************************************************************
						 

						
							PS C:\Users\Niallf\Documents\Citrix\AutoConfig&gt; Merge-CvadAcToSite -MachineCatalogs 
							TASK [Auto-Config: Customer Id: CitrixOnPremises] ****************** 
							The action you have selected will change the cloud site configuration; are you sure you want to proceed?  Type the full word response [Yes] for continue, any other key(s) to stop. 
							yes 
							TASK [Auto-Config: Initializing for Merge] *************************  
							&gt;&gt;&gt; Ok
						 

						
							TASK [Machine Catalog: Read and process expected components] ******* 
							... &gt;&gt;&gt; Ok
						 

						
							TASK [Machine Catalog: Read Cloud components] ********************** 
							.. &gt;&gt;&gt; Ok
						 

						
							TASK [Machine Catalog: Process Cloud components] ******************* 
							 &gt;&gt;&gt; Ok
						 

						
							TASK [Machine Catalog: Backup Machine Catalog] *********************  
							&gt;&gt;&gt; Ok
						 

						
							TASK [Machine Catalog (1 of 1): Add Component: MC-Win2019-MultiSession] ***************************** 
							..................... &gt;&gt;&gt; Added
						 

						
							TASK [Machine Catalog: Read Cloud components] ********************** 
							...... &gt;&gt;&gt; Ok
						 

						
							TASK [Machine Catalog: Process Cloud components] ******************* 
							. &gt;&gt;&gt; Ok
						 

						
							TASK [Machine Catalog: Backup Machine Catalog] *********************  
							&gt;&gt;&gt; Ok
						 

						
							TASK [Machine Catalog: Verify Components] **************************  
							&gt;&gt;&gt; Verified
						 

						
							TASK [LogFile: Location] *******************************************
						 

						
							C:\Users\Niallf\Documents\Citrix\AutoConfig\Merge_2024_05_30_05_55_07\CitrixLog_Merge_2024_05_30_05_55_07.txt 
							    Fixups Count:            2
						 

						
							Automated Configuration version: 3.0.100.0
						 

						
							 
							Overall_Success    : True 
							Individual_Success : {[Zones, No Action], [Tags, No Action], [AdminScopes, No Action], [AdminRoles, No Action]...} 
							CustomResult       : Citrix.AutoConfig.Common.ComponentCommon.EvaluationResultData 
							FileSpecification  : 
							FileName           : 
							Duration           : 0 
							 
						 
					
				
			
		
	



	 
 


	Merge-CvadAcToSite -DeliveryGroups:
 


	
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #0200B0;
    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 #0200B0;
    background-color: #0200B0;
    color: #ffffff;
    padding: 10px;
}

.table_component td {
    border: 1px solid #0200B0;
    background-color: #0200B0;
    color: #ffffff;
    padding: 10px;
    font-family: Consolas,Courier New;
	font-size:12px;
}	
	
		
			
				
					
						
							*** Welcome to Citrix Virtual Apps and Desktops Automated Configuration ***
						 

						
							            *************************************************************************** 
							            *** To solve issues:        Show-CvadAcDocument                         *** 
							            ***************************************************************************
						 

						
							PS C:\Users\Niallf\Documents\Citrix\AutoConfig&gt; Merge-CvadAcToSite -DeliveryGroups 
							TASK [Auto-Config: Customer Id: CitrixOnPremises] ******************
						 

						
							The action you have selected will change the cloud site configuration; are you sure you want to proceed?  Type the full word response [Yes] for continue, any other key(s) to stop. 
							Yes 
							TASK [Auto-Config: Initializing for Merge] ************************* 
							 &gt;&gt;&gt; Ok
						 

						
							TASK [Delivery Group: Read and process expected components] ******** 
							....... &gt;&gt;&gt; Ok
						 

						
							TASK [Delivery Group: Read Cloud components] *********************** 
							. &gt;&gt;&gt; Ok
						 

						
							TASK [Delivery Group: Process Cloud components] ******************** 
							 &gt;&gt;&gt; Ok
						 

						
							TASK [Delivery Group: Backup Delivery Group] *********************** 
							 &gt;&gt;&gt; Ok
						 

						
							TASK [Delivery Group (1 of 1): Add Component: DG-Win2019-MultiSession] *********************** 
							.............. &gt;&gt;&gt; Added
						 

						
							TASK [Delivery Group: Read Cloud components] *********************** 
							. &gt;&gt;&gt; Ok
						 

						
							TASK [Delivery Group: Process Cloud components] ******************** 
							... &gt;&gt;&gt; Ok
						 

						
							TASK [Delivery Group: Backup Delivery Group] *********************** 
							 &gt;&gt;&gt; Ok
						 

						
							TASK [Delivery Group: Verify Components] *************************** 
							 &gt;&gt;&gt; Verified
						 

						
							TASK [LogFile: Location] ******************************************* 
							C:\Users\Niallf\Documents\Citrix\AutoConfig\Merge_2024_05_30_06_09_15\CitrixLog_Merge_2024_05_30_06_09_15.txt 
							    Fixups Count:            1
						 

						
							Automated Configuration version: 3.0.100.0
						 

						
							 
							Overall_Success    : True 
							Individual_Success : {[Zones, No Action], [Tags, No Action], [AdminScopes, No Action], [AdminRoles, No Action]...} 
							CustomResult       : Citrix.AutoConfig.Common.ComponentCommon.EvaluationResultData 
							FileSpecification  : 
							FileName           : 
							Duration           : 0 
							 
						 
					
				
			
		
	



	 
 


	Merge-CvadAcToSite -ApplicationGroups:
 


	
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #0200B0;
    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 #0200B0;
    background-color: #0200B0;
    color: #ffffff;
    padding: 10px;
}

.table_component td {
    border: 1px solid #0200B0;
    background-color: #0200B0;
    color: #ffffff;
    padding: 10px;
    font-family: Consolas,Courier New;
	font-size:12px;
}	
	
		
			
				
					
						
							            *** Welcome to Citrix Virtual Apps and Desktops Automated Configuration ***
						 

						
							            *************************************************************************** 
							            *** To solve issues:        Show-CvadAcDocument                         *** 
							            ***************************************************************************
						 

						
							PS C:\Users\Niallf\Documents\Citrix\AutoConfig&gt; Merge-CvadAcToSite -ApplicationGroups 
							TASK [Auto-Config: Customer Id: CitrixOnPremises] ********************************************************************** 
							The action you have selected will change the cloud site configuration; are you sure you want to proceed?  Type the full word response [Yes] for continue, any other key(s) to stop. 
							yes 
							TASK [Auto-Config: Initializing for Merge] ***************************************************************************** &gt;&gt;&gt; Ok
						 

						
							TASK [Application Group: Read and process expected components] ***** 
							..... &gt;&gt;&gt; Ok
						 

						
							TASK [Application Group: Read Cloud components] ******************** 
							. &gt;&gt;&gt; Ok
						 

						
							TASK [Application Group: Process Cloud components] ***************** 
							. &gt;&gt;&gt; Ok
						 

						
							TASK [Application Group: Backup Application Group] ***************** 
							 &gt;&gt;&gt; Ok
						 

						
							TASK [Application Group (1 of 1): Update Component: AG-Win2019-Tools] ************************************************* 
							 &gt;&gt;&gt; Updated
						 

						
							    TASK [Application Group (1 of 1): Add Application Group Delivery Groups: DG-Win2019-MultiSession] ***********************************
						 

						
							     &gt;&gt;&gt; Added
						 

						
							TASK [Application Group: Read Cloud components] ******************** 
							. &gt;&gt;&gt; Ok
						 

						
							TASK [Application Group: Process Cloud components] ***************** 
							. &gt;&gt;&gt; Ok
						 

						
							TASK [Application Group: Backup Application Group] ***************** 
							 &gt;&gt;&gt; Ok
						 

						
							TASK [Application Group: Verify Components] ************************ 
							 &gt;&gt;&gt; Verified
						 

						
							TASK [LogFile: Location] ******************************************* 
							C:\Users\Niallf\Documents\Citrix\AutoConfig\Merge_2024_05_30_07_21_44\CitrixLog_Merge_2024_05_30_07_21_44.txt
						 

						
							Automated Configuration version: 3.0.100.0
						 

						
							 
							Overall_Success    : True 
							Individual_Success : {[Zones, No Action], [Tags, No Action], [AdminScopes, No Action], [AdminRoles, No Action]...} 
							CustomResult       : Citrix.AutoConfig.Common.ComponentCommon.EvaluationResultData 
							FileSpecification  : 
							FileName           : 
							Duration           : 0 
							 
						 
					
				
			
		
	



	 
 


	Merge-CvadAcToSite -Applications:
 


	
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #0200B0;
    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 #0200B0;
    background-color: #0200B0;
    color: #ffffff;
    padding: 10px;
}

.table_component td {
    border: 1px solid #0200B0;
    background-color: #0200B0;
    color: #ffffff;
    padding: 10px;
    font-family: Consolas,Courier New;
	font-size:12px;
}	
	
		
			
				
					
						
							*** Welcome to Citrix Virtual Apps and Desktops Automated Configuration ***
						 

						
							            *************************************************************************** 
							            *** To solve issues:        Show-CvadAcDocument                         *** 
							            ***************************************************************************
						 

						
							PS C:\Users\Niallf\Documents\Citrix\AutoConfig&gt; Merge-CvadAcToSite -Applications 
							TASK [Auto-Config: Customer Id: CitrixOnPremises] ********************************************************************** 
							The action you have selected will change the cloud site configuration; are you sure you want to proceed?  Type the full word response [Yes] for continue, any other key(s) to stop. 
							yes 
							TASK [Auto-Config: Initializing for Merge] *****************************************************************************  
							&gt;&gt;&gt; Ok
						 

						
							TASK [Application: Read and process expected components] *************************************************************** 
							.... &gt;&gt;&gt; Ok
						 

						
							TASK [Application: Read Cloud components] ****************************************************************************** 
							. &gt;&gt;&gt; Ok
						 

						
							TASK [Application: Process Cloud components] *************************************************************************** 
							 &gt;&gt;&gt; Ok
						 

						
							TASK [Application: Backup Application] *********************************************************************************  
							&gt;&gt;&gt; Ok
						 

						
							TASK [Application (1 of 7): Add Component: Calculator] *****************************************************************  
							&gt;&gt;&gt; Added
						 

						
							TASK [Application (2 of 7): Add Component: Command Prompt] *************************************************************  
							&gt;&gt;&gt; Added
						 

						
							TASK [Application (3 of 7): Add Component: Microsoft Edge] *************************************************************  
							&gt;&gt;&gt; Added
						 

						
							TASK [Application (4 of 7): Add Component: Notepad] ********************************************************************  
							&gt;&gt;&gt; Added
						 

						
							TASK [Application (5 of 7): Add Component: Paint] **********************************************************************  
							&gt;&gt;&gt; Added
						 

						
							TASK [Application (6 of 7): Add Component: Snipping Tool] **************************************************************  
							&gt;&gt;&gt; Added
						 

						
							TASK [Application (7 of 7): Add Component: WordPad] ******************************************************************** 
							&gt;&gt;&gt; Added
						 

						
							TASK [Application: Read Cloud components] ************************** 
							. &gt;&gt;&gt; Ok
						 

						
							TASK [Application: Process Cloud components] *********************** 
							......... &gt;&gt;&gt; Ok
						 

						
							TASK [Application: Backup Application] ***************************** 
							&gt;&gt;&gt; Ok
						 

						
							TASK [Application: Verify Components] ****************************** 
							&gt;&gt;&gt; Verified
						 

						
							TASK [LogFile: Location] ******************************************* 
							C:\Users\Niallf\Documents\Citrix\AutoConfig\Merge_2024_05_30_07_22_50\CitrixLog_Merge_2024_05_30_07_22_50.txt
						 

						
							Automated Configuration version: 3.0.100.0
						 

						
							 
							Overall_Success    : True 
							Individual_Success : {[Zones, No Action], [Tags, No Action], [AdminScopes, No Action], [AdminRoles, No Action]...} 
							CustomResult       : Citrix.AutoConfig.Common.ComponentCommon.EvaluationResultData 
							FileSpecification  : 
							FileName           : 
							Duration           : 0 
							 
						 
					
				
			
		
	



	 
 


	Merge-CvadAcToSite -GroupPolicies:
 


	
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #0200B0;
    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 #0200B0;
    background-color: #0200B0;
    color: #ffffff;
    padding: 10px;
}

.table_component td {
    border: 1px solid #0200B0;
    background-color: #0200B0;
    color: #ffffff;
    padding: 10px;
    font-family: Consolas,Courier New;
	font-size:12px;
}	
	
		
			
				
					
						
							*** Welcome to Citrix Virtual Apps and Desktops Automated Configuration ***
						 

						
							            *************************************************************************** 
							            *** To solve issues:        Show-CvadAcDocument                         *** 
							            ***************************************************************************
						 

						
							PS C:\Users\Niallf\Documents\Citrix\AutoConfig&gt; Merge-CvadAcToSite -GroupPolicies 
							TASK [Auto-Config: Customer Id: CitrixOnPremises] ********************************************************************** 
							The action you have selected will change the cloud site configuration; are you sure you want to proceed?  Type the full word response [Yes] for continue, any other key(s) to stop. 
							Yes 
							TASK [Auto-Config: Initializing for Merge] *****************************************************************************  
							&gt;&gt;&gt; Ok
						 

						
							TASK [Group Policy: Read and process expected components] ************************************************************** 
							.. &gt;&gt;&gt; Ok
						 

						
							TASK [Group Policy: Read Cloud components] ************************* 
							. &gt;&gt;&gt; Ok
						 

						
							TASK [Group Policy: Process Cloud components] ********************** 
							. &gt;&gt;&gt; Ok
						 

						
							TASK [Group Policy: Backup Group Policy] *************************** 
							 &gt;&gt;&gt; Ok
						 

						
							TASK [Group Policy (1 of 3): Add Component: Disable Session Reliability] ********************************************** 
							... &gt;&gt;&gt; Added
						 

						
							TASK [Group Policy (2 of 3): Add Component: Allow Clipboard Redirection] ************************ 
							.. &gt;&gt;&gt; Added
						 

						
							TASK [Group Policy (3 of 3): Add Component: Baseline Policy] ******* 
							.. &gt;&gt;&gt; Added
						 

						
							TASK [Group Policy: NO CHANGE: Number of components not changed: 1 ] ******************************************* 
							 &gt;&gt;&gt; No Change
						 

						
							.... 
							TASK [Group Policy: Read Cloud components] ************************* 
							 &gt;&gt;&gt; Ok
						 

						
							TASK [Group Policy: Process Cloud components] ********************** 
							.... &gt;&gt;&gt; Ok
						 

						
							TASK [Group Policy: Backup Group Policy] *************************** 
							 &gt;&gt;&gt; Ok
						 

						
							TASK [Group Policy: Verify Components] ***************************** 
							 &gt;&gt;&gt; Verified
						 

						
							TASK [LogFile: Location] ******************************************* 
							C:\Users\Niallf\Documents\Citrix\AutoConfig\Merge_2024_05_30_07_24_34\CitrixLog_Merge_2024_05_30_07_24_34.txt
						 

						
							Automated Configuration version: 3.0.100.0
						 

						
							 
							Overall_Success    : True 
							Individual_Success : {[Zones, No Action], [Tags, No Action], [AdminScopes, No Action], [AdminRoles, No Action]...} 
							CustomResult       : Citrix.AutoConfig.Common.ComponentCommon.EvaluationResultData 
							FileSpecification  : 
							FileName           : 
							Duration           : 0
						 
					
				
			
		
	



	 
 


	Merge-CvadAcToSite -HostConnections -CheckMode:
 


	
.table_component {
    overflow: auto;
    width: 100%;
}

.table_component table {
    border: 1px solid #0200B0;
    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 #0200B0;
    background-color: #0200B0;
    color: #ffffff;
    padding: 10px;
}

.table_component td {
    border: 1px solid #0200B0;
    background-color: #0200B0;
    color: #ffffff;
    padding: 10px;
    font-family: Consolas,Courier New;
	font-size:12px;
}	
	
		
			
				
					
						
							*** Welcome to Citrix Virtual Apps and Desktops Automated Configuration ***
						 

						
							            *************************************************************************** 
							            *** To solve issues:        Show-CvadAcDocument                         *** 
							            ***************************************************************************
						 

						
							PS C:\Users\Niallf\Documents\Citrix\AutoConfig&gt; Merge-CvadAcToSite -HostConnections -CheckMode 
							&gt;&gt; 
							TASK [Auto-Config: Customer Id: CitrixOnPremises] *****************************
						 

						
							TASK [Auto-Config: Initializing for Merge: running in Check Mode] ************* 
							 &gt;&gt;&gt; Ok
						 

						
							TASK [Host Connection: Read and process expected components] ****************** 
							.. &gt;&gt;&gt; Ok
						 

						
							TASK [Host Connection: Read Cloud components] ********************************* 
							 &gt;&gt;&gt; Ok
						 

						
							TASK [Host Connection: Process Cloud components] ****************************** 
							. &gt;&gt;&gt; Ok
						 

						
							TASK [Host Connection: Backup Host Connection] ******************************** 
							 &gt;&gt;&gt; Ok
						 

						
							TASK [Host Connection: NO CHANGE: Number of components not changed: 1 ] ******* 
							 &gt;&gt;&gt; No Change
						 

						
							TASK [LogFile: Location] ****************************************************** 
							C:\Users\Niallf\Documents\Citrix\AutoConfig\Merge_2024_05_30_07_47_04\CitrixLog_Merge__CheckedMode_2024_05_30_07_47_04.txt 
							    Fixups Count:            2
						 

						
							Automated Configuration version: 3.0.100.0
						 

						
							 
							Overall_Success    : True 
							Individual_Success : {[Zones, No Action], [Tags, No Action], [AdminScopes, No Action], [AdminRoles, No Action]...} 
							CustomResult       : Citrix.AutoConfig.Common.ComponentCommon.EvaluationResultData 
							FileSpecification  : 
							FileName           : 
							Duration           : 0]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_06/ExportCommand.JPG.db647d5e9cd421b669b9d2188d0ef395.JPG" length="24101" type="image/jpeg"/><pubDate>Mon, 03 Jun 2024 09:19:47 +0000</pubDate></item><item><title>POC Guide: Citrix uberAgent + Splunk</title><link>https://community.stage.citrix.com/tech-zone/learn/poc-guides/citrix-uberagent/</link><description><![CDATA[Overview



	Monitoring both on-premises and virtual environments is essential for every enterprise as part of the operational model. A reliable data source for security and user experience offers valuable insights for daily tasks, security diagnostics and investigations, user experience, and proactive and reactive solutions.
 


	Citrix uberAgent is an advanced software solution meticulously crafted for monitoring desktops, virtual desktop infrastructure (VDI), and server-based computing environments. It goes beyond basic system metrics and delivers detailed insights into application performance, user experience, and system health. 
 


	This POC guide will walk you through the initial deployment process of uberAgent and show you how to facilitate data visualization using Splunk Enterprise (on-premises) or Splunk Cloud.
 


	Solution Overview



	Citrix uberAgent gathers security and performance information from the endpoint device (physical or virtual machines) and sends the data to your visualization solution. Out of the box, uberAgent provides 70+ Splunk dashboards to visualize data.
 


	 
 


	
 


	Architecture



	It is recommended that you review the Citrix uberAgent Tech Brief, which provides details about the architecture and capabilities of Citrix uberAgent.
 


	 
 


	
 


	 
 


	Prerequisites



	uberAgent:
 


	
		uberAgent requires a valid license file. Please see the licensing guide or contact your ATS or Citrix Sales.
	



	Splunk:
 


	
		Operating system: all that are supported by Splunk
	
	
		Splunk version: Splunk Enterprise 7.0 or newer or Splunk Cloud
	



	OS versions:
 


	
		Windows 10 or later
	
	
		Windows Server 2016 R2 or later
	
	
		macOS Monterey or newer
	



	Platforms supported:
 


	
		Citrix Virtual Apps and Desktops, Citrix DaaS, Microsoft RDS, VMware Horizon View, and Remote desktop session hosts (e.g., Citrix, VMware Horizon View, Microsoft RDS) are explicitly supported. The same applies to any virtual desktop (e.g., Citrix Virtual Apps and Desktops, Citrix DaaS, or VMware View).
	



	 
	Browser extensions: 
 


	
		Google Chrome
	
	
		Edge
	
	
		Firefox
	



	 
 


	Installation



	The installation consists of four configuration steps:
 


	
		Create a Splunk Enterprise (on-premises) trial instance or register a Splunk Cloud trial.
	
	
		Install uberAgent UXM and ESA applications in the Splunk instance.
	
	
		Install uberAgent on the endpoint device (physical machine, virtual Server, or master/gold image).
	
	
		Install uberAgent to monitor the Citrix Site. 
	



	Create Splunk Enterprise Trial Instance



	Read on if you prefer an on-premises Splunk instance. If you want to go with Splunk Cloud, go to the next section.
 


	
		Download Splunk Enterprise software from the downloads page. You can request a free trial or use your company's instance. We will use the trial version for this guide, which provides 500MB/day for 60 days.
	



	
 


	
		Fill out the form to create your Splunk account to access the software.
	



	
 


	 
 


	
		Install Splunk on your dedicated server and follow the configuration wizard with default values. 
	
	
		Accept the “License Agreement” and click Next.
	



	
 


	
		Enter the Username and Password to access your Splunk instance.
	



	
 


	
		Click Install to begin the installation process. The installation process will take a few minutes.
	



	
 


	
		Once the installation process is completed, click Finish. The Splunk Management Console will launch. Enter your username and password previously created.
	



	
 


	 
	 
 


	
		Note: 
	 

	
		(Optional) If you have a Splunk License, install it. Here is the link to the process:
	 

	
		https://docs.splunk.com/Documentation/Splunk/latest/Admin/Installalicense.
	 



	 
 


	Install uberAgent UXM and ESA applications in the Splunk instance 



	The next step is to install the UXM (uberAgent User Experience) and ESA (uberAgent Security) applications in Splunk. This will add the uberAgent capabilities and out-of-the-box Dashboards to your Splunk instance.
 


	 
 


	The result is the following in your console:
 


	
 


	 
	 
 


	Follow the link to download the uberAgent software from citrix.com.
 


	Select the latest available version and download the software.
 


	 
 


	A .zip file with all uberAgent components for your endpoint devices and Splunk applications will be downloaded.
 


	 
 


	Extract the folder to your desired location.
 


	
 


	 
 


	Go to the uberAgent Components folder and identify the three files we will use to install the UXM (uberAgent User Experience) and ESA (uberAgent Security) applications in Splunk.
 


	
 


	 
 


	Open an Internet Browser and navigate to your Splunk console's Home Page by typing http://servername:8000. Then, log in to Splunk using your username and password.
 


	 
 


	
 


	 
	 
 


	In the left top corner, select Apps and click Manage.
 


	
 


	 
 


	On the next screen, click Install app from file in the top right corner.
 


	Click Choose File
 


	Select each uberAgent_####.tgz file
 


	uberAgent_ESA_searchhead.tgz
 


	uberAgent_indexer.tgz
 


	uberAgent_searchhead.tgz
 


	Click Upload
 


	 
 


	
 


	
		Once all three .tgz files are uploaded, go to Home &gt; Settings &gt; Server Control &gt; Restart Splunk to restart the Splunk instance.
	



	
 


	
 


	 
 


	
		Log in again to the Splunk instance.
	



	
 


	 
 


	
		Validate that the UXM and ESA uberAgent applications are available. Then, click on each app to launch the Dashboard. There will not be any data initially.
	



	
 


	 
 


	Register Splunk Cloud Trial



	If you want to use uberAgent with Splunk Cloud, continue here. If you prefer an on-premises Splunk Enterprise, refer to the previous section.
 


	A Splunk Cloud trial is good for 14 days and 5 GB of ingested data per day.
 


	Create an account at splunk.com by signing up at the top right.
 


	
 


	Fill out the registration form and click Create Your Account at the bottom.
 


	After the registration, log in to splunk.com and click Free Splunk at the top right.
 


	 
 


	Click Start Trial. Check your inbox for the credentials and log on to the Splunk Cloud instance. 
 


	
 


	Install the uberAgent indexer Splunk app



	
		In the Apps menu of your Splunk instance, click on Find More Apps to get to the app installation.
	



	  
 


	
		Search for uberAgent and choose uberAgent indexer app.
	



	  
 


	
		Enter the credentials of your Splunk.com account and click on Agree and Install.
	



	  
 


	
		Click Done.
	



	Create the uberAgent index



	
		 Click on Settings -&gt; Indexes at the top right of your Splunk instance.
	



	  
 


	
		Click New Index. Add the uberagent index. 
	



	 
 


	
		Note
	 

	
		All characters are lowercase.
	 



	  
 


	
		Add the score_uberagent_uxm index.
	
	
		Repeat step three and add the index score_uberagent_esa. 
	



	 
 


	Install the uberAgent dashboard apps



	
		In the Apps menu of your Splunk instance, click on Find More Apps to get to the app installation.
	



	
 


	 
 


	
		Search for uberAgent and select uberAgent UXM - Digital Employee Experience (DEX).
	



	
 


	 
 


	
		Repeat the steps to add the uberAgent ESA - Endpoint Security Analytics and uberAgent Helpdesk apps.
	



	Enable data model acceleration



	
		Click on Settings -&gt; Data models at the top right of your Splunk instance.
	



	
 


	 
 


	
		The following steps must be performed on each data model, except uberAgentESA_System_SecurityInventory. Click Edit and choose Edit Acceleration.
	



	
 


	 
 


	
		Tick the box for Accelerate and click Save.
	



	
 


	Add a Splunk HEC input



	
		Click on Settings -&gt; Data inputs at the top right of your Splunk instance.
	



	  
 


	
		 Click on Add new for the type HTTP Event Collector.
	



	  
 


	
		Give it the name uberagent.
	



	  
 


	
		Select the uberagent index in the input settings.
	



	 
 


	
		Review the settings.
	



	
 


	
		Copy the displayed token. We need the token in the next chapter.
	



	
 


	 
 


	Endpoint Device uberAgent Installation



	Once you have the Splunk instance ready, install uberAgent on your endpoint device. Remember, the endpoint device can be a physical machine, virtual server, client, or master/gold image.
 


	Citrix Virtual Apps and Desktops Integrated



	uberAgent is a component of the Citrix Virtual Apps and Desktops (CVAD) meta-installer since version 2503. The installation process is straightforward, with administrators simply selecting uberAgent from the Optional Software screen during the CVAD VDA setup. Note that it is selected by default.
 


	
 


	After the installation has finished, download the Config &amp; Support Tool. Run the tool and select Configure uberAgent.
 


	
 


	The following window lets you configure uberAgent and create a config archive, that you can distribute with Citrix Policies later.
 


	Select Web/GitHub as configuration source. The correct link is automatically detected.
 


	We recommend checking Use Data Volume Optimized Config for a PoC. It keeps the generated data volume low while showing sufficient details for the PoC phase. You may change to the normal configuration later.
 


	Receiver Type: Splunk
 


	Protocol:
 


	
		Splunk Cloud: always HTTP
	
	
		Splunk Enterprise: TCP or HTTP. TCP is sufficient for a PoC
	



	Servers: your Splunk Cloud or Splunk Enterprise servers. You can specify multiple Enterprise servers comma separated. A port is mandatory. Format: http(s)://server/IP:port.
 


	REST Token: the Splunk HTTP event collector token. Only required when the protocol is HTTP.
 


	Disable Certificate Validation: Check if you are using Splunk Cloud trial or a self-signed certificate.
 


	Use Default uberAgent Index: the default uberAgent index is named uberagent. If your Splunk team has other naming conventions, you can specify a different name by unchecking the box. Also see the documentation on the topic.
 


	License: leave empty as the license file is distributed through Citrix policies.
 


	Click on Create Config Archive.
 


	
 


	Go to Citrix Web Studio and create a new policy. The uberAgent related settings can be found in the root and under VDA Data Collection.
 


	
 


	Enable uberAgent data collection: enabled
 


	Enhance Director with uberAgent data: enabled
 


	uberAgent configuration archive path: local directory or file share where the above created config archive is stored. If you use a file share, the computer account needs read permissions on the share as uberAgent is running as SYSTEM.
 


	uberAgent license path: local directory or file share where the license file is stored. If you use a file share, the computer account needs read permissions on the share as uberAgent is running as SYSTEM.
 


	
 


	Assign the policy to your PoC VDAs.
 


	The configuration has finished. As soon as the Citrix configuration is applied to the VDA, uberAgent starts sending data to Splunk. Note that your VDA must be a member of a delivery group to apply policies.
 


	Standalone



	
		Note
	 

	
		This section applies to uberAgent version 7.4.0.
	 



	If you not already have downloaded uberAgent, download it from citrix.com and extract the downloaded .zip file.
 


	Install uberAgent as follows:
 


	
		Open the uberAgent unzipped folder.
	
	
		Go to uberAgent Components → bin → uberAgent-en-64.msi → right-click and Install
	



	 
 


	
		Note: 
	 

	
		If you are using Citrix AppLayering, installing the agent in the Platform Layer is recommended.
	 



	 
 


	Accept the license terms and click Next.
 


	Continue with the default Destination folder.
 


	
 


	Continue and select both options for Configuration Security.
 


	 
 


	
 


	 
 


	Click Install.
 


	 
 


	
 


	 
 


	Click Finish to complete the installation.
 


	
 


	After the installation has finished, download the Config &amp; Support Tool. Run the tool and select Configure uberAgent.
 


	
 


	The following window lets you configure uberAgent and apply the configuration.
 


	Select Web/GitHub as configuration source. The correct link is automatically detected.
 


	We recommend checking Use Data Volume Optimized Config for a PoC. It keeps the generated data volume low while showing sufficient details for the PoC phase. You may change to the normal configuration later.
 


	Receiver Type: Splunk
 


	Protocol:
 


	
		Splunk Cloud: always HTTP
	
	
		Splunk Enterprise: TCP or HTTP. TCP is sufficient for a PoC
	



	Servers: your Splunk Cloud or Splunk Enterprise servers. You can specify multiple Enterprise servers comma separated. A port is mandatory. Format: http(s)://server/IP:port.
 


	REST Token: the Splunk HTTP event collector token. Only required when the protocol is HTTP.
 


	Disable Certificate Validation: Check if you are using Splunk Cloud trial or a self-signed certificate.
 


	Use Default uberAgent Index: the default uberAgent index is named uberagent. If your Splunk team has other naming conventions, you can specify a different name by unchecking the box. Also see the documentation on the topic.
 


	License: select your license file.
 


	
 


	Click on Apply. The tool configures uberAgent, copies the license file to the correct location and starts the service.
 


	Preparing a Citrix Master/Gold image



	If you use an imaging method such as Machine Creation Services (MCS), Citrix Provisioning (PVS), or Citrix AppLayering, it is recommended that you generalize uberAgent to prepare the image for deployment.
 


	Stop the uberAgent service (leave it Automatic).
 


	Open a command prompt as an Administrator.
 


	Run the following command: reg delete "HKLM\SOFTWARE\vast limits\uberAgent" /f /reg:64
 


	(Optional): Delete the existing uberAgent.log file at C:\Windows\Temp.
 


	
 


	 
 


	 
 


	 
 


	Add uberAgent extension to Web Browsers



	For this POC, we are going to add the Chrome browser extension. If you use Firefox or Edge, follow the links for reference.
 


	There are two options to install the extension for Chrome:
 


	
		Directly from the Chrome store
	
	
		Group Policy
	



	We are going to install this POC directly from the Chrome Store.
 


	Launch Chrome and go to the following URL:
 


	https://chromewebstore.google.com/detail/uberagent/jghgedlkcoafeakcaepncnlanjkbinpb?pli=1
 


	Click on Add to Chrome.
 


	Click on Add Extension.
 


	After enabling the extension, you receive the notification uberAgent has been added to Chrome.
 


	
 


	 
 


	Now that the master/gold image and the Browser extension are ready, we can install uberAgent on the Citrix Delivery Controller for on-premises Citrix Virtual Apps and Desktops deployments or on the Cloud Connectors for Citrix DaaS deployments.
 


	Monitor Citrix Virtual Apps and Desktops Sites



	uberAgent detects whether it runs on a Citrix Delivery Controller (DDC) or a Citrix Virtual Desktop Agent (VDA). On DDCs, uberAgent automatically activates additional metrics like machine registration status, license usage, and published application inventory. There are some recommendations for installing uberAgent to monitor Citrix sites, including:
 


	
		Install the uberAgent endpoint agent on at least one delivery controller per site.
	
	
		Before installing the agent, run the following script template to grant the user account the required permissions. Before running it with elevated permissions, fill in your domain names and DDCs.
	
	
		Required permissions:
		
			
				Each delivery controller’s computer account
			
			
				The local SYSTEM account.
			
		
	



	Add-PSSnapin Citrix.DelegatedAdmin.Admin.V1
 


	New-AdminAdministrator -Sid S-1-5-18 -Enabled $true
 


	Add-AdminRight -Role 0a05f0c6-0153-4852-a55a-989d6a95c0eb -Administrator S-1-5-18 -All
 


	New-AdminAdministrator -Name &lt;Domain&gt;\&lt;computer account&gt; -Enabled $true
 


	Add-AdminRight -Role 0a05f0c6-0153-4852-a55a-989d6a95c0eb -Administrator &lt;Domain&gt;\&lt;computer account&gt; -All
 


	For more details, please refer to the following documentation.
 


	Monitoring Citrix DaaS Sites



	Requirements:
 


	
		Create a Citrix Cloud API client as described in our Citrix docs. 
	
	
		Go to citrix.cloud.com and log in with your credentials.
	
	
		Install the Citrix Virtual Apps and Desktops Remote Powershell SDK on the endpoint on a separate server/machine.
	



	
		Note:
	 

	
		The Citrix Cloud API client name is case-sensitive. Name it “uberAgent”.
	 



	
 


	 
	 
 


	Configuration:
 


	
		Once you install the agent on the VM/Server, open the C:\ProgramData\vast limits\uberAgent\Configuration\uberAgent.conf file.
	
	
		Search for CitrixCloud.
	
	
		Replace the content as shown in the screenshot:
	



	[CitrixCloud_Config]
 


	API endpoint = https://api-us.cloud.com
 


	CustomerId = &lt;CustomerId&gt;
 


	ClientId = &lt;ClientId&gt;
 


	ClientSecret = &lt;ClientSecret&gt;
 


	CollectCitrixCloudInformation=True
 


	 
 


	
		Restart the uberAgent service to complete the process.
	



	
 


	 
 


	
		Note: 
	 

	
		The API endpoint URL depends on your region:
	 

	
		Americas: API endpoint = https://api-us.cloud.com
	 

	
		Europe: API endpoint = https://api-eu.cloud.com
	 



	 
 


	After installing the uberAgent agent in your master/gold image, seal it and deploy it to your Citrix workload using your usual distribution method (MCS/PVS/AppLayering).
 


	For more details, please refer to the following documentation.
 


	Testing and Final Result



	Once completed, validate that the machines where you installed the uberAgent agent are shown in the Splunk Dashboard uberAgent UXM under Machines tab &gt; Machine Performance.
 


	To complete the testing, start a machine, launch a new session, and open an application. Wait a few minutes to allow the agent to capture data. All data will be available in the Splunk Dashboards for uberAgent User Experience (UXM) and uberAgent Security (ESA).]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2025_05/image.png.f60702d8968be0055050434c99f1067a.png" length="133878" type="image/png"/><pubDate>Tue, 28 May 2024 16:15:00 +0000</pubDate></item><item><title>POC Guide: Citrix Profile Management with Workspace Environment Management</title><link>https://community.stage.citrix.com/tech-zone/learn/poc-guides/citrix-profiles-wem/</link><description><![CDATA[Overview



	Modern Citrix Management through WEM tools provides advanced User Profile solutions to deploy Traditional file base and Container-based profiles in a simplified way through the built-in templates and granular configuration for your WEM configuration sets.
 


	This guide shows how to deploy a Citrix Profile Management solution using the Workspace Environment Management and the WEM Tool Hub.
 


	 Prerequisites
 


	You will use the Workspace Environment Management toolset to deploy your profile solution. These tools can be run from the same location, allowing you to centralize the configuration.
 


	
		WEM Web console: The new Citrix Web console for WEM allows you to use the Out of the Box templates to set up your profile solution.   Run Citrix Workspace Environment Management Web Console.exe for on-premises environments to install the web console services. By default, the infrastructure services are installed in the following folder: 
	



	C:\Program Files (x86)\Citrix\Workspace Environment Management Web Console.
 


	
		WEM Tool Hub: WEM Tool Hub is a collection of tools that aims to simplify the configuration experience for Workspace Environment Management administrators. As part of the Profile configuration, you need a Storage repository to allocate your profiles. That’s where you will use the WEM Tool Hub to configure everything from the same place. This tool provides a configuration wizard to create the repository in your File Server, create the shared folder, and assign it to your users. Go to the WEM Utilities tab in the WEM web console to download it.
	



	Profile Configuration
 


	
		The first step is to enable” the Profile Management settings in the Workspace Environment Management web console. Go to Configuration Set name &gt; Profiles &gt; Profile Management Settings and click the Enable toggle. This will enable the configuration options and the Built-in Templates.
	



	 
 


	
 


	 
	 
 


	
		Once enabled, you can access Quick Setup in the top right corner. This is where you access the built-in Profile Templates. You have two options:

		
			
				Restore a backup of a previous Profile configuration.
			
			
				Start a new configuration with a template.
			
		
	
	
		We will use Start with Template to configure a fresh Profile solution for our deployment.
	



	 
 


	
 


	 
 


	
		After this, you start your configuration by selecting the type of Profile solution you want. In our case, we choose Container-based.
	



	
 


	
		The Template-based deployment through Workspace Environment Management provides access to centralize your Profile configuration. 
	



	 
 


	Setting up the Profile solution



	
		The first step in setting up the profile solution is to set the basic settings. There are two mandatory inputs for the configuration and a few more optional settings:
	



	
		Enable Profile Management: This option enables WEM to manage your profile settings.
	
	
		Set path to user store: This is the store location where the profiles will be located.
	



	
		Note:
	 

	
		To configure this, we will use the WEM Tool Hub advanced feature to centralize the configuration.
	 



	 
 


	
		For File-Based Profiles, use the Basic Settings section to configure the user store path as seen here:
	



	
 


	 
 


	
		For Container Based Profiles, use the Profile Container section to configure the Profile Container Content as seen here:
	



	
 


	Install and Configure the WEM Tool Hub
 


	
		Find the WEM Tool Hub install file you downloaded earlier. Install and Open the WEM Tool Hub and select User Store Creation Tool.
	



	
 


	 
 


	
		This feature will start a configuration wizard to create the Store for the Profiles. It walks you through the steps where you enter the required information:

		
			
				Where would you like to create the user store? Specify whether you want to use an existing machine or another, such as a File server. Enter the Server name and Admin credentials to create the connection.
			
			
				Specify the Folder Path. You can select an existing folder or be notified if you are trying to create a new one with an existing name.
			
			
				(Optional) Create a Shared Folder. WEM Tool Hub allows you to create a shared folder without accessing the file server.
			
			
				User Groups: Specify what Active Directory groups will be assigned to this Store.
			
		
	



	
 


	 
 


	
		Once you enter the required information, a folder is created to use as your User Store Path. Copy the information and add it to the Profile configuration.
	



	
 


	Profile Container Configuration
 


	Once you create the Profile Container Folder in the WEM Tool Hub and enter the path in the WEM configuration template, you can continue fine-tuning your Profile solution.
 


	You have advanced options to manage your Profile:
 


	
		Enable Folder and file Exclusions.
	
	
		Enable file Inclusions.
	
	
		Enable VHD Auto expansion: If enabled, when the profile container reaches 90% utilization, it automatically expands by 10 GB, with a maximum capacity of 80 GB. Depending on your needs, you can adjust the default auto-expansion settings using the following options: Auto-expansion trigger threshold (%), Auto-expansion increment (GB), and Auto-expansion limit (GB).
	



	
		Set users and groups access to profile containers: 
	



	
 


	Profile Handling
 


	These optional settings allow you to specify how to manage the User Profile. Depending on your profile needs, you can choose the following options:
 


	
		Delete locally cached profiles on logoff: This setting sets a delay before deleting cached profiles. The time is set in seconds, and the value can be between 0 and 600 seconds.
	
	
		Enable Template Profile: Profile Management can use a centrally stored template when creating profiles. The template can be a standard roaming, local, or mandatory profile on any network file share.
	



	There are three options:
 


	
		Template Overrides local profile.
	
	
		Template overrides roaming profile.
	
	
		Use template profile as Citrix Mandatory profile.
	



	 
 


	
		Note:
	 

	
		In Path to the template profile, enter the location of the profile you want to use as a template or mandatory profile. This path is the full path to the folder containing the NTUSER.DAT registry file and any other folders and files required for the template.
	 



	 
 


	
 


	Advanced Settings
 


	Profile configuration through Workspace Environment Management allows you to apply advanced settings. In this section, we set up the following features:
 


	
		Microsoft Applications Profile Containers: This part allows you to configure your profile container for Outlook, OneDrive, and Universal Windows Platform
	
	
		Microsoft Outlook search index: The user-specific Microsoft Outlook offline folder file (*.ost) and Microsoft search database are roamed along with the user profile, improving the user experience when searching mail in Microsoft Outlook.
	
	
		OneDrive container: Profile Management roams OneDrive folders with users by storing the folders on a VHDX disk. The disk is attached during logins and detached during logoffs.
	
	
		MSFT - UWP: Universal Windows Platform roaming.
	



	
 


	 
 


	
		Advanced VHD settings: Here, you can define the size of the containers and enable features such as Disk compaction and Multi-session write-back profile

		
			
				Multi-session write-back for profile containers. If enabled, profile Management saves changes in multi-session scenarios for FSLogix Profile Container and Citrix Profile Management profile containers. If the same user launches multiple sessions on different machines, changes made in each session are synchronized and saved to the user’s profile container disk.
			
			
				Customize storage path for VHDX files. This option lets you specify a separate path to store VHDX files. By default, VHDX files are stored in the user store. 
			
			
				VHD disk compaction. If enabled, VHD disks are automatically compacted on user logoff when certain conditions are met. This policy lets you save the storage space consumed by the profile container, OneDrive container, and mirror folder container. You can adjust the default VHD compaction settings and behavior depending on your needs and available resources.
			
		
	



	
 


	 
 


	Profile Validation



	Once a user session is initiated, Workspace Environment Management will create the Profile container in the predefined store location created with the WEM Tool Hub.
 


	
 


	Summary
 


	This guide walked you through the installation and configuration of Citrix Profiles for a Citrix DaaS or Citrix Virtual Apps and Desktops deployment, utilizing Workspace Environment Management tools to centralize the configuration and management. During the process, we used the WEM Tool Hub to create the profile store and templates for the Workspace Environment Management Web console. Finally, we validated the creation of the profile.]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_05/image.png.7f5342ab7b8a90c46b6a0bf1fe2cb44e.png" length="89090" type="image/png"/><pubDate>Wed, 08 May 2024 13:50:58 +0000</pubDate></item><item><title>Tech Brief:  Citrix uberAgent</title><link>https://community.stage.citrix.com/tech-zone/learn/tech-briefs/uberagent/</link><description><![CDATA[Citrix uberAgent is an advanced software solution meticulously crafted for monitoring desktops, virtual desktop infrastructure (VDI), and server-based computing environments. It goes beyond basic system metrics and delivers detailed insights into application performance, user experience, and system health. With a focus on simplicity and efficiency, Citrix uberAgent is engineered to minimize performance impact on monitored systems while offering leading-edge capabilities for analyzing user experience and system performance. 
 


	The software provides in-depth visibility into network utilization at both the operating system and per-application levels, even for multiple installed browsers. Its key strength lies in offering granular data on user experience, application, operating system usage, and security metrics. This includes generating individual and customizable experience and risk scores, aiding in a holistic understanding of system performance and user experience.  
 


	All in all, Citrix uberAgent stands out for its remarkable sophistication. It is an indispensable asset for businesses aiming to comprehensively grasp their system performance and user experience.
 


	 
 


	
		Note:
	 

	
		uberAgent User Experience Monitoring (UXM) and Endpoint Security Analytics (ESA) are now components of Citrix Platform Licensing and Citrix observability (Analytics) Kickstart offerings.
	 



	 
 


	Citrix uberAgent key functionalities and capabilities



	
		Application Performance Monitoring: Citrix uberAgent tracks application startup times, hang detection, and resource usage, providing insights into how applications behave and perform in real-world scenarios.
	
	
		User Experience Score: At its core, Citrix uberAgent offers advanced user experience monitoring, helping IT departments identify and resolve issues related to logon times, session performance, and overall user satisfaction. This is crucial in environments where user productivity directly impacts business outcomes.
	
	
		Endpoint Security Analytics (ESA): With the ESA feature, Citrix uberAgent aids in identifying potential security threats by monitoring process creations, network connections, and other events that could indicate malicious activity. This functionality enriches security incident and event management (SIEM) systems with valuable context, enhancing organizational security posture.
	
	
		Network and Browser Performance: Citrix uberAgent provides detailed insights into network connection quality and browser performance, including HTTP request timings and web app usage. This enables IT to optimize network settings and web applications for better performance.
	
	
		Resource Utilization: It tracks CPU, memory, disk, and network usage by applications and users, helping identify resource bottlenecks and optimize capacity planning.
	



	 
 


	Conceptual Architecture



	
 


	 
 


	User Experience Monitoring (UXM)



	Experience score is a standout feature of Citrix uberAgent that focuses on various aspects of the user experience, such as application responsiveness, logon duration, and session reliability. This module lets organizations pinpoint end-user satisfaction and productivity issues, offering actionable insights to improve application delivery and performance. UXM is especially valuable in complex and distributed IT environments where identifying the root cause of performance issues can be challenging.
 


	The experience score dashboard serves as the gateway to the Citrix uberAgent UXM Splunk app. It visually represents the experience scores for the entire estate, segregating the data by category and component. The dashboard highlights components that could cause issues. The dashboard also offers quick access to essential KPIs such as logon duration, application responsiveness, and application errors. This feature empowers users to identify the origins of problems and take corrective measures promptly.
 


	
 


	



	 



	Score Calculation



	The scores are calculated regularly, assessing them against low and high severity thresholds with corresponding weights. The final score is derived by subtracting the product of threshold counters and their weights from 10. Higher weights correspond to lower scores.
 


	The score's default calculation settings have been carefully selected and tuned. They should work well in most environments. Please check our documentation if you want to optimize the calculation for your organization’s requirements.
 


	 
 


	User Logon Duration



	Citrix uberAgent collects logon details like profile load time, Group Policy processing time, and process performance.
 


	
 


	 



	Application Network Monitoring



	Citrix uberAgent has a network monitoring feature that diligently monitors outgoing network connections. This feature associates each network connection with the respective application handling it and generates important metrics such as latency, packet loss, data volume, process, host target, and user.
 


	In addition to its extensive capabilities, Citrix uberAgent's per-application network monitoring records failed connections that firewalls may have blocked, thus providing an additional layer of security. However, it is essential to note that Citrix uberAgent's per-application network monitoring feature does not inspect packets or breach TLS or other types of encryptions.
 


	
 


	 
 


	Web App Monitoring



	Citrix uberAgent provides web application monitoring to track page loads and inter-page communication on all major browsers for every website. The monitoring system collects essential data for every event, including the user, duration, and HTTP status code. The system's default configuration does not send complete URLs to the backend. Instead, the events are summarized based on the web server, the host displayed in the address bar. However, the complete URL monitoring feature can also be enabled for specific domains or sites of interest.
 


	
 


	
 


	 



	Citrix NetScaler (ADC) Monitoring



	Citrix uberAgent collects appliance and gateway performance, utilization, and inventory data from Citrix NetScaler Application Delivery Controllers for monitoring purposes.
 


	
 


	 
 


	Citrix Virtual Apps and Desktops site monitoring (CVAD and DaaS)



	Citrix uberAgent detects whether it runs on a Citrix Delivery Controller (DDC) or a Citrix Virtual Desktop Agent (VDA). On DDCs, Citrix uberAgent automatically activates additional metrics like machine registration status, license usage, and published application inventory.
 


	Citrix site monitoring collects a rich set of metrics about many aspects of Citrix Virtual Apps and Desktops:
 


	
		Published applications
	
	
		Databases
	
	
		Desktops
	
	
		Desktop groups
	
	
		Hypervisors
	
	
		Licenses
	
	
		Machines
	
	
		Machine Catalogs
	



	
 


	 



	Machine Metrics



	Citrix uberAgent collects information about the machine, such as the  CPU, GPU, disk IO, memory usage, and overall machine inventory. 
 


	
 


	 
 


	Endpoint Security Analytics (ESA)



	ESA extends Citrix uberAgent's capabilities into the security domain, providing detailed analytics on endpoint security events. This includes monitoring and analyzing process start events, DNS queries, and network connections. ESA is designed to complement existing EDR solutions like Defender. With ESA, IT security teams can quickly identify suspicious activities and potential breaches, significantly reducing the time to respond to security incidents.
 


	
 


	



	



	 



	Security &amp; Compliance Inventory



	The Security &amp; Compliance Inventory (SCI) is a testing and rating framework that checks the attack surface of your operating systems and applications. The test results are used to calculate security scores pinpointing configuration and security hardening weaknesses.
 


	uberAgent ESA comes with a comprehensive suite of SCI tests that cover a broad range of attack scenarios, including, but not limited to, the following.
 


	
		Man-in-the-Middle Attacks
	
	
		PowerShell Abuse
	
	
		Lateral Movement
	
	
		Passwordless Login
	



	 
 


	Threat Detection Events



	This dashboard provides an overview of all the processes identified as exhibiting risky behavior. The data presented is enriched with information from the MITRE ATT&amp;CK® framework, widely recognized as a comprehensive knowledge base of adversary tactics, techniques, and procedures. The dashboard provides detailed insights into the processes that exhibit suspicious activity, allowing you to take quick and appropriate action to safeguard your system from potential threats.
 


	
 


	
 


	 



	DNS Exfiltration &amp; Tunneling



	This dashboard offers a comprehensive range of data and metrics related to DNS exfiltration and tunneling activities on endpoints. By analyzing this data, users can gain insights into the nature and scope of these activities, including the types of endpoints targeted, the frequency and volume of exfiltration and tunneling events, and the methods used to carry out these activities. In addition, the dashboard can provide information on potential vulnerabilities that may be exploited and recommendations for enhancing endpoint security and minimizing the risk of data loss or theft.]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_05/image.png.bf1a962c4b8ee6718a44eb53f3f013c2.png" length="111159" type="image/png"/><pubDate>Tue, 30 Apr 2024 12:36:59 +0000</pubDate></item><item><title>Workspace Environment Management - Security</title><link>https://community.stage.citrix.com/tech-zone/learn/tech-briefs/wem-security/</link><description>Overview



	This Tech Brief explores the pivotal security features embedded within Citrix Workspace Environment Management (WEM). In today&#x2019;s rapidly evolving digital landscape, robust application security within WEM is not just a luxury. It is a necessity. By harnessing the power of WEM, organizations can bolster their security posture, seamlessly navigate the complexities of application management, and protect their environment against many threats. This Tech Brief provides an overview of WEM&#x2019;s security features, designed to empower IT administrators with the tools they need to enforce security measures effectively. It covers mechanisms such as Execution Rules, Windows Installer Rules, Script Rules, Packaged Rules, the intricacies of Process Management, and Privilege Elevation within WEM.
 


	 
 


	Through this article, we will try to highlight the benefits and operational implications of WEM&#x2019;s security capabilities. We offer a blend of technical details suited for seasoned IT professionals while remaining accessible to those new to the domain. We aim to demystify WEM's security features, highlighting how they integrate into broader IT security strategies to safeguard your organizational environment.
 


	In the following sections, we explore each feature in detail, outlining their roles, use cases, and the subsequent benefits they offer.
 


	Application Security



	Application security offers various mechanisms to bolster security within virtual and physical environments. Executable, Windows Installer, Script, and Packaged Rules. A crucial element to using each section is first adding the default rules. These predefined rules are a foundational starting point before any deny rules should be applied.
 


	Execution Rules



	The implementation of Execution Rules significantly strengthens application security. These rules serve as a line of defense against unauthorized or malicious software executions within the digital workspace. The types of rules that fall under this category can be broadly classified into several types based on their operational criteria:
 


	
		Path: These rules specify the exact paths from which applications can execute. Administrators can ensure that only applications from trusted locations can run by enforcing rules based on directory paths.
	
	
		Publisher: This rule leverages publisher information to allow or disallow applications. It is predicated on the publisher's information (Publisher, Product name, file name, and version) to ensure the application's legitimacy.
	
	
		Hash: This more granular approach permits or blocks applications based on their unique hash values. Even if the application moves locations or is renamed, the hash remains the same, providing a consistent method for control.
	



	
 


	 
 


	Execution Rules are pivotal for fortifying application security in several ways:
 


	
		Prevent Malware Execution: By specifying which applications can run and which cannot, Execution Rules prevent the execution of unauthorized or malicious programs that could potentially introduce malware into the system.
	
	
		Enforce Software Compliance: These rules ensure that only licensed and approved applications are run in the user environment, aiding in compliance with software licensing and organizational policies.
	
	
		Limit Application Surface Attacks: Controlling application execution reduces the surface for potential attacks. This is particularly beneficial in minimizing the risk of zero-day attacks, where newly discovered software vulnerabilities could be exploited if not promptly patched.
	
	
		User Access Control: Execution Rules can be configured to apply to specific users or user groups, thus tailoring the application access according to job roles and requirements. This means enhanced security without impeding workflow efficiency.
	



	By integrating Execution Rules into the wider security framework, WEM empowers administrators to construct a more resilient and secure workspace environment, minimizing risks while ensuring that productivity tools remain readily available to authorized users. These rules are crucial to the dynamic security posture necessary to confront the evolving threat landscape organizations face today.
 


	 Windows Installer Rules



	Windows Installer Rules within WEM are specifically designed to manage the installation of software packages. These rules govern how Windows Installer (.msi files) operates within the managed environment, allowing administrators to control which software can be installed, updated, or removed. Windows Installer Rules work by defining the permissions around Windows Installer Packages based on criteria such as:
 


	&#xB7;    Path: These rules specify the location from which the installation can be initiated. Administrators can ensure that only installations from trusted locations can run by enforcing rules based on directory paths.
 


	&#xB7;    Publisher: This rule leverages the software publisher&#x2019;s information to allow or disallow installs. It is predicated on the publisher's file information (Publisher, Product name, file name, and version) to ensure the legitimacy of the installations.
 


	&#xB7;    Hash: This more granular approach permits or blocks installs based on their unique hash values. Even if the installer moves location or is renamed, the hash remains the same, providing a consistent method for control.
 


	 
 


	 
 


	These rules can be applied globally or targeted to specific user groups, providing flexibility and control over the software deployment landscape.
 


	Windows Installer Rules are pivotal in maintaining the security and compliance of software installations within an organization:
 


	
		Maintain Software Compliance: They help ensure that only authorized installations occur, preventing the deployment of unlicensed software and reducing legal risks.
	
	
		Enhance System Stability: By controlling software installations, these rules help maintain system stability and prevent conflicts between programs.
	
	
		&#xB7;Improve Security: They prevent potentially harmful software from being installed, which could introduce vulnerabilities or compromise system integrity.
	
	
		Reduce Administrative Overhead: Automated enforcement of installation policies eases the burden on IT staff, freeing them from manual oversight of software deployments.
	
	
		Customization and Flexibility: Rules can be customized to meet the specific needs of different departments or user groups within the organization.
	



	In summary, Windows Installer Rules are integral to the WEM security infrastructure. They deliver comprehensive management and control over the software installation process, enhancing the organization's overall security posture.
 


	 Script Rules



	Script Rules in WEM serve a crucial role in enhancing security by governing the execution of scripts within the IT environment. They act as gatekeepers, determining which scripts are permitted to run, thereby preventing the execution of unauthorized or malicious scripts that could pose a security threat. The ability to manage script execution is essential because scripts are often used to automate tasks but can also be exploited to carry out harmful actions without user interaction.
 


	The Script Rules are defined based on the same criteria as all the application security rules: Path, Publisher, and Hash. These rules can apply to all users or be assigned to specific users or groups, allowing certain privileged users to override them.
 


	&#xB7;    Path: These rules allow or prevent scripts from running based on their file path location. If a script is not located in a designated safe directory, it won't execute.
 


	&#xB7;    Publisher: This type focuses on the script's publisher information. Only scripts matching the publisher info (Publisher, Product name, file name, and version) are permitted to run, ensuring their authenticity and integrity.
 


	&#xB7;    Hash: Hash rules are defined by the unique hash value of a script file. Since each file has a distinctive hash, this method ensures that only specific, verified scripts are executed.
 


	
 


	 Script Rules enhance security by:
 


	
		Preventing Unauthorized Scripts: Script rules prevent malware and unauthorized changes to system configurations by blocking the execution of unauthorized or potentially harmful scripts.
	
	
		Ensuring Compliance: Script rules help enforce policy compliance, ensuring only approved scripts run in the environment. This is essential for meeting various regulatory and compliance requirements.
	
	
		Limiting Exploits: Many cyber-attacks use scripts to exploit vulnerabilities. Script Rules can mitigate such threats by controlling which scripts have execution rights.
	
	
		Reducing Surface Attack: Script Rules reduce the attack surface by ensuring that only necessary and safe scripts are allowed to run, thereby minimizing the vectors through which attacks can occur.
	



	In summary, Script Rules are a crucial part of the application security framework in WEM, as they provide administrators with a robust set of tools to manage script execution, thus significantly enhancing the security of applications and the overall IT environment.
 


	Through these rules, WEM empowers administrators to have granular control over script execution, contributing to the security and operational efficiency of the organization's IT infrastructure. Organizations can significantly mitigate the risk of script-related security threats by carefully crafting and enforcing Script Rules.
 


	By setting up script rules, WEM provides a robust framework to secure the IT environment against script-based threats, enforce policy compliance, and enable secure automation of administrative tasks.
 


	 Packaged Rules



	Packaged Rules within WEM are designed to manage and secure the execution of packaged applications, such as those delivered in Microsoft's App-V format or similar encapsulation technologies.
 


	These rules dictate which packaged applications execute based on criteria like a publisher, package name, and file version. By defining such parameters, WEM ensures that only trusted packaged applications can run, aligning with the organization's security protocols and compliance standards.
 


	Once the packaged applications have been identified within the organization, identify those that should be allowed or denied based on business requirements. Create the rules to allow or deny based on the identifiers above. The rules can then be applied universally or tailored to specific groups or users, providing application access and usage flexibility.
 


	In summary, Packaged Rules are critical to WEM application security management. By strategically implementing these rules, organizations can ensure that their packaged applications are delivered efficiently and securely, maintaining operational effectiveness and a strong security posture.
 


	 Process Management



	The process management feature in WEM is designed to monitor and control the applications and processes that users can launch from Explorer. This management ensures system stability and security by preventing unnecessary or malicious processes from consuming resources or executing harmful actions.
 


	Process Blacklist
 


	Process management includes a process blacklist feature, a crucial security tool that blocks unauthorized or harmful executables from running in the system. This part of WEM&#x2019;s security suite allows administrators to identify and catalog disallowed processes&#x2014;typically those known to be malicious or unnecessary - into a blacklist. WEM enforces this policy throughout the user environment, automatically preventing the initiation of any blacklisted process, thereby offering real-time protection. Overall, the process blacklist is instrumental in ensuring system stability and security across varied applications and user interactions.
 


	
 


	 
 


	 Privilege Elevation



	Privilege Elevation is designed to selectively increase user permissions for specific tasks or applications without granting broad administrative rights. This feature enhances security by providing users with the necessary privileges to perform their job functions while minimizing the risk of unauthorized system changes or potential security breaches that could arise from wider administrative rights.
 


	 
 


	 Execution Rules
 


	Execution Rules define the conditions under which applications and scripts can run with elevated privileges. These rules can be crafted to allow certain trusted applications to execute with higher permissions, necessary for updates or specific functionalities that require administrative rights, without elevating the user&#x2019;s overall access level.
 


	Privilege Elevation allows administrators to grant users permanent or scheduled elevated rights to execute specific tasks or applications. This controlled elevation is crucial for maintaining tight security protocols while enabling users to perform functions requiring higher privileges than their standard user accounts. Elevation can be precisely configured with Execution Rules that dictate the conditions under which applications can run with elevated privileges.
 


	 
 


	
 


	 
 


	Execution Rules can include settings that detail the criteria for elevation, such as path location, publisher, or the specific hash of the executable. Administrators can also define elevation time windows, specifying when the elevated rights are active, to control further and restrict the use of elevated privileges to only necessary periods.
 


	Additionally, these rules can extend to child processes spawned by an elevated application, providing granular control over the extent of privileges granted. By implementing Execution Rules, WEM allows necessary applications to function with elevated rights safely and only under conditions that align with organizational security policies. This ensures users can perform essential duties while the system's overall security posture remains uncompromised.
 


	Windows Installer Rules
 


	Windows Installer Rules govern the installation and updating of software by managing the privileges required for these processes. The importance of these rules lies in their ability to ensure that only authorized installations or updates take place, thus safeguarding against unauthorized changes to the system, potential security vulnerabilities, and compliance issues. By controlling installer privileges, these rules also help maintain system stability and prevent the installation of unlicensed or non-compliant software.
 


	These rules are crucial for maintaining a secure environment. They ensure that only installations from trusted sources or with the appropriate privileges can proceed. They also prevent users from installing unauthorized or potentially harmful software, which is essential for complying with security policies and regulatory standards.
 


	Administrators can define elevation time windows, limiting the time frame during which installations with elevated privileges are permitted. This prevents users from having persistent administrative rights, reducing the risk of security breaches.
 


	Additionally, rules can extend to child processes spawned by the installer, ensuring that the installation procedure, including subsidiary actions, adheres to the organization&#x2019;s security protocols. This level of granular control over both parent installers and their child processes ensures that all aspects of software installation are under scrutiny and control, reinforcing the system's security and enhancing compliance posture.
 


	 
 


	Self-elevation
 


	Self-elevation allows users to temporarily elevate their permissions to perform specific tasks requiring administrative rights. This capability provides significant security advantages, as it eliminates the need to grant users permanent administrative privileges, which could expose the system to security risks. By offering controlled elevation, organizations can maintain a principle of least privilege, reducing the attack surface and potential for accidental or malicious system changes.
 


	Self-elevation is configured via the administration console, where administrators establish policies that determine how users can elevate their permissions to execute tasks or applications that require admin rights. These policies allow for the specification of individual applications that users can run with elevated privileges and can include time-based restrictions, confining how long these permissions are active to reduce risk.
 


	It can also be restricted to specific applications and applied to certain users or groups.
 


	 



	 



	Conclusion



	In summary, Citrix Workspace Environment Management offers robust security features for protecting and managing the modern digital workspace. Execution Rules ensure that only authorized applications and scripts run, while Windows Installer Rules and Packaged App Rules enforce compliance and prevent unauthorized software installations. Privilege Elevation and Self-Elevation features provide users with the necessary administrative capabilities when required without compromising overall security.
 


	The security functionalities within WEM are not just about defending against external threats but also enabling businesses to operate more efficiently and securely. By implementing a principle of least privilege and automating privilege management, WEM ensures that users have the necessary access without exposing the organization's systems to unnecessary risks.
 


	The role of security within WEM is foundational. It supports a strategic approach to workspace management by integrating comprehensive security measures that are both proactive and reactive. These measures are vital in building a trusted environment where both productivity and protection are optimized, demonstrating that WEM is a powerful ally in the ongoing effort to balance functionality with security in an ever-evolving threat landscape.
 


	 
 


	References
 


	For a more in-depth understanding of the security features within WEM and their implementation, the following resources are invaluable:
 


	
		Citrix Tech Zone offers many resources, including deep-dive articles, expert-led discussions, and community forums for real-world insights and troubleshooting.
	
	
		Citrix blogs where you can find updates on the latest features, security tips, and thought leadership articles on workspace security.
	
	
		Citrix Tech Insight where you can watch how WEM improves the security posture and reduces the threat surface for your users within your Citrix DaaS and CVAD deployments.
	
	
		Citrix Online Training and webinars offered by Citrix can be instrumental for both new and experienced administrators looking to enhance their skills in WEM security management.</description><pubDate>Wed, 24 Apr 2024 16:35:44 +0000</pubDate></item><item><title>POC Guide: Global App Configuration service</title><link>https://community.stage.citrix.com/tech-zone/learn/poc-guides/gacs/</link><description><![CDATA[Overview



	The Citrix Global App Configuration Service allows Citrix administrators to centrally manage end users' Citrix client experience. Over 360 settings help streamline experiences like updates and plugins, Citrix App Protection, Citrix Enterprise Browser, HDX experiences, and more on a per-operating-system level.  The Citrix Global Apps and Configuration service is available for cloud,  hybrid, or on-premises Citrix deployments. This POC guide provides high-level instructions for using the Global App Configuration service and provides details on how to use the service to manage:
 


	
		Citrix Workspace app versions
	
	
		Citrix Secure Access agent
	
	
		Citrix Endpoint Analysis plug-in
	
	
		3rd party plug-ins (Zoom VDI plug-in manager)
	
	
		Citrix Workspace app settings without a Citrix or Group Policy
	
	
		Citrix Enterprise Browser
	



	Prerequisites



	
		Citrix Workspace App versions must be at or above the following versions:

		
			
				Windows (Current Release - 2106, LTSR -2203.1)
			
			
				Mac (2203.1)
			
			
				iOS (2104)
			
			
				HTML5 (2111)
			
			
				ChromeOS (2203)
			
			
				Android (2104)
			
		
	
	
		A valid Citrix Cloud account. If you need to create a Citrix Cloud account, visit here.
	
	
		The addresses &lt;https://discovery.cem.cloud.us&gt;, &lt;https://gacs-discovery.cloud.com&gt;, and &lt;https://gacs-config.cloud.com&gt; must be contactable.
	
	
		This guide features the configuration and installation of the Zoom VDI plug-in; therefore, the prerequisites found here must also be completed.
	
	
		For on-premises StoreFront deployments, the StoreFront URL must be claimed before you can configure settings. Follow the instructions here to claim the URL.
	



	Manage Citrix Workspace app



	1. Sign in to Citrix Cloud.
 


	
 


	2. Once signed in, click the top left hamburger menu and click Workspace Configuration.
 


	
 


	3. Click App Configuration.
 


	
 


	4. Select your store and click Configure.
 


	
 


	5. Change the Workspace URL channel from Production to Test Channel.
 


	
 


	6. Select Mac and Windows, and click Updates and Plug-ins.
 


	
 


	7. Expand Citrix Workspace App Version, select Windows, and click Edit.
 


	
 


	8. Uncheck. Use default settings, select Update type Long Term Service Release (LTSR), and select Fast for Delay group. Click Save Draft.
 


	
 


	
		Note:
	 

	
		Current Release (CR) and Long-Term Service Release (LTSR) releases are supported. The last three versions are available when using the CR release.
	 



	9. Click Yes on the Save Settings pop-up screen.
 


	
 


	10. Select Mac, then click Edit.
 


	
 


	11. Uncheck. Use default settings, select Update type Current Release, and select Fast for Delay group. Click Save Draft.
 


	
 


	12. Click Yes on the Save Settings pop-up screen.
 


	
 


	13. Click Publish Drafts.
 


	
 


	14. Click Save on the Save settings pop-up screen.
 


	
 


	15. The Citrix Workspace app version is now configured via the Global App Configuration service.
 


	
 


	
		Note:
	 

	
		If enabling the VDA auto-update feature in the Citrix Global App Configuration service, you must update the VDA registry manually. Please see this documentation for more information.
	 



	Manage Plug-ins



	In our deployment, the Citrix Secure Access agent allows end users to access private apps without a traditional VPN. We will configure the Citrix Global App Configuration service to install and automatically update the agent to the latest version.
 


	1. Expand Secure Access Plug-in in Updates and Plug-ins.
 


	
 


	2. Select Windows and click Edit.
 


	
 


	3. Uncheck. Use default settings, select Install and update, and check Install the plug-in silently after the end user adds the store. Click Save Draft.
 


	
 


	
		Note:
	 

	
		Learn more about the deployment mode settings used in this step here.
	 



	4. Expand Zoom VDI Plug-In Management.
 


	
 


	5. Select Windows and click Edit.
 


	
 


	6. Uncheck. Use default settings, select Install and update, and check Install the plug-in before the end user logs in. Click Save Draft.
 


	
 


	
		Note:
	 

	
		This option will work only at the time of store addition. It won't work when the session is timed out, and a new login is made.
	 



	7. Click Yes on the Save Settings pop-up.
 


	
 


	8. Select Mac and Click Edit.
 


	
 


	9. Uncheck. Use default settings, select Install and update, and check Install the plug-in before the end user logs in. Click Save Draft.
 


	
 


	10. Click Yes on the Save Settings pop-up.
 


	
 


	11. Click Publish Drafts.
 


	
 


	12. Click Save on the Save settings pop-up.
 


	
 


	The Citrix Secure Access agent and Zoom VDI Plug-in Manager are now configured to install and update with the Citrix Global App Configuration service.
 


	Manage Citrix Workspace app Settings



	With over 360 configuration settings available within the Global App Configuration service, Citrix administrators can reduce the overhead of managing experiences separately for different users and devices across the enterprise. For our deployment, we will enable several App Experience, Security, and Session Experience settings using the Global App Configuration service, which must typically be configured by Citrix or Group Policy.
 


	1. Select Mac and Windows from the App Configuration &gt; URL Configuration page.
 


	
 


	2. Expand Security and Authentication and select App Protection.
 


	
 


	3. Expand Anti Key Logging, select Mac and Windows, and set it to Enable.
 


	
 


	4. Expand Anti Screen Capture, select Mac and Windows, and set to Enable.
 


	
 


	
		Note:
	 

	
		Starting with the Citrix Workspace app for Windows 2302 or Windows 2301 versions, you can configure App Protection for authentication screens and self-service plug-ins using the Global App Configuration service. Additionally, configurations do not apply for Virtual Apps and Desktops and web and SaaS apps. The Delivery Controller and Citrix Secure Private Access continue to control these resources.
	 



	5. Select Authentication.
 


	
 


	6. Expand Secure Access Auto Login, select Mac and Windows, and switch to Enabled. (This setting allows the Citrix Workspace app user to single-sign onto the Citrix Secure Access client using the store configured on the Citrix Workspace app.) 
 


	
 


	7. Expand Session Experience and select Clipboard. Expand the Clipboard Redirection setting, choose Windows, and switch to Enabled.
 


	
 


	8. Click Publish Drafts.
 


	
 


	9. Click Save on the Save settings pop-up screen.
 


	
 


	10. Your settings are now saved.
 


	11. Click View configured changes to review the changes made.
 


	
 


	12. Select the Platform drop-down and choose Windows. Click Apply.
 


	
 


	13. Scroll to Citrix Workspace app version and click View changes in full.
 


	
 


	14. Review the changes and click Close.
 


	
 


	15. Review the other changes made by selecting View changes in full for each.
 


	Review your current Citrix and Group policies to see which Citrix Workspace app configuration settings can be moved into the Citrix Global App Configuration service.
 


	Manage Citrix Enterprise Browser



	The Citrix Global App Configuration service can configure Citrix Enterprise Browser settings, allowing Citrix administrators to set various settings or system policies for the Citrix Enterprise Browser. For our deployment, we will set the Citrix Enterprise Browser as the default browser for all web and SaaS apps, remove the ability to save browser history and change how we handle default cookies.
 


	1. On the App configuration &gt; URL Configuration page, select Mac and Windows, then Enterprise Browser.
 


	
 


	2. Scroll to and expand Open All SaaS Apps Through Citrix Enterprise Browser, select Mac and Windows, and switch each to Enabled.
 


	
 


	3. Expand Saving Browser History Disabled, select Mac and Windows, and switch each to Enabled.
 


	
 


	4. Scroll to and expand Default Cookies, select Mac and Windows, and choose Keep cookies for the session duration from the dropdown.
 


	
 


	5. Click Publish Drafts.
 


	
 


	6. Click Save on the Save settings pop-up.
 


	
 


	Your Citrix Enterprise Browser settings are now configured.
 


	Summary
 


	This guide provides high-level instructions for Citrix administrators to test the Citrix Global App Configuration service in a proof-of-concept environment. You learned how to configure the service, deploy and update the Citrix Workspace app, Secure Access agent, and Zoom VDI plug-in Manager, and configure several of the over 360 Citrix Workspace app settings available to be managed through the service. To learn more about the Citrix Global App Configuration service, visit the following:
 


	Citrix Global App Configuration service Product Documentation
 


	Citrix Global App Configuration service Tech Brief
 


	Citrix Global App Configuration service Settings and Behaviors FAQ]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_04/image.png.b134cdfb4a427ca5cfddf8ac7cbd6794.png" length="53176" type="image/png"/><pubDate>Mon, 22 Apr 2024 14:47:00 +0000</pubDate></item><item><title>Deployment Guide: Citrix VDA for macOS</title><link>https://community.stage.citrix.com/tech-zone/build/deployment-guides/citrix-mac-vda/</link><description>OverviewThe Citrix Virtual Delivery Agent for macOS (Citrix VDA for macOS) enables HDX access to macOS Remote desktop from any device with the Citrix Workspace App installed. The Citrix VDA for macOS is designed and engineered as &#x201C;yet another VDA&#x201D; backed by Citrix's leading HDX technologies within the DaaS/CVAD product family. It adheres to the existing Citrix product architecture. It follows all the common roadmap of HDX features and all interfaces defined between critical components in DaaS/CVAD to ensure that our customers' knowledge and experience can be fully reused in this new VDA. This deployment guide provides the steps to Install and configure the Citrix VDA for macOS using NDJ (non-Active Directory domain joined) technology to connect the macOS desktop to your management plane (DaaS or CVAD). The Mac device itself could be either domain-bonded or not (for the case of support SSO feature, AD binding is one supported scenario - refer to the product documentation for more details). The following steps are covered in the guide: Prepare the installation of the non-domain joined VDA. Install the Citrix VDA for macOS. Create the Delivery Group Connect via Citrix Gateway Service to the Citrix VDA for macOS. For information regarding using MDM/UEM solutions such as Jamf PRO to deploy Mac VDA please refer to related article in the product documentation Installation section; If the deployment will be in your infrastructure provider, please refer to our MacStadium deployment guide and AWS EC2 Instances guide separately. Remote Access OptionsThe Citrix VDA for macOS supports access from Citrix DaaS and Citrix Virtual Apps and Desktops v2407 or later. This supports multiple access options to macOS VDAs through Citrix Gateway Service with Citrix Workspace or via your on-premises NetScaler Gateway and StoreFront deployment. Access via Citrix Cloud Gateway Service and Citrix Workspace Access via Citrix Cloud with On-Premises NetScaler Gateway and Citrix StoreFront Access via On-Premises NetScaler Gateway and Citrix StoreFront PrerequisitesAny Apple Silicon (M1, M2, and M3 families) based macOS device. macOS 13 (Venture), 14 (Sonoma), 15 (Sequoia, beta and formal release) The following network requirements are configured: https://docs.citrix.com/en-us/mac-vda/system-requirements#network-requirements Citrix DaaS or CVAD subscription Citrix Workspace app 2402 or later (Windows, Linux, Mac, ipadOS, iOS, Android, HTML5, Chrome OS) Citrix VDA for macOS is downloaded to your macOS device from Citrix Downloads.  Prepare the Installation Note: The steps for preparation and installation are quite similar if you&#x2019;re deploying with Citrix Virtual Apps and Desktops; please refer to the Installation section in the product documentation for details. 1. Login to Citrix DaaS, open Web Studio, and select Machine Catalogs.    2. Click Create Machine Catalog.  3. Select Remote PC Access and click Next. (The Single-session OS option is also supported.)    4. Select "I want users to connect to the same (static) desktop each time they log on" and click Next.    5. Select the minimum functional level for this catalog and click Next on the Machine Accounts page.    6. Click Next on the Scopes page.    7. Click Next on the Workspace Environment Management (Optional) page.    8. Leave the "Enable VDA upgrade" unchecked and click Next.    9. Name your Machine Catalog and click Finish.    10. The Machine Catalog is now created.  11. Right-click on the Citrix VDA for macOS Machine Catalog and select Manage Enrollment Tokens.  12. Click Generate.  13. Enter your Token name, select Use current date and time for start, enter 100 into the Specify how many times the token can register VDAs, choose an appropriate end date for the token to allow VDA registrations, and click Generate.  14. Click Copy.  15. Optionally, click Download to download this token for later usage.  16. You will now see your active Enrollment token. Click Close.  Install the VDA1. On your macOS device, download .Net 8.0 from https://dotnet.microsoft.com/en-us/download/dotnet/8.0. 2. Select .NET Runtime v8.0.8 and choose the macOS Arm64 download link.  3. Install the Arm64 .Net Runtime package for macOS and check the installation directory path using the command:  which dotnet  4. To begin the VDA installation, select the Citrix VDA for macOS installer and double-click it.  5. Click Continue.  6. Click Continue on the License Agreement.  7. Click Agree.  8. Select Install for all users of this computer and click Continue.  9. Click Install.  10. Enter the administrator password and click Install Software if prompted.  11. The Citrix VDA for macOS installs.    12. Choose the required option on the vdaconfig UI and click Open Screen Recording Preference to enable Citrix Graphics Service. Then click Open System Settings.  13. Enable Citrix Graphics Service and close the window.  14. Click Open Accessibility Preferences.  15. Enabled the Citrix Input Service.  16. Validate that .NET 8.0 is installed correctly.  17. Copy the Enrollment Token from Notepad, paste it into Enroll with Token, then click Enroll.  18. Enter the administrator password if prompted and click OK.  19. Once your enrollment is successful, you will receive the enrolled successfully message as seen here:  20. Click Close.  Your Citrix VDA for macOS has been installed.  21. Return to Citrix DaaS Web Studio console and verify that the macOS device is registered with the Machine Catalog.  Create Delivery Group1. Within Citrix DaaS Web Studio, select Delivery Groups, then click Create Delivery Group.  2. Select your Citrix VDA for macOS Machine Catalog and click Next.  3. Select which users can access the Delivery Group and click Next.  4. Add the required Desktop Assignment Rule and click Next.  5. Select Next on the App Protection screen.  6. Click Next on the Scopes window.  7. Select the appropriate License Assignment for your Citrix DaaS deployment and click Next.  8. Click Next on the Policy Set window.  9. Provide a name for your Delivery Group and click Finish.  10. Your Delivery Group is now created and ready for user access.  Enable RendezvousOur deployment will contain non-domain-joined macOS devices. The Rendezvous v2 protocol is used in this scenario, so no Cloud Connectors are required. However, we must enable the Rendezvous Citrix policy for our deployment to work. 1. Within Citrix Web Studio, select Policies and then click Create Policy.  2. Select ICA within View by Category, then scroll to and select Rendezvous Protocol. Click Allow.  3. Click Next.  4. Select Filtered users and computers and expand the Delivery Group.  5. Select Allow in the Mode drop-down menu, and then choose your Citrix VDA for macOS Delivery Group, select Enable, and click Save.   6. review your filters, then click Next.  7. Select Enable policy, provide a Policy name, then click Finish.  8. The Rendezvous policy is now active and enabled.  Launch the macOS VDA1. Open your Citrix Workspace app or browser, enter your Citrix Workspace URL, and log in. Your Citrix VDA for macOS desktop will be available for launch.  2. Launch the Desktop.  3. Your remote session via Citrix HDX to your macOS device will begin. SummaryThis guide walked you through the installation and configuration of the Citrix VDA for macOS. This consisted of preparing the Citrix DaaS environment by creating a Machine Catalog and capturing an Enrollment Token, installing and registering the Citrix VDA on macOS device, and creating a Delivery Group with user assignments. As a reminder, the Citrix VDA for macOS is currently in Public Tech Preview. This deployment guide will be updated during the preview period and when the feature goes GA. For more information, please visit the Citrix VDA for macOS product documentation.</description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_09/image.png.af5bb00e9f819242f59f5446c8c0e7b5.png" length="104261" type="image/png"/><pubDate>Fri, 19 Apr 2024 12:03:00 +0000</pubDate></item><item><title>Deployment Guide: Citrix Profile Management - OneDrive Container</title><link>https://community.stage.citrix.com/tech-zone/build/deployment-guides/cpm-one-drive-container/</link><description>Overview



	This document is intended for Citrix technical professionals, IT decision-makers, partners, and consultants who want to deploy Microsoft OneDrive Citrix Virtual Apps environment.  The content is relevant for both on-premises and public cloud architectures. The reader should understand the Citrix app, desktop virtualization offerings, and Microsoft OneDrive.
 


	The document provides best practices for deploying Microsoft OneDrive in a Citrix Virtual Apps environment. The goal is to overcome the challenges of delivering OneDrive in a Citrix environment and provide an optimal user experience.
 


	 Installing OneDrive in a Citrix virtual application environment, especially for multi-user, non-persistent scenarios, requires careful planning and configuration to ensure seamless user experiences.
 


	 There are several challenges when deploying OneDrive Citrix Virtual Apps environment, including:
 


	
		Users who roam between multiple endpoints require their applications, files, and data to roam with them.
	
	
		Users with multiple applications open from different sessions on hosts require access to the same storage repository.
	
	
		Environments that have unique applications that store data in specific mapped drives, often without user intervention.
	
	
		Microsoft OneDrive syncs data from the cloud upon user login, which causes challenges in a non-persistent environment where data is deleted after user logoff and can consume a significant amount of storage in the data center where the user&#x2019;s sessions are hosted.
	



	Solving these challenges requires careful planning when deploying Microsoft OneDrive. This deployment guide provides the reader with the recommended installation, configuration, and optimizations to resolve the challenges of deploying OneDrive in a Citrix Virtual Apps environment with the Citrix Profile Management OneDrive Container.
 


	 Conceptual Architecture
 


	
 


	Citrix Profile Management is a profile solution for Citrix Virtual Apps servers installed on each computer where user profiles must be managed. Citrix Profile Management addresses user profile deficiencies in environments where simultaneous domain logins by the same user introduce complexities and consistency issues to the profile and optimizes profiles efficiently and reliably. Citrix Profile Management is a crucial component of a well-optimized Citrix Virtual Apps environment. Please review the Citrix Profile Management Quick Start Guide for additional information on deploying the solution. Refer to Profile Management architecture for more details on the folder structure of the user store and the central location for Citrix user profiles.
 


	Profile Container



	
 


	The Citrix Profile Management Profile Container is a VHDX-based profile solution that allows you to store the profile folders of your choice or the entire user profile on a VHDX profile disk. A VHDX file is created per user on your profile storage share and mounted to the VDA session(s) when the users log on, resolving any issues with slow logons and improving the logon experience. Once the users have logged into their virtual application, their profile folders are available immediately.
 


	 
 


	
		 Note: 
	 

	
		Starting with Profile Management 2109, Profile Containers can store the entire user profile in the VHDX profile disk.
	 



	 
 


	Microsoft OneDrive Container



	
 


	The Citrix Profile Management OneDrive container is a VHDX-based folder roaming solution. Profile Management creates a VHDX file per user on a file share and stores the users&#x2019; OneDrive folders into the VHDX files. The VHDX files are attached when users log on and detached when users log off. 
 


	 With Citrix Profile Management OneDrive Containers, end-user OneDrive folders roam with users to allow access to the same OneDrive folders on any computer or virtual session. These containers are VHDX-based and are created per user within a file share. They are then mounted to the virtual session when users log on and detached when users log off. The VHDX files for the OneDrive container are stored on the same storage server as the Citrix Profile Management user store.
 


	 The VHDX files for Citrix Profile Management, such as the OneDrive container and profile container, can be stored in different locations in a hybrid solution with a container plus a file-based profile.
 


	 Roaming and simultaneous access from multiple sessions



	 In many cases, end users roam between multiple endpoints in these settings, requiring their applications, files, and data to roam with them. This is seen a lot in healthcare settings that deliver Citrix Virtual Applications. Additionally, these users may have multiple applications open from different sessions on different hosts, all requiring access to the same storage repository.
 


	 
 


	
 


	 
 


	Citrix Profile Management is designed to resolve the roaming and multiple-session scenario. The above diagram depicts a scenario where the user has launched multiple virtual applications from multiple Citrix Virtual Delivery Agents (VDAs). The user then logs out of two applications and moves to a second device, where they log onto the open application session. Once they log off from the remaining application setting, Citrix Profile Management writes back on the specific settings that were changed during the session while letting other unchanged settings remain untouched.
 


	OneDrive Container for roaming and multiple sessions
 


	Introducing OneDrive into an environment where roaming or multiple-session Citrix Virtual Apps environments are common also brings many challenges. Challenges include the OneDrive sync app needing to be supported when using file-based profile roaming and previously requiring FSLogix to be supported. However, Citrix Profile Management v2311 and the Citrix OneDrive Container resolve these challenges and allow users to roam and open multiple virtual application sessions when using OneDrive.
 


	 
 


	
		Note: 
	 

	
		Many organizations require LTSR infrastructure and Virtual Delivery Agents in their environments. If so, Citrix Virtual Apps and Desktops LTSR v2402 includes the Citrix Profile Management OneDrive Container. Alternatively, customers could remain on LTSR v2203 for VDAs and use newer Citrix Profile Management versions, including the OneDrive Container.
	 



	OneDrive Install Recommendations



	Installing OneDrive within a Citrix Virtual Apps environment takes careful consideration. The typical OneDrive installation is installed into each user's profile, which in a Citrix Virtual Apps non-persistent environment will cause issues. OneDrive provides a per-machine install option for this type of environment, which installs OneDrive so that each profile logged in will use the same OneDrive.exe binary. This is recommended when installing OneDrive into a Citrix Virtual Apps non-persistent environment. The following recommendations should be considered when deploying OneDrive in a Citrix Virtual Apps environment.
 


	
		Ensure that the following prerequisites and requirements for OneDrive and Citrix are met.
	



	a.       Citrix Virtual Apps and Desktops 2311 or later, or Citrix DaaS for the Citrix management plane.
 


	b.       Windows Server 2019 and above for the VDA operating system.
 


	c.       Citrix Profile Management 2311 or later
 


	e.       SMB File share for the VHDX containers
 


	Shellbridge is enabled by default in Citrix VDA 2212 and later versions. If using Citrix VDA 2203, Shellbridge must be enabled manually by adding the following Registry key:
 


	'HKEY_LOCAL_MACHINE\SOFTWARE\Citrix\Citrix Virtual Desktop Agent`
 


	'Name: Shellbridge`
 


	`Type: REG_DWORD`
 


	`Value: 1`
 


	
		Install OneDrive once at the machine level, making it available to all users who access the virtual environment. This method can save storage space but may result in a less personalized experience. For more information on deploying OneDrive per machine level, refer to Microsoft&#x2019;s OneDrive per-machine installation.
	
	
		OneDrive must be added to the following registry location: HKLM\software\Microsoft\Windows\CurrentVersion.  The following command will create the key required for the per-machine installation to run correctly.
	



	`REG ADD HKLM\Software\Microsoft\Windows\CurrentVersion\Run /v OneDrive /t REG_SZ /d "\"C:\Program Files\Microsoft OneDrive\OneDrive.exe\" /background"`
 


	
		Install the OneDrive sync client on the base image of your virtual machines or in the shared application layer.

		
			
				Configure OneDrive settings to match your deployment strategy (shared).
			
		
	
	
		Create a Group Policy Object (GPO) settings for the following:
		
			
				OneDrive Files On-Demand: This setting prevents files from being downloaded into a user's local cache until the user accesses them.
			
			
				Allow Storage Sense: This setting will remove local copies of files that have not been accessed for a defined period and help control the size of the OneDrive cache.
			
		
	



	Citrix Profile Management Recommended Configuration with OneDrive



	Integrating Citrix Profile Management with OneDrive requires careful planning and consideration, as several configurations are necessary to ensure optimal performance and user experience. With the Citrix Profile Management OneDrive container enabled in CPM 2311, the OneDrive container can be accessed via concurrent sessions by default. Citrix Profile Management file-based users must allow the OneDrive container to roam the OneDrive data. This supports simultaneous access to OneDrive. OneDrive data can be roamed for full container users with the profile container, but it does not support concurrent access to the OneDrive data. Full container users must specifically enable OneDrive container for OneDrive data concurrent access.
 


	
		Enable Citrix Profile Management.
	



	 
	 
 


	
		Enable and set the path to the Profile Management User Store.
	



	  
	
 


	
		When using Citrix Profile Management and OneDrive, administrators need to Enable the OneDrive Container so that Citrix Profile Management creates a VHDX file per user on a file share and stores the users&#x2019; OneDrive folders into the VHDX files. The VHDX files are attached when users log on and detached when users log off.
	



	 
 


	
		Enable the OneDrive container&#x2014;list of OneDrive folders policy and add your OneDrive folders as a path relative to the user profile, then click Save.
	



	
 


	
		It is recommended that VHDX disks be automatically reattached in sessions be enabled when deploying containers in the Citrix Profile Management solution (profile container, OneDrive container, Outlook container).
	



	
 


	
		Once the Citrix Profile Management OneDrive container is enabled and users are signed into OneDrive, no other sign-in to OneDrive is required if users sign in to a new workstation and launch a new one or connect to a disconnected session. Any files stored In OneDrive will also be available to them immediately as they are not downloaded from the cloud but rather synced between the virtual app server they are connected to and the local area network (LAN). Lastly, offline access is still available for kiosks that do not have internet access to their files.
	



	Storage, Scale, and Sync Considerations



	Each OneDrive user is granted 1 TB of storage space for their personal library. Synchronizing the user&#x2019;s entire library across multiple devices consumes significant storage. Citrix Profile Management VHDX containers are created in the user store per user, ensuring a single copy of the OneDrive VHDX or profile container VHDX, regardless of the number of user sessions. The VHDX base disk and the difference disk are in the user store. No matter how large a user's OneDrive folder is, it won't consume the OS disk space in an MCS scenario or the write cache disk space in a PVS scenario. Additionally, the storage consumed on the VDAs is minimal as the OneDrive VHDX containers are remote-mounted, preventing the entire user library from copying across the network during the login process.
 


	
		 If Citrix Profile Containers are used, the default storage size of the VHDX file is 50GB per user. However, if required, you can use the Default capacity of the VHD containers policy to set a smaller default for profile size.
	



	 
 


	
		Citrix Profile container VHDX files can auto-compact upon user logoff to save space for central or cloud storage locations.  Certain conditions must be met for the compaction to take effect.
	



	  
 


	
		Lastly, to help with scaling the environment, you can limit the ability for OneDrive to sync only when files are required by enabling the Use OneDrive Files On-Demand Group Policy setting. This setting allows OneDrive to download files when they are needed. Users accessing published applications throughout the day typically only require access to a few files within their OneDrive container. Enabling this setting ensures that the OneDrive sync client does not sync unneeded files to the container.
	



	 
 


	Tips and Optimizations
 


	Optimizing the environment to make the OneDrive user experience consistent with a physical desktop is essential to healthcare customers. Several settings can be configured to adjust OneDrive settings to improve performance and reduce network traffic.
 


	
		Disable the OneDrive Update service on the master image. The OneDrive Update service keeps the application updated in the per-machine installation of OneDrive. It is recommended that you disable this service in non-persistent image workloads.
	
	
		Disable the OneDrive per machine Standalone Update and Reporting scheduled tasks. The Standalone Update scheduled task updates the OneDrive application service. The Reporting scheduled task audits every file OneDrive and provides up-to-date reports on all user file activity.
	
	
		Limit selective sync to essential folders. Syncing only essential OneDrive folders in the environment helps optimize performance and enhances the end-user experience in the Citrix environment.
	
	
		Setting upload and download limits. Setting limits to uploads and downloads for OneDrive can assist in avoiding overloading the virtual infrastructure.
	
	
		Enable VHD disk compaction. The Citrix Profile Management setting automatically compacts the VHDX file on user logoff when the file exceeds a specified value or the number of logoffs reaches a specified value.
	
	
		Replicate profile containers. Replicating user profile containers provides profile redundancy for user logins but not for in-session failovers. However, replicating the containers increases system I/O and may prolong logoffs.
	
	
		Enable Automatically reattach VHDX disks in sessions. This policy enables a high level of stability if a session failover occurs. The connection to the profile container is re-established to the profile store, and the VHDX is automatically re-attached.
	
	
		By default, with Local Caching, the entire profile is cached locally during log-in. To Reduce Login times, enable Profile Streaming, which caches profile folders on demand after login.
	
	
		Exclusive Access. VHD containers allow concurrent access by default. If needed, you can disable concurrent access for the profile and OneDrive.
	



	Summary



	Deploying Microsoft OneDrive in a Citrix Virtual Apps environment to be used within healthcare settings comes with many challenges. Having the right strategy and careful planning and execution will allow healthcare organizations to deploy OneDrive successfully within a Citrix Virtual Apps environment and overcome the difficulties of roaming users, multiple sessions, and cloud synchronization of files. Optimizing the environment will provide a better overall end-user experience within the environment and applications.
 


	 References



	 Citrix Profile Containers Deployment Guide</description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_04/image.png.8a321ec3c92fb6214644d69f4954d776.png" length="85864" type="image/png"/><pubDate>Mon, 15 Apr 2024 12:07:43 +0000</pubDate></item><item><title>Citrix DaaS and Terraform - Automatic Deployment of a Resource Location on Google Cloud Platform (GCP)</title><link>https://community.stage.citrix.com/tech-zone/automation/terraform-daas-gcp/</link><description><![CDATA[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&gt; 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&gt;

				
			
		
	

	
		 
		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&gt; choco --v
 Chocolatey v2.2.2

 PS C:\TACG&gt;
				
			
		
	
	 

	
		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&gt; 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&gt;

				
			
		
	

	
		 
	

	
		 
		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&gt; terraform -version
 Terraform v1.7.4 on windows_amd64
 
PS C:\TACG&gt;
					
				
			
		

		
			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&gt; 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 "&gt;= 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&gt;
					
				
			
		

		
			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 = "&gt;= 1.7.4"
											
											 

											
												  required_providers {
											

											
												    citrix = {
											

											
												      source  = "citrix/citrix"
											

											
												      version = "&gt;=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 = "&gt;= 1.7.4"
															
															 

															
																  required_providers {
															

															
																    citrix = {
															

															
																      source  = "citrix/citrix"
															

															
																      version = "&gt;=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 &lt;IP-Address of the computer you want to reach&gt; -Credential &lt;IP-Address of the computer you want to reach&gt;\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 &lt;IP-Address of the computer you want to reach&gt; -Credential &lt;IP-Address of the computer you want to reach&gt;\administrator  

The response should look like:  
[10.156.0.5]: PS C:\Users\Administrator\Documents&gt;
									
								
							
						

						
							 
						 

						
							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     = &lt;&lt;-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!  

#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;
null_resource.CreateTestScriptOnCC1 (remote-exec): C:\Users\Administrator&gt;powershell -File c:/temp/xdinst/DATA/Terraform-Test-WinRM.ps1

#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;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
									
								
								
									
										&lt;gcp-project-id&gt;.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&gt; 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&gt;
										
									
								
							

							
								 
							 

							
								
									
										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&gt; gcloud compute networks list
NAME     SUBNET_MODE  BGP_ROUTING_MODE  IPV4_RANGE  GATEWAY_IPV4
default  AUTO         REGIONAL
PS C:\TACG&gt;

PS C:\TACG&gt; 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&gt; 

PS C:\TACG&gt; gcloud compute networks list --uri
https://www.googleapis.com/compute/v1/projects/tacg-gcp-XXXXXXXXXX/global/networks/default

PS C:\TACG&gt;

PS C:\TACG&gt; 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&gt;
										
									
								
							

							
								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&gt; 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&gt;

PS C:\TACG&gt; 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&gt;

PS C:\TACG&gt; 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&gt; 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&gt;

											
										
									
								

								
									 
									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&gt; 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&gt;
												
											
										
									

									
										 
									 

									
										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&gt; 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&gt;
											
										
									
								
							

							
								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&gt; 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&gt;
											
										
									
								

								
									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&gt; gcloud compute machine-types list --filter="(CPUS&lt;=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&gt;
														
													
												
											

											
												 
											 

											
												
													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&gt; gcloud compute machine-types list --filter="(CPUS&lt;=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&gt;
														
													
												
											

											
												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&gt; 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&gt;

PS C:\TACG&gt; 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&gt;
											
										
									
								

								
									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 "&lt;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&gt; terraform init

Initializing the backend...

Initializing provider plugins...
- Finding latest version of hashicorp/template...
- Finding hashicorp/google versions matching "&gt;= 5.21.0"...
- Finding mastercard/restapi versions matching "1.18.2"...
- Finding citrix/citrix versions matching "&gt;= 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&gt; 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" = &lt;&lt;-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" = &lt;&lt;-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" = &lt;&lt;-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" = &lt;&lt;-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&gt; 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&gt;
														
													
												
											

											
												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: --&gt; 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: --&gt; 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  = &lt;&lt;-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&gt; 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 "&gt;= 5.21.0"...
- Finding citrix/citrix versions matching "&gt;= 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&gt; 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&gt; 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'...
#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadRequiredComponentsToCC1: Provisioning with 'file'...
#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadRequiredComponentsToCC2: Provisioning with 'file'...
#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadRequiredComponentsToCC1: Provisioning with 'file'...
#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadRequiredComponentsToCC2: Provisioning with 'file'...
#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadRequiredComponentsToCC1: Provisioning with 'file'...
#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadRequiredComponentsToCC2: Provisioning with 'file'...
#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadRequiredComponentsToCC1: Provisioning with 'file'...
null_resource.UploadRequiredComponentsToCC1: Still creating... [10s elapsed]
null_resource.UploadRequiredComponentsToCC2: Still creating... [10s elapsed]
#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadRequiredComponentsToCC2: Provisioning with 'file'...
#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadRequiredComponentsToCC1: Provisioning with 'file'...
#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.UploadRequiredComponentsToCC2: Provisioning with 'file'...
#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;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!
#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;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!
#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;
null_resource.CallRequiredScriptsOnCC1 (remote-exec): C:\Users\Administrator&gt;powershell -File c:/temp/xdinst/DATA/InstallCWCOnCC.ps1
#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;
null_resource.CallRequiredScriptsOnCC2 (remote-exec): C:\Users\Administrator.TACG-GCP-TF-CC2&gt;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]
#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.CallRequiredScriptsOnCC1: Still creating... [1m40s elapsed]
#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;Obj S="progress" RefId="1"&gt;&lt;TNRef RefId="0" /&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.CallRequiredScriptsOnCC1: Creation complete after 1m41s [id=1683817289421392956]
time_sleep.wait_1800_seconds_CC1: Creating...
#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;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!
#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;
null_resource.CallRebootScriptOnCC1 (remote-exec): C:\Users\Administrator&gt;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
#&lt; CLIXML
&lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&gt;&lt;Obj S="progress" RefId="0"&gt;&lt;TN RefId="0"&gt;&lt;T&gt;System.Management.Automation.PSCustomObject&lt;/T&gt;&lt;T&gt;System.Object&lt;/T&gt;&lt;/TN&gt;&lt;MS&gt;&lt;I64 N="SourceId"&gt;1&lt;/I64&gt;&lt;PR N="Record"&gt;&lt;AV&gt;Preparing modules for first use.&lt;/AV&gt;&lt;AI&gt;0&lt;/AI&gt;&lt;Nil /&gt;&lt;PI&gt;-1&lt;/PI&gt;&lt;PC&gt;-1&lt;/PC&gt;&lt;T&gt;Completed&lt;/T&gt;&lt;SR&gt;-1&lt;/SR&gt;&lt;SD&gt; &lt;/SD&gt;&lt;/PR&gt;&lt;/MS&gt;&lt;/Obj&gt;&lt;/Objs&gt;null_resource.CallRebootScriptOnCC2: Still creating... [10s elapsed]

... ** Output shortened ** ... 

Apply complete! Resources: 30 added, 0 changed, 0 destroyed.
PS C:\TACG\_CCOnGCP\_CCOnGCP-Install&gt;
											
										
									
								

								
									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&gt; terraform init

Initializing the backend...

Initializing provider plugins...
- Finding citrix/citrix versions matching "&gt;= 0.5.4"...
- Finding hashicorp/google versions matching "&gt;= 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&gt; 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&gt; 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&gt;
											
										
									
								
							

							
								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
							

							
								
									Disclaimer
								 

								
									EXCEPT WHERE EXPRESSLY PROVIDED OTHERWISE BY CLOUD SOFTWARE GROUP, THE CONTENT IN THIS DOCUMENT IS PROVIDED “AS IS” AND CLOUD SOFTWARE GROUP HEREBY DISCLAIMS ALL EXPRESS OR IMPLIED REPRESENTATIONS, WARRANTIES, GUARANTIES, AND CONDITIONS, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES OR CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD TO BE LEGALLY INVALID. CLOUD SOFTWARE GROUP MAKES NO REPRESENTATIONS, WARRANTIES, GUARANTIES, OR CONDITIONS AS TO THE QUALITY, SUITABILITY, TRUTH, ACCURACY, OR COMPLETENESS OF ANY OF THE CONTENT CONTAINED ON THE WEBSITE.
								 

								
									The above disclaimer applies to any damages, liability, or injuries caused by any failure of performance, error, omission, interruption, deletion, defect, delay in operation or transmission, computer virus, communication line failure, theft or destruction of or unauthorized access to, alteration of, or use, whether for breach of contract, tort, negligence or any other cause of action.
								 
							

							
								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 = "&gt;= 1.7.5"
															
															 

															
																  required_providers {
															

															
																    restapi = {
															

															
																      source  = "Mastercard/restapi"
															

															
																      version = "&gt;=1.18.2"
															

															
																    }
															
															 

															
																    citrix = {
															

															
																      source  = "citrix/citrix"
															

															
																      version = "&gt;=0.5.4"
															

															
																    }
															
															 

															
																    google = {
															

															
																      source = "hashicorp/google"
															

															
																      version = "&gt;=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  = &lt;&lt;-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  = &lt;&lt;-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 = "&gt;= 1.7.5"
																
																 

																
																	  required_providers {
																

																
																	    restapi = {
																

																
																	      source  = "Mastercard/restapi"
																

																
																	      version = "&gt;=1.18.2"
																

																
																	    }
																
																 

																
																	    citrix = {
																

																
																	      source  = "citrix/citrix"
																

																
																	      version = "&gt;=0.5.4"
																

																
																	    }
																
																 

																
																	    google = {
																

																
																	      source = "hashicorp/google"
																

																
																	      version = "&gt;=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  = &lt;&lt;-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  = &lt;&lt;-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  = &lt;&lt;-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  = &lt;&lt;-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  = &lt;&lt;-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  = &lt;&lt;-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  = &lt;&lt;-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  = &lt;&lt;-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  = &lt;&lt;-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 = "&gt;= 1.7.5"
																			
																			 

																			
																				  required_providers {
																			

																			
																				    restapi = {
																			

																			
																				      source  = "Mastercard/restapi"
																			

																			
																				      version = "&gt;=1.18.2"
																			

																			
																				    }
																			
																			 

																			
																				    citrix = {
																			

																			
																				      source  = "citrix/citrix"
																			

																			
																				      version = "&gt;=0.5.4"
																			

																			
																				    }
																			
																			 

																			
																				    google = {
																			

																			
																				      source = "hashicorp/google"
																			

																			
																				      version = "&gt;=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;
}]]></description><enclosure url="https://media.invisioncic.com/r328636/monthly_2024_04/cc-dg-autoscale-ready.png.416a97bb38b127d6809a8d28be38c908.png" length="142665" type="image/png"/><pubDate>Wed, 10 Apr 2024 07:45:00 +0000</pubDate></item></channel></rss>
