Jump to content
Updated Privacy Statement

Citrix DaaS and Terraform - Automatic Deployment of a Resource Location on Microsoft Azure

  • Contributed By: Gerhard Krenn Special Thanks To: Jason Samuel, Avijit Gahtori, Alan Goldman, Aneesh Khandekar, Charlie Lopez

Overview

The aim of this guide is to provide an overview about using Terraform to create a complete Citrix DaaS Resource Location on Microsoft Azure.
At the end of the process you created:

  • A new Resource Location (RL) on Azure
  • A Domain Controller Virtual Machine and a new Active Directory forest for this Resource Location
  • 2 Cloud Controller Virtual Machines registered with the 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 the uploaded Master Image VHD or on an Azure-based Master Image
  • A Delivery Group based on the Machine Catalog with full Autoscale Support

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 in a safe and consistent way 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.

deployment-guides_citrix-daas-terraform-azure-scheme.png

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:

PS C:\TACG> Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString (https://community.chocolatey.org/install.ps1))       

 Forcing web requests to allow TLS v1.2 (Required for requests to Chocolatey.org)
 Getting latest version of the Chocolatey package for download.                                                         
 Not using proxy.    

 Getting Chocolatey from https://community.chocolatey.org/api/v2/package/chocolatey/2.2.2.
 Downloading https://community.chocolatey.org/api/v2/package/chocolatey/2.2.2 to C:\TACG\AppData\Local\Temp\chocolatey\chocoInstall\chocolatey.zip
 Not using proxy.

 Extracting C:\TACG\AppData\Local\Temp\chocolatey\chocoInstall\chocolatey.zip to C:\TACG\AppData\Local\Temp\chocolatey\chocoInstall

Installing Chocolatey on the local machine
 Creating ChocolateyInstall as an environment variable (targeting 'Machine')
   Setting ChocolateyInstall to 'C:\ProgramData\chocolatey'
 WARNING: It's very likely you will need to close and reopen your shell before you can use choco.
 Restricting write permissions to Administrators
 We are setting up the Chocolatey package repository.
 The packages themselves go to 'C:\ProgramData\chocolatey\lib'
   (i.e. C:\ProgramData\chocolatey\lib\yourPackageName).
 A shim file for the command line goes to 'C:\ProgramData\chocolatey\bin' and points to an executable in 'C:\ProgramData\chocolatey\lib\yourPackageName'.

 Creating Chocolatey folders if they do not already exist.

 chocolatey.nupkg file not installed in lib.
  Attempting to locate it from bootstrapper.
 PATH environment variable does not have C:\ProgramData\chocolatey\bin in it. Adding...
 WARNING: Not setting tab completion: Profile file does not exist at
 'C:\TACG\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1'.

 Chocolatey (choco.exe) is now ready.
 You can call choco from anywhere, command line or powershell by typing choco.
 Run choco /? for a list of functions.
 You may need to shut down and restart powershell and/or consoles first prior to using choco.
 Ensuring Chocolatey commands are on the path
 Ensuring chocolatey.nupkg is in the lib folder
 PS C:\TACG>

Run choco --v to check if Chocolatey installed successfully:

 PS C:\TACG> choco --v
 Chocolatey v2.2.2

 PS C:\TACG>
Installation of Terraform

After the successful installation of Chocolatey you can install Terraform by running this command on the PowerShell session:

choco install terraform
PS C:\TACG> choco install terraform
 Chocolatey v2.2.2
 Installing the following packages:
 terraform
 By installing, you accept licenses for the packages.
 Progress: Downloading terraform 1.6.4... 100%

 terraform v1.6.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.6.4/terraform_1.6.4_windows_amd64.zip'
 Progress: 100% - Completed download of C:\TACG\AppData\Local\Temp\chocolatey\terraform\1.6.4\terraform_1.6.4_windows_amd64.zip (24.3 MB).
 Download of terraform_1.6.4_windows_amd64.zip (24.3 MB) completed.
 Hashes match.

Extracting C:\TACG\AppData\Local\Temp\chocolatey\terraform\1.6.4\terraform_1.6.4_windows_amd64.zip to C:\ProgramData\chocolatey\lib\terraform\tools...
 C:\ProgramData\chocolatey\lib\terraform\tools
  ShimGen has successfully created a shim for terraform.exe

  The install of terraform was successful.
  Software installed to 'C:\ProgramData\chocolatey\lib\terraform\tools'

 Chocolatey installed 1/1 packages.
 See the log for details (C:\ProgramData\chocolatey\logs\chocolatey.log).
 PS C:\TACG>

Run terraform -version to check if Terraform installed successfully:

 PS C:\TACG> terraform -version
 Terraform v1.6.4 on windows_amd64

 PS C:\TACG>

The installation of Terraform is now completed.

Terraform - Basics and Commands

Terraform Block

The terraform {} block contains Terraform settings, including the required providers to provision your infrastructure. Terraform installs providers from the Terraform Registry.

Providers

The provider block configures the specified provider. A provider is a plug-in that Terraform uses to create and manage your resources. Providing multiple provider blocks in the Terraform configuration enables managing resources from different providers.

Resources

Resource blocks define the components of the infrastructure - physical, virtual, or logical. These blocks contain arguments to configure the resource. The providers reference lists the required and optional arguments for each resource.

The core Terraform workflow consists of three stages

Write:
You define resources that are deployed, altered, or deleted.

Plan:
Terraform creates an execution plan describing the infrastructure that it creates, updates, or destroys based on the existing infrastructure and your configuration.

Apply:
On approval, Terraform does the proposed operations in the correct order, respecting any resource dependencies.

Terraform does not only add complete configurations, it also allows you to change previously added configurations.
For example, changing the DNS servers of a NIC of an Azure VM does not require to redeploy the whole configuration - Terraform only alters the needed resources.

Terraform Provider for Citrix

Citrix has developed a custom Terraform provider for automating Citrix product deployments and configurations.

Note:

The Citrix Terraform provider is currently in Tech Preview!
This guide is updated when the provider has reached RTM.

Using Terraform with Citrix' provider, you can manage your Citrix products via Infrastructure as Code. Terraform is giving you higher efficiency and consistency on infrastructure management and better reusability on infrastructure configuration.

The provider defines individual units of infrastructure and currently supports both Citrix Virtual Apps and Desktops and Citrix Desktop as a Service solutions.
You can automate the creation of a site setup including host connections, machine catalogs, and delivery groups.
You can deploy resources in Azure, AWS, and GCP in addition to supported on-premises Hypervisors.

Note:

All information about the Citrix Terraform provider can be found on GitHub: https://github.com/citrix/terraform-provider-citrix

deployment-guides_citrix-daas-terraform-github.png

Terraform expects to be invoked from a working directory that contains configuration files written in the Terraform language.
Terraform uses configuration content from this directory, and also uses the directory to store settings, cached plug-ins and modules, and state data.

A working directory must be initialized before Terraform can do any operations in it.
Initialize the working directory by using the command:

terraform init 
PS C:\TACG> terraform init

 Initializing the backend...

 Successfully configured the backend "local"! Terraform will automatically use this backend unless the backend configuration changes.

 Initializing provider plug-ins...
 - Finding citrix/citrix versions matching ">= 0.3.1"...
 - Installing citrix/citrix v0.3.3...
 - Installed citrix/citrix v0.3.3 (signed by a HashiCorp partner, key ID 25D62DD8407EA386)

 Partner and community providers are signed by their developers.
 If you'd like to know more about provider signing, you can read about it here:
 https://www.terraform.io/docs/cli/plug-ins/signing.html

 Terraform has created a lock file .terraform.lock.hcl to record the provider selections it made above. Include this file in your version control repository so that Terraform can guarantee to make the same selections by default when you run "terraform init" in the future.

 Terraform has been successfully initialized!

 You may now begin working with Terraform. Try running "terraform plan" to see any changes that are required for your infrastructure. 
 All Terraform commands should now work.

 If you ever set or change modules or backend configuration for Terraform, rerun this command to reinitialize your working directory. 
 If you forget, other commands will detect it and remind you to do so if necessary.

 PS C:\TACG>

The provider defines how Terraform can interact with the underlying API. Configurations must declare which providers they require so Terraform can install and use them.

Terraform CLI finds and installs providers when initializing a working directory. It can automatically download providers from a Terraform registry, or load them from a local mirror or cache.

Example: Terraform configuration files - provider.tf

The file provider.tf contains the information on the target site where to apply the configuration.
Depending on whether it is a Citrix Cloud site or a Citrix On-Premises site, the provider needs to be configured differently:

# Cloud Provider
provider "Citrix" {
  customer_id   = "idofthexxxxxxxxx"                     ‘Citrix Cloud Customer ID
  client_id     = "3edrxxxx-XXXX-XXXX-XXXX-XXXXXXXXXX"   ‘ID of the Secure API client planned to use
  client_secret = "*********************"                ‘Secret of the Secure API client planned to use
}

A guide for the creation of a secure API client can be found in Citrix Developer Docs and is shown later as well.

# On-Premises Provider
provider "Citrix" {
  hostname      = "10.0.0.6"
  client_id     = "foo.local\\admin"
  client_secret = "foo"
}
Example - Schema used for the Provider configuration
  • client_id (String): Client Id for Citrix DaaS service authentication
    For Citrix On-Premises customers: Use this variable to specify the Domain-Admin Username.
    For Citrix Cloud customers: Use this variable to specify Cloud API Key Client ID.
    Can be set via the Environment Variable CITRIX_CLIENT_ID.

  • client_secret (String, Sensitive): Client Secret for Citrix DaaS service authentication
    For Citrix On-Premises customers: Use this variable to specify the Domain-Admin Password.
    For Citrix Cloud customers: Use this variable to specify Cloud API Key Client Secret.
    Can be set via the Environment Variable CITRIX_CLIENT_SECRET.|

  • customer_id (String): Citrix Cloud customer ID
    Only applicable for Citrix Cloud customers.
    Can be set via the Environment Variable CITRIX_CUSTOMER_ID.

  • disable_ssl_verification (Boolean): Disable SSL verification against the target DDC
    Only applicable to on-premises customers. Citrix Cloud customers do not need this option. Set to true to skip SSL verification only when the target DDC does not have a valid SSL certificate issued by a trusted CA.
    When set to true, please make sure that your provider config is set for a known DDC hostname.
    It is recommended to configure a valid certificate for the target DDC.
    Can be set via the Environment Variable CITRIX_DISABLE_SSL_VERIFICATION.|

  • environment (String): Citrix Cloud environment of the customer
    Only applicable for Citrix Cloud customers. Available options: Production, Staging, Japan, JapanStaging.
    Can be set via the Environment Variable CITRIX_ENVIRONMENT.|

  • hostname (String) | Hostname/base URL of Citrix DaaS service
    For Citrix On-Premises customers (Required): Use this variable to specify the Delivery Controller hostname.
    For Citrix Cloud customers (Optional): Use this variable to override the Citrix DaaS service hostname.
    Can be set via the Environment Variable CITRIX_HOSTNAME.|

Note:

All information about the Citrix Terraform provider can be found on GitHub.

Example: Create a Resource Group in Azure using Terraform

We create a dedicated Resource Group in Azure to have all resources in one separate place. The Resource Group does not exist before running Terraform:

deployment-guides_citrix-daas-terraform-azure-upload-create-az-rg-before.png

Before creating the Resource Group, we need to define the needed values in the corresponding Variables file azure-resource-group.auto.tfvars.json:

deployment-guides_citrix-daas-terraform-azure-var-azrg.png

Running a test before applying to check if all is correct:

PS C:\TACG> 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.CC-AZ-TF will be created
   + resource "azurerm_resource_group" "CC-AZ-TF" {
   + id         = (known after apply)
   + location   = "eastus"
   + name       = "RG-CC-AZ-TF"

}

 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>

No errors occurred, so we can apply the configuration and create the Resource Group:

PS C:\TACG> 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.CC-AZ-TF will be created
    + resource "azurerm_resource_group" "CC-AZ-TF" {
    + id        = (known after apply)
    + location  = "eastus"
    + name      = "RG-CC-AZ-TF"

}

 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.CC-AZ-TF: Creating...
 azurerm_resource_group.CC-AZ-TF: Creation complete after 2s [id=/subscriptions/58xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/RG-CC-AZ-TF]

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

 PS C:\TACG>

The Resource Group was successfully created:

deployment-guides_citrix-daas-terraform-azure-upload-create-az-rg-success.png

Deploying a Citrix Cloud Resource location on Microsoft Azure using Terraform

Overview

The aim of this guide is to showcase the possibility of creating a complete Citrix Cloud Resource Location in Azure using Terraform. We want to reduce manual interventions to the absolute minimum.

All Terraform configuration files can be found on GitHub: Insert GitHub link as soon as legal issues are solved

NOTE: For easier reading and understanding, we did not use any loop-constructs or count keywords - each resource is created explicitly.

The Terraform flow is split into different parts:

  • Part One:

    • Creating the initially needed Resources on Azure:
      • Creating a dedicated Resource Group on Azure
      • Creating a dedicated Storage Account and Storage Container on Azure
      • Uploading a pre-configured Master Image used for deploying the Machine Catalog in step 3
      • Uploading core components like Citrix Cloud Connector software and Citrix Remote PowerShell SDK to the Storage Container
  • Part Two:

    • Creating three Virtual Machines in the previously created Resource Group:

      • One VM, used as a dedicated Domain Controller (DC)
      • Two VMs, used as Cloud Controllers (CC)
      • Creating a forest on the DC
      • Putting the 2 CCs into the new domain
      • Installing the needed software on the CCs
    • Creating the necessary Resources in Citrix Cloud:

      • Creating a Resource Location in Citrix Cloud
      • Configuring the 2 CCs as Cloud Controllers
      • Registering the 2 CCs in the newly created Resource Location
  • Part Three:

    • Creating the Machine Catalog and Delivery Group in Citrix Cloud:
      • Retrieving the Site- and Zone-ID of the Resource Location
      • Creating a dedicated Hypervisor Connection to Azure
      • 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

NOTE: The first part is separated from the next steps as it is a more or less lengthy process to upload the Master Image. Depending on the available upload bandwidth it can take several hours to complete!

All needed configuration settings are stored in the corresponding Variables which need to be set. Some Configuration settings are propagated throughout the whole Terraform configuration like the Resource Group name, the Resource Group location, Authentication and Authorization settings and so on.

You need to start each of the 3 modules manually using the Terraform workflow terraform init, terraform plan, and terraform apply in the corresponding module directory. Terraform then completes the necessary configuration steps of the corresponding module.

NOTE: Each module must be completed successfully before the next module can be started!

File System structure

Root-Directory

Module 1: CConAzureUpload: Filename Purpose
CConAZUpload-provider.tf Provider definition and configuration
CConAZUpload-main.tf Resource configuration and primary flow definition
CConAZUpload-main-Variables.tf Definition of Variables
CConAzUpload-main.auto.tfvars.json Setting the values of the Variables
CitrixPoshSDK.exe Installer for Citrix Remote PowerShell SDK
CWCConnector.exe Installer for Cloud Connector software
Auto-created scripts Terraform-created scripts needed for further steps
Module 2: CConAzureCreation: Filename Purpose
CConAzCreate-provider.tf Provider definition and configuration
CConAzCreate-main.tf Resource configuration and primary flow definition
CConAzCreate-main-Variables.tf Definition of main Variables
CConAzCreate-main.auto.tfvars.json Import of Variables from previous modules
CConAzCreate-main.auto-2.tfvars.json Setting the main Variables - Possibility to set Personal settings/needs
CConAzCreate-nic-Variables.tf Definition of Azure NIC-related Variables
CConAzCreate-nic.auto.tfvars.json Setting the Azure NIC-related Variables
CConAzCreate-nsg-Variables.tf Definition of Azure Network Security Group-related Variables
CConAzCreate-nsg.auto.tfvars.json Setting the Azure Network Security Group-related Variables
CConAzCreate-pubip-Variables.tf Definition of Azure Public-IP-related Variables
CConAzCreate-pubip.auto.tfvars.json Setting the Azure Public-IP-related Variables
CConAzCreate-resourcegroup-Variables.tf Definition of Azure Resource Group-related Variables
CConAzCreate-resourcegroup.auto.tfvars.json Setting the Azure Resource Group-related Variables
CConAzCreate-subnet-Variables.tf Definition of Azure Subnet-related Variables
CConAzCreate-subnet.auto.tfvars.json Setting the Azure Subnet-related Variables
CConAzCreate-vm-Variables.tf Definition of Azure VM-related Variables
CConAzCreate-vm.auto.tfvars.json Setting the Azure VM-related Variables
CConAzCreate-vnet-Variables.tf Definition of Azure vNet-related Variables
CConAzCreate-vnet.auto.tfvars.json Setting the Azure vNet-related Variables
Auto-created scripts Terraform-created scripts needed for further steps
Module 3: CConAzureCitrixCloudStuff: Filename Purpose
CConAzCitrixCloudStuff-provider.tf Provider definition and configuration
CConAzCitrixCloudStuff-hyp.tf Hypervisor- and Hypervisor-Pool-Resource configuration
CConAzCitrixCloudStuff-hyp-Variables.tf Definition of Hypervisor-related Variables
CConAzCitrixCloudStuff-hyp.auto.tfvars.json Setting of Hypervisor-related Variables
CConAzCitrixCloudStuff-main.auto.tfvars.json Import of Variables from previous modules
CConAzCitrixCloudStuff-mc.tf Machine Catalog-Resource configuration
CConAzCitrixCloudStuff-mc-Variables.tf Definition of Machine Catalog-related Variables
CConAzCitrixCloudStuff-mc.auto.tfvars.json Setting the Machine Catalog-related Variables
CConAzCitrixCloudStuff-dg.tf Delivery Group-Resource configuration
CConAzCitrixCloudStuff-dg-Variables.tf Definition of Delivery Group-related Variables
CConAzCitrixCloudStuff-dg.auto.tfvars.json Setting the Delivery Group-related Variables
CConAzCitrixCloudStuff-zone.tf Configuration of the PowerShell-Scripts necessary for retrieving the Zone- and Site-ID
GetSiteID.ps1, GetZoneID.ps1 Auto-created PowerShell-Scripts necessary for retrieving the Zone- and Site-ID
GetSiteID.txt, GetZoneID.txt Files storing the retrieved Zone- and Site-IDs

NOTE: All Terraform-related directories and files (.terraform, -terrafrom.lock.hcl, terraform.tfstate, terraform.tfstate) must not be changed or deleted - doing so might break the deployment!

Change the settings in the .json files according to your needs.

The following prerequisites are needed before setting the corresponding settings or running the Terraform workflow to ensure a smooth and error-free build.

Prerequisites

Getting the Azure Resource Locations

The following PowerShell cmdlet lists Azure´s available Resource Locations:

Get-AzLocation | Select Location,Displayname
PS C:\TACG> Get-AzLocation | Select Location,Displayname

| Location            | DisplayName          |
|---------------------|----------------------|
|  eastus             | East US              | 
|  eastus2            | East US 2            |
|  southcentralus     | South Central US     |
|  westus2            | West US 2            |
|  westus3            | West US 3            |
|  australiaeast      | Australia East       |
|  southeastasia      | Southeast Asia       |
|  northeurope        | North Europe         |
|  swedencentral      | Sweden Central       |
|  uksouth            | UK South             |
|  westeurope         | West Europe          |
|  centralus          | Central US           |
|  germanywestcentral | Germany West Central |

...
PS C:\TACG>

Write down the Location parameter of the Azure Resource Location that you want to use.

Getting the available Source Images from Azure

We want to automatically deploy the virtual machines necessary for the DC and the CCs - so we need detailed configuration settings:

storage_image_reference {
    publisher = "Publisher of the Image used to create the VM"
    offer     = "Offer of the Image used to create the VM"
    sku       = "SKU of the Image used to create the VM"
    version   = "Version of the Image used to create the VM" - preferable “latest”
  }

Some PowerShell scripts help us to list the available Images for Windows Server-based Images:

$location="EastUs"
Get-AzVMImagePublisher -Location $location | Where PublisherName -like 'MicrosoftWindows*'
PS C:\TACG> $location="EastUs"
PS C:\TACG> Get-AzVMImagePublisher -Location $location | Where PublisherName -like 'MicrosoftWindows*'

 PublisherName                 Location Id
 -------------                 -------- --
 MicrosoftWindowsDesktop       eastus   /Subscriptions/58xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/Providers/Microsoft.Compute/Locations/eastus/Publishers/MicrosoftWindowsDesktop
 MicrosoftWindowsServer        eastus   /Subscriptions/58xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/Providers/Microsoft.Compute/Locations/eastus/Publishers/MicrosoftWindowsServer
 MicrosoftWindowsServerHPCPack eastus   /Subscriptions/58xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/Providers/Microsoft.Compute/Locations/eastus/Publishers/MicrosoftWindowsServerHPCPack

 PS C:\TACG>

Note the PublisherName parameter of the Azure Resource Location that you want to use.

$location="EastUs"
$PublisherName="MicrosoftWindowsServer"
Get-AzVMImageOffer -Location $location -PublisherName $PublisherName | select offer
PS C:\TACG> $location="EastUs"
PS C:\TACG> $PublisherName="MicrosoftWindowsServer"
PS C:\TACG> Get-AzVMImageOffer -Location $location -PublisherName $PublisherName | select offer

 Offer
 -----
 19h1gen2servertest
 microsoftserveroperatingsystems-previews
 servertesting
 windows-cvm
 WindowsServer
 windowsserver-gen2preview
 windowsserver-previewtest
 windowsserverdotnet
 windowsserverhotpatch-previews
 WindowsServerSemiAnnual
 windowsserverupgrade

 PS C:\TACG>

Note the Offer parameter of the Azure Resource Location that you want to use.

$location="EastUs"
$PublisherName="MicrosoftWindowsServer"
$OfferName="WindowsServer"
Get-AzVMImageSku -Location $location -PublisherName $PublisherName -Offer $OfferName | Select SKUs
PS C:\TACG> $PublisherName="MicrosoftWindowsServer"
PS C:\TACG> $OfferName="WindowsServer"
PS C:\TACG> $location="EastUs"
PS C:\TACG> Get-AzVMImageSku -Location $location -PublisherName $PublisherName -Offer $OfferName | Select SKUs

 Skus
 ----
 2008-R2-SP1
 2008-R2-SP1-smalldisk
 2008-R2-SP1-zhcn
 2012-Datacenter
 2012-datacenter-gensecond
 2012-Datacenter-smalldisk
 2012-datacenter-smalldisk-g2
 2012-Datacenter-zhcn
 2012-datacenter-zhcn-g2
 2012-R2-Datacenter
 2012-r2-datacenter-gensecond
 2012-R2-Datacenter-smalldisk
 2012-r2-datacenter-smalldisk-g2
 2012-R2-Datacenter-zhcn
 2012-r2-datacenter-zhcn-g2
 2016-Datacenter
 2016-datacenter-gensecond
 2016-datacenter-gs
 2016-Datacenter-Server-Core
 2016-datacenter-server-core-g2
 2016-Datacenter-Server-Core-smalldisk
 2016-datacenter-server-core-smalldisk-g2
 2016-Datacenter-smalldisk
 2016-datacenter-smalldisk-g2
 2016-Datacenter-with-Containers
 2016-datacenter-with-containers-g2
 2016-datacenter-with-containers-gs
 2016-Datacenter-zhcn
 2016-datacenter-zhcn-g2
 2019-Datacenter
 2019-Datacenter-Core
 2019-datacenter-core-g2
 2019-Datacenter-Core-smalldisk
 2019-datacenter-core-smalldisk-g2
 2019-Datacenter-Core-with-Containers
 2019-datacenter-core-with-containers-g2
 2019-Datacenter-Core-with-Containers-smalldisk
 2019-datacenter-core-with-containers-smalldisk-g2
 2019-datacenter-gensecond
 2019-datacenter-gs
 2019-Datacenter-smalldisk
 2019-datacenter-smalldisk-g2
 2019-Datacenter-with-Containers
 2019-datacenter-with-containers-g2
 2019-datacenter-with-containers-gs
 2019-Datacenter-with-Containers-smalldisk
 2019-datacenter-with-containers-smalldisk-g2
 2019-Datacenter-zhcn
 2019-datacenter-zhcn-g2
 2022-datacenter
 2022-datacenter-azure-edition
 2022-datacenter-azure-edition-core
 2022-datacenter-azure-edition-core-smalldisk
 2022-datacenter-azure-edition-hotpatch
 2022-datacenter-azure-edition-hotpatch-smalldisk
 2022-datacenter-azure-edition-smalldisk
 2022-datacenter-core
 2022-datacenter-core-g2
 2022-datacenter-core-smalldisk
 2022-datacenter-core-smalldisk-g2
 2022-datacenter-g2
 2022-datacenter-smalldisk
 2022-datacenter-smalldisk-g2
 datacenter-core-1809-with-containers-smalldisk-g2
 Datacenter-Core-1903-with-Containers-smalldisk
 datacenter-core-1903-with-containers-smalldisk-g2
 datacenter-core-1909-with-containers-smalldisk
 datacenter-core-1909-with-containers-smalldisk-g2
 datacenter-core-2004-with-containers-smalldisk
 datacenter-core-2004-with-containers-smalldisk-g2
 datacenter-core-20h2-with-containers-smalldisk
 datacenter-core-20h2-with-containers-smalldisk-g2
 datacenter-core-20h2-with-containers-smalldisk-gs

 PS C:\TACG> 

Note the Skus parameter of the Azure Resource Location that you want to use.

Based on all retrieved parameters we can now directly define the Storage_Image_Reference block - in this example we use Windows Server 2022 Azure Edition in the latest available version:

storage_image_reference {
    publisher = "MicrosoftWindowsServer"
    offer     = "WindowsServer"
    sku       = "2022-datacenter-azure-edition"
    version   = "latest" 
}

You can also set the corresponding variable in our CConAzCreate-vm.auto.tfvars.json file:

"azurerm-vm-sourceimage-publisher": "MicrosoftWindowsServer",
"azurerm-vm-sourceimage-offer": "WindowsServer",
"azurerm-vm-sourceimage-sku": "2022-datacenter-azure-edition",
"azurerm-vm-sourceimage-version": "latest"

Getting the available VM sizes from Azure

We need to determine the available VM sizes. A PowerShell script helps us to list the available Images for Windows Server-based Images:

$location="EastUs"
Get-AzVMSize -Location $location | Where Name -like 'Standard_D2*' | Select Name,NumberOfCores,MemoryInMB,OSDiskSizeInMB,ResourceDiskSizeInMB | Format-Table
PS C:\TACG> $location="EastUs"
PS C:\TACG> Get-AzVMSize -Location $location | Where Name -like 'Standard_D2*' | Select Name,NumberOfCores,MemoryInMB,OSDiskSizeInMB,ResourceDiskSizeInMB | Format-Table

 Name                 NumberOfCores MemoryInMB OSDiskSizeInMB ResourceDiskSizeInMB
 ----                 ------------- ---------- -------------- --------------------
 Standard_D2_v2               2     7168       1047552        102400
 Standard_D2_v2_Promo         2     7168       1047552        102400
 Standard_D2_v3               2     8192       1047552        51200
 Standard_D2                  2     7168       1047552        102400
 Standard_D2s_v3              2     8192       1047552        16384
 Standard_D2d_v4              2     8192       1047552        76800
 Standard_D2_v4               2     8192       1047552        0
 Standard_D2ds_v4             2     8192       1047552        76800
 Standard_D2s_v4              2     8192       1047552        0
 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_D2ds_v5             2     8192       1047552        76800
 Standard_D2d_v5              2     8192       1047552        76800
 Standard_D2s_v5              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_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

 PS C:\TACG>

Note the Name parameter of the Azure Resource Location that you want to use.

Caution: Be sure that your subscription has no quota limitation on the chosen VM type and you have enough resources on Azure to create all the Virtual Machines planned to put into the Machine Catalog by checking quotas - otherwise the creation of the Machine Catalog fails if there are not enough compute resources available!

Example: Checking the vCPU usage and limit for a D2S_v5 VM which we plan to use in this guide:

 $Location = 'East US'
 $VMSize = 'Standard_D2s_v5'
 $SKU = Get-AzComputeResourceSku -Location $Location | where ResourceType -eq "virtualMachines" | select Name,Family
 $VMFamily = ($SKU | where Name -eq $VMSize | select -Property Family).Family
 Get-AzVMUsage -Location $Location | Where-Object { $_.Name.Value -eq $VMFamily }
PS C:\TACG> $Location = 'East US'
PS C:\TACG> $VMSize = 'Standard_D2s_v5'
PS C:\TACG> $SKU = Get-AzComputeResourceSku -Location $Location | where ResourceType -eq "virtualMachines" | select Name,Family
PS C:\TACG> $VMFamily = ($SKU | where Name -eq $VMSize | select -Property Family).Family
PS C:\TACG> Get-AzVMUsage -Location $Location | Where-Object { $_.Name.Value -eq $VMFamily }

Name                                Current Value Limit  Unit
----                                ------------- -----  ----
Standard DSv5 Family vCPUs          10            16     Count

PS C:\TACG>

This example shows that we can use a maximum of 3 further D2S_v5 VMs before exceeding the limit. The vCPU quota or any other quota can be increased using the Azure Console or PowerShell. More information about increasing vCPU quotas can be found here: https://learn.microsoft.com/en-us/azure/quotas/per-vm-quota-requests

Pre-configured Master Image for deploying the Machine Catalog

Terraform needs a pre-configured Master Image provided in a fixed-type VHD format to create the Machine Catalog.

To convert a VHDX into a fixed-type VHD, use this PowerShell cmdlet:

Convert-VHD -path SourcePath -DestinationPath DestinationPath -VHDType fixed
 PS C:\TACG> Convert-VHD -path c:\_hyper-v\w11-mi.vhdx -DestinationPath c:\_hyper-v\w11-min.vhd -VHDType fixed
 Converting Virtual Disk                                                                                  44%                                                                                                                    [ooooooooooooooooooooooooooooooooooooooooooooooo                                                             ]                                                                                                                     

 PS C:\TACG> 

Further Software Components for Configuration and Deployment

Please make sure to put the actual version of the following software components in the Root-directory of Module 1 - CConAzureUpload:

These components are required during the workflow. The Terraform engine looks for these files.

Creating a Service Principle 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.

Creation using PowerShell: Use the following commands to create a Service Principal in Azure:

PS C:\TACG> Add-PSSnapin Az.*
PS C:\TACG> Connect-AZaccount

 Account              SubscriptionName   TenantId                             Environment
 -------              ----------------   --------                             -----------
 adxxxxxxx@xxxxxx     Azure-Abonnement 1 e85XXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX AzureCloud
PS C:\TACG> 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":       "e9eXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
   "displayName": "azure-cli-2023-10-23-14-19-40",
   "password":    "fFXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
   "tenant":      "e85XXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
 }
 PS C:\TACG>

Write down all parameters and put them into the CConAzUpload-main.auto.tfvars.json file.

…
"azurerm-clientid": "e9eXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
"azurerm-clientsecret": "fFXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"azurerm-tenantid": "e85XXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
"azurerm-subscriptionid": "58dXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
…

Creating a Secure Client in Citrix Cloud

The Secure Client in Citrix Cloud is the same to 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 to more than one customer, you must create API clients within each customer.
API clients are automatically restricted to the rights of that administrator that created it. For example, if an administrator is restricted to access only notifications, then the administrator’s API clients have the same restrictions:

  • Reducing an administrator’s access also reduces the access of the API clients owned by that administrator
  • Removing an administrator’s access also removes the administrator’s API clients.

To create an API client, select the Identity and Access Management option from the menu. If this option does not appear, you do not have adequate permissions to create an API client. Contact your administrator to get the required permissions.

  1. Open Identity and Access Management in WebStudio:

    deployment-guides_citrix-daas-terraform-azure-create-cc-apiclient.png

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

    deployment-guides_citrix-daas-terraform-azure-create-cc-apiclient1.png

  3. After the Secure Client is created, copy and write down the shown ID and Secret:

    deployment-guides_citrix-daas-terraform-azure-create-cc-apiclient2.png

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 CConAzCreate-main-2.auto.tfvars.json file:

...
"cc-customerid": "uzXXXXXXXX",
"cc-apikey-clientId": "f4eXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
"cc-apikey-clientSecret": "VXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"cc-apikey-type": "client_credentials",
...

Creating a Bearer Token in Citrix Cloud

The Bearer Token is needed for Authorization of some REST-API calls in Citrix Cloud.
As the Citrix provider currently has not implemented all functionalities yet, some REST-API calls are still needed. The Bearer Token authorizes these calls.

It is important to set the URI to call and the required parameters correct: The URI must follow this syntax:
For example [https://api-us.cloud.com/cctrustoauth2/{customerid}/tokens/clients] where {customerid} is your Customer ID you obtained from the Account Settings page.
If your Customer ID is for example 1234567890, the URI is [https://api-us.cloud.com/cctrustoauth2/1234567890/tokens/clients]

In this example we use the Postman application to create a Bearer Token:

  1. Paste the correct URI into Postman´s address bar and select POST as the method. Verify the correct settings of the API call.

    deployment-guides_citrix-daas-terraform-azure-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.

  2. Put the access-token value in the CConAzCreate-main-2.auto.tfvars.json file:

...
"cc-apikey-type": "client_credentials",
"cc-apikey-bearer": "CWSAuth bearer=eyJhbGciOiJSUzI1NiIsI...0q0IW7SZFVzeBittWnEwTYOZ7Q" 
...

Module 1: Create the initially needed Resources on Microsoft Azure

This module is split into the following configuration parts:

  1. Creating a dedicated Resource Group on Azure
  2. Creating a dedicated Storage Account and Storage Container on Azure
  3. Uploading a pre-configured Master Image used for deploying the Machine Catalog in step 3
  4. Uploading core components like Citrix Cloud Connector software and Citrix Remote PowerShell SDK to the Storage Container

NOTE: This first part is separated from the next steps as it is a more or less lengthy process to upload the Master Image. Depending on the available upload bandwidth it can take several hours to complete!

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

PS C:\TACG> terraform init

 Initializing the backend...

 Initializing provider plug-ins...
 - Finding hashicorp/azurerm versions matching "3.0.0"...
 - Finding mastercard/restapi versions matching "1.18.2"...
 - Finding citrix/citrix versions matching ">= 0.3.5"...
 - Finding latest version of hashicorp/time...
 - Finding latest version of hashicorp/local...
 - Installing hashicorp/azurerm v3.0.0...
 - Installed hashicorp/azurerm v3.0.0 (signed by HashiCorp)
 - Installing mastercard/restapi v1.18.2...
 - Installed mastercard/restapi v1.18.2 (self-signed, key ID DCB8C431D71C30AB)
 - Installing citrix/citrix v0.3.5...
 - Installed citrix/citrix v0.3.5 (signed by a HashiCorp partner, key ID 25D62DD8407EA386)
 - Installing hashicorp/time v0.10.0...
 - Installed hashicorp/time v0.10.0 (signed by HashiCorp)
 - Installing hashicorp/local v2.4.1...
 - Installed hashicorp/local v2.4.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/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>

PS C:\TACG> 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.CC-AZ-TF will be created
     + resource "azurerm_resource_group" "CC-AZ-TF" {
     + id       = (known after apply)
     + location = "eastus"
     + name     = "RG-CC-AZ-TF-TEST"
}

   # azurerm_storage_account.CC-AZ-SB will be created
     + resource "azurerm_storage_account" "CC-AZ-SB" {
     + access_tier                       = (known after apply)
     + account_kind                      = "StorageV2"
     + account_replication_type          = "LRS"
     + account_tier                      = "Standard"
     + allow_nested_items_to_be_public   = true
     + enable_https_traffic_only         = true
     + id                                = (known after apply)
     + infrastructure_encryption_enabled = false
     + is_hns_enabled                    = false
     + large_file_share_enabled          = (known after apply)
     + location                          = "eastus"
     + min_tls_version                   = "TLS1_2"
     + name                              = "saxxxxxxxxxxxxxx"
     + nfsv3_enabled                     = false
     + primary_access_key                = (sensitive value)
     + primary_blob_connection_string    = (sensitive value)
     + primary_blob_endpoint             = (known after apply)
     + primary_blob_host                 = (known after apply)
     + primary_connection_string         = (sensitive value)
     + primary_dfs_endpoint              = (known after apply)
     + primary_dfs_host                  = (known after apply)
     + primary_file_endpoint             = (known after apply)
     + primary_file_host                 = (known after apply)
     + primary_location                  = (known after apply)
     + primary_queue_endpoint            = (known after apply)
     + primary_queue_host                = (known after apply)
     + primary_table_endpoint            = (known after apply)
     + primary_table_host                = (known after apply)
     + primary_web_endpoint              = (known after apply)
     + primary_web_host                  = (known after apply)
     + queue_encryption_key_type         = "Service"
     + resource_group_name               = "RG-CC-AZ-TF-TEST"
     + secondary_access_key              = (sensitive value)
     + secondary_blob_connection_string  = (sensitive value)
     + secondary_blob_endpoint           = (known after apply)
     + secondary_blob_host               = (known after apply)
     + secondary_connection_string       = (sensitive value)
     + secondary_dfs_endpoint            = (known after apply)
     + secondary_dfs_host                = (known after apply)
     + secondary_file_endpoint           = (known after apply)
     + secondary_file_host               = (known after apply)
     + secondary_location                = (known after apply)
     + secondary_queue_endpoint          = (known after apply)
     + secondary_queue_host              = (known after apply)
     + secondary_table_endpoint          = (known after apply)
     + secondary_table_host              = (known after apply)
     + secondary_web_endpoint            = (known after apply)
     + secondary_web_host                = (known after apply)
     + shared_access_key_enabled         = true
     + table_encryption_key_type         = "Service"
   }

   # azurerm_storage_blob.CC-AZ-SB-CTXPOSHSDK will be created
   + resource "azurerm_storage_blob" "CC-AZ-SB-CTXPOSHSDK" {
     + access_tier            = (known after apply)
     + content_type           = "application/octet-stream"
     + id                     = (known after apply)
     + metadata               = (known after apply)
     + name                   = "CitrixPoshSdk.exe"
     + parallelism            = 8
     + size                   = 0
     + source                 = "CitrixPoshSdk.exe"
     + storage_account_name   = "saxxxxxxxxxxxxxx"
     + storage_container_name = "scsaxxxxxxxxxxxxxx"
     + type                   = "Block"
     + url                    = (known after apply)
   }

   # azurerm_storage_blob.CC-AZ-SB-CWC will be created
   + resource "azurerm_storage_blob" "CC-AZ-SB-CWC" {
     + access_tier            = (known after apply)
     + content_type           = "application/octet-stream"
     + id                     = (known after apply)
     + metadata               = (known after apply)
     + name                   = "cwcconnector.exe"
     + parallelism            = 8
     + size                   = 0
     + source                 = "cwcconnector.exe"
     + storage_account_name   = "saxxxxxxxxxxxxxx"
     + storage_container_name = "scsaxxxxxxxxxxxxxx"
     + type                   = "Block"
     + url                    = (known after apply)
   }

   # azurerm_storage_container.CC-AZ-SB will be created
   + resource "azurerm_storage_container" "CC-AZ-SB" {
     + container_access_type   = "blob"
     + has_immutability_policy = (known after apply)
     + has_legal_hold          = (known after apply)
     + id                      = (known after apply)
     + metadata                = (known after apply)
     + name                    = "scsaxxxxxxxxxxxxxx"
     + resource_manager_id     = (known after apply)
     + storage_account_name    = "saxxxxxxxxxxxxxx"
   }

   # local_file.OutputAllVars will be created
   + resource "local_file" "OutputAllVars" {
     + content              = jsonencode(
           {
             + azurerm-clientid                       = "b1xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
             + azurerm-clientsecret                   = "h5xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
             + azurerm-resource_group_location        = "eastus"
             + azurerm-resource_group_name            = "RG-CC-AZ-TF-TEST"
             + azurerm-storage_account_container_name = "scsaxxxxxxxxxxxxxx"
             + azurerm-storage_account_location       = "eastus"
             + azurerm-storage_account_name           = "saxxxxxxxxxxxxxx"
             + azurerm-storage_account_replication    = "LRS"
             + azurerm-storage_account_tier           = "Standard"
             + azurerm-subscriptionid                 = "58xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
             + azurerm-tenantid                       = "e8xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
             + cc-customerid                          = "uxxxxxxxxxxxxxx"
           }
       )
     + 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             = "./../__CConAzureCreation/CConAzCreate-main.auto.tfvars.json"
     + id                   = (known after apply)
   }

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

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

+ resource "azurerm_storage_blob" "CC-AZ-SB-MI" {
     + access_tier            = (known after apply)
     + content_type           = "application/octet-stream"
     + id                     = (known after apply)
     + metadata               = (known after apply)
     + name                   = "TF-MIN-UP-VHD.vhd"
     + parallelism            = 8
     + size                   = 0
     + source                 = "D:/_PPMM/__TF/__CConAzureUpload/W11-MIN.vhd"
     + storage_account_name   = "saxxxxxxxxxxxxxx"
     + storage_container_name = "scsaxxxxxxxxxxxxxx"
     + type                   = "Block"
     + url                    = (known after apply)
   }

 Plan: 9 to add, 0 to change, 0 to destroy.
PS C:\TACG\_PPMM\__TF\__CConAzureUpload> 
PS C:\TACG\_PPMM\__TF\__CConAzureUpload> 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.CC-AZ-TF will be created
   + resource "azurerm_resource_group" "CC-AZ-TF" {
     + id       = (known after apply)
     + location = "eastus"
     + name     = "RG-CC-AZ-TF-TEST"
   }

   # azurerm_storage_account.CC-AZ-SB will be created
   + resource "azurerm_storage_account" "CC-AZ-SB" {
     + access_tier                       = (known after apply)
     + account_kind                      = "StorageV2"
     + account_replication_type          = "LRS"
     + account_tier                      = "Standard"
     + allow_nested_items_to_be_public   = true
     + enable_https_traffic_only         = true
     + id                                = (known after apply)
     + infrastructure_encryption_enabled = false
     + is_hns_enabled                    = false
     + large_file_share_enabled          = (known after apply)
     + location                          = "eastus"
     + min_tls_version                   = "TLS1_2"
     + name                              = "saxxxxxxxxxxxxxx"
     + nfsv3_enabled                     = false
     + primary_access_key                = (sensitive value)
     + primary_blob_connection_string    = (sensitive value)
     + primary_blob_endpoint             = (known after apply)
     + primary_blob_host                 = (known after apply)
     + primary_connection_string         = (sensitive value)
     + primary_dfs_endpoint              = (known after apply)
     + primary_dfs_host                  = (known after apply)
     + primary_file_endpoint             = (known after apply)
     + primary_file_host                 = (known after apply)
     + primary_location                  = (known after apply)
     + primary_queue_endpoint            = (known after apply)
     + primary_queue_host                = (known after apply)
     + primary_table_endpoint            = (known after apply)
     + primary_table_host                = (known after apply)
     + primary_web_endpoint              = (known after apply)
     + primary_web_host                  = (known after apply)
     + queue_encryption_key_type         = "Service"
     + resource_group_name               = "RG-CC-AZ-TF-TEST"
     + secondary_access_key              = (sensitive value)
     + secondary_blob_connection_string  = (sensitive value)
     + secondary_blob_endpoint           = (known after apply)
     + secondary_blob_host               = (known after apply)
     + secondary_connection_string       = (sensitive value)
     + secondary_dfs_endpoint            = (known after apply)
     + secondary_dfs_host                = (known after apply)
     + secondary_file_endpoint           = (known after apply)
     + secondary_file_host               = (known after apply)
     + secondary_location                = (known after apply)
     + secondary_queue_endpoint          = (known after apply)
     + secondary_queue_host              = (known after apply)
     + secondary_table_endpoint          = (known after apply)
     + secondary_table_host              = (known after apply)
     + secondary_web_endpoint            = (known after apply)
     + secondary_web_host                = (known after apply)
     + shared_access_key_enabled         = true
     + table_encryption_key_type         = "Service"
   }

   # azurerm_storage_blob.CC-AZ-SB-CTXPOSHSDK will be created
   + resource "azurerm_storage_blob" "CC-AZ-SB-CTXPOSHSDK" {
     + access_tier            = (known after apply)
     + content_type           = "application/octet-stream"
     + id                     = (known after apply)
     + metadata               = (known after apply)
     + name                   = "CitrixPoshSdk.exe"
     + parallelism            = 8
     + size                   = 0
     + source                 = "CitrixPoshSdk.exe"
     + storage_account_name   = "saxxxxxxxxxxxxxx"
     + storage_container_name = "scsaxxxxxxxxxxxxxx"
     + type                   = "Block"
     + url                    = (known after apply)
   }

   # azurerm_storage_blob.CC-AZ-SB-CWC will be created
   + resource "azurerm_storage_blob" "CC-AZ-SB-CWC" {
     + access_tier            = (known after apply)
     + content_type           = "application/octet-stream"
     + id                     = (known after apply)
     + metadata               = (known after apply)
     + name                   = "cwcconnector.exe"
     + parallelism            = 8
     + size                   = 0
     + source                 = "cwcconnector.exe"
     + storage_account_name   = "saxxxxxxxxxxxxxx"
     + storage_container_name = "scsaxxxxxxxxxxxxxx"
     + type                   = "Block"
     + url                    = (known after apply)
   }

   # azurerm_storage_container.CC-AZ-SB will be created
   + resource "azurerm_storage_container" "CC-AZ-SB" {
     + container_access_type   = "blob"
     + has_immutability_policy = (known after apply)
     + has_legal_hold          = (known after apply)
     + id                      = (known after apply)
     + metadata                = (known after apply)
     + name                    = "scsaxxxxxxxxxxxxxx"
     + resource_manager_id     = (known after apply)
     + storage_account_name    = "saxxxxxxxxxxxxxx"
   }

   # local_file.OutputAllVars will be created
   + resource "local_file" "OutputAllVars" {
     + content              = jsonencode(
           {
             + azurerm-clientid                       = "b1xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
             + azurerm-clientsecret                   = "h5xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
             + azurerm-resource_group_location        = "eastus"
             + azurerm-resource_group_name            = "RG-CC-AZ-TF-TEST"
             + azurerm-storage_account_container_name = "scsaxxxxxxxxxxxxxx"
             + azurerm-storage_account_location       = "eastus"
             + azurerm-storage_account_name           = "saxxxxxxxxxxxxxx"
             + azurerm-storage_account_replication    = "LRS"
             + azurerm-storage_account_tier           = "Standard"
             + azurerm-subscriptionid                 = "58xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
             + azurerm-tenantid                       = "e8xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
             + cc-customerid                          = "uxxxxxxxxxxxxxx"
           }
       )
     + 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             = "./../__CConAzureCreation/CConAzCreate-main.auto.tfvars.json"
     + id                   = (known after apply)
   }

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

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

+ resource "azurerm_storage_blob" "CC-AZ-SB-MI" {
     + access_tier            = (known after apply)
     + content_type           = "application/octet-stream"
     + id                     = (known after apply)
     + metadata               = (known after apply)
     + name                   = "TF-MIN-UP-VHD.vhd"
     + parallelism            = 8
     + size                   = 0
     + source                 = "D:/_PPMM/__TF/__CConAzureUpload/W11-MIN.vhd"
     + storage_account_name   = "saxxxxxxxxxxxxxx"
     + storage_container_name = "scsaxxxxxxxxxxxxxx"
     + type                   = "Block"
     + url                    = (known after apply)
   }

... output removed, as upload lasted more then 20 hours ...
Apply complete! Resources: 9 added, 0 changed, 0 destroyed.

 PS C:\TACG\_PPMM\__TF\__CConAzureUpload>

As no errors occurred, Terraform has completed the creation, configuration, and upload of the relevant prerequisites.
Variables needed in the next module were passed to the next module´s root-directory as a json-file.

Example of successful creation:
Creation of the Storage Container on Azure including all required uploads:

deployment-guides_citrix-daas-terraform-azure-upload-create-st-success.png

Now the next step can be started.

Module 2: Create all Resources in Microsoft Azure and Citrix Cloud

This module is split into the following configuration parts:

  1. Creating three Virtual Machines in the previously created Resource Group:

    • One VM, used as a dedicated Domain Controller (DC)
    • Two VMs, used as Cloud Controllers (CC)
  2. Creating a forest on the DC

  3. Putting the 2 CCs into the new domain

  4. Installing the needed software on the CCs

  5. Creating the necessary Resources in Citrix Cloud:

    • Creating a Resource Location in Citrix Cloud
    • Configuring the 2 CCs as Cloud Controllers
    • Registering the 2 CCs in the newly created Resource Location

NOTE: This module has some complexity as many automated actions and many different entities are created on Azure and run on the VMs.

Our provider currently does not support creating a Resource Location on Citrix Cloud. Therefore we use an other provider to create it using a REST-API call.

Please make sure you have configured the variables according to your needs.

NOTE: Each entity can be solely configured using the corresponding .auto.tfvars.json file.

Example:
The configuration of the VMs can be set in the CConAzCreate-vm.auto.tfvars.json file

{
    "azurerm-vm-name": "VM-TF-CCCC1",
    "azurerm-vm-name2": "VM-TF-CCCC2",
    "azurerm-vm-name-dc": "VM-TF-CC-DC",
    "azurerm-vm-size": "Standard_D2s_v5",
    "azurerm-vm-adun": "azxxxxxxxxxxxxxxxxxxxxxxxx",
    "azurerm-vm-adpw": "!Dxxxxxxxxxxxxxxxxxxxxxxxxx",
    "azurerm-vm-domainAdminUsername": "tf\\azxxxxxxxxxxxxxxxxxxxxx",
    "azurerm-vm-domainadmin-pw": "!Dxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "azurerm-vm-domainname": "tf.the-austrian-citrix-guy.at",
    "azurerm-vm-netbiosname": "tf",
    "azurerm-vm-nics": 
    [
        "NIC1"
    ],
    "azurerm-vm-nics2": 
    [
        "NIC2"
    ],
    "azurerm-vm-nics3": 
    [
        "NIC3"
    ],
    "azurerm-vm-osdisk-name": "OsDisk1",
    "azurerm-vm-osdisk-name2": "OsDisk2",
    "azurerm-vm-osdisk-name-dc": "OsDisk-DC",
    "azurerm-vm-osdisk-cache": "ReadWrite",
    "azurerm-vm-osdisk-storagetype": "Standard_LRS",
    "azurerm-vm-osdisk-size": "120",
    "azurerm-vm-osdisk-createfrom": "FromImage",
    "azurerm-vm-sourceimage-publisher": "MicrosoftWindowsServer",
    "azurerm-vm-sourceimage-offer": "WindowsServer",
    "azurerm-vm-sourceimage-sku": "2022-datacenter-azure-edition",
    "azurerm-vm-sourceimage-version": "latest"
}

The Terraform configuration contains some idle time slots to make sure that background operations on Azure or on the VMs can completed before the next configuration steps occur.
We have seen different elapsed configuration times related to different loads on the Azure systems!

The configuration can be started by following the normal Terraform workflow:
terraform init,
terraform plan
and if no errors occur

terraform apply

PS C:\_TACG\__CConAzureCreation> terraform init

 Initializing the backend...

 Initializing provider plug-ins...
 - terraform.io/builtin/terraform is built in to Terraform
 - Finding latest version of hashicorp/time...
 - Finding latest version of hashicorp/local...
 - Finding latest version of hashicorp/random...
 - Finding latest version of hashicorp/http...
 - Finding mastercard/restapi versions matching "1.18.2"...
 - Finding citrix/citrix versions matching ">= 0.3.5"...
 - Finding hashicorp/azurerm versions matching "3.0.0"...
 - Installing hashicorp/time v0.10.0...
 - Installed hashicorp/time v0.10.0 (signed by HashiCorp)
 - Installing hashicorp/local v2.4.1...
 - Installed hashicorp/local v2.4.1 (signed by HashiCorp)
 - Installing hashicorp/random v3.6.0...
 - Installed hashicorp/random v3.6.0 (signed by HashiCorp)
 - Installing hashicorp/http v3.4.1...
 - Installed hashicorp/http v3.4.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.3.5...
 - Installed citrix/citrix v0.3.5 (signed by a HashiCorp partner, key ID 25D62DD8407EA386)
 - Installing hashicorp/azurerm v3.0.0...
 - Installed hashicorp/azurerm v3.0.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/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\__CConAzureCreation> 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_managed_disk.CreateManagedDiskFromUploadedVHD will be created
   + resource "azurerm_managed_disk" "CreateManagedDiskFromUploadedVHD" {
       + create_option                 = "Import"
       + disk_iops_read_only           = (known after apply)
       + disk_iops_read_write          = (known after apply)
       + disk_mbps_read_only           = (known after apply)
       + disk_mbps_read_write          = (known after apply)
       + disk_size_gb                  = (known after apply)
       + hyper_v_generation            = "V2"
       + id                            = (known after apply)
       + location                      = "eastus"
       + logical_sector_size           = (known after apply)
       + max_shares                    = (known after apply)
       + name                          = "w11-min.vhd"
       + os_type                       = "Windows"
       + public_network_access_enabled = true
       + resource_group_name           = "RG-CC-AZ-TF-TEST"
       + source_uri                    = "https://saxxxxxxxxxxxxxx.blob.core.windows.net/scsaxxxxxxxxxxxxxx/w11-min.vhd"
       + storage_account_id            = "/subscriptions/58xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/RG-CC-AZ-TF-TEST/providers/Microsoft.Storage/storageAccounts/saxxxxxxxxxxxxxx"
       + storage_account_type          = "Standard_LRS"
       + tier                          = (known after apply)
     }

   # azurerm_network_interface.CC-AZ-TF will be created
   + resource "azurerm_network_interface" "CC-AZ-TF" {
       + applied_dns_servers           = (known after apply)
       + dns_servers                   = (known after apply)
       + enable_accelerated_networking = false
       + enable_ip_forwarding          = false
       + id                            = (known after apply)
       + internal_dns_name_label       = (known after apply)
       + internal_domain_name_suffix   = (known after apply)
       + location                      = "eastus"
       + mac_address                   = (known after apply)
       + name                          = "NIC-TF-AZ-CC1"
       + private_ip_address            = (known after apply)
       + private_ip_addresses          = (known after apply)
       + resource_group_name           = "RG-CC-AZ-TF-TEST"
       + virtual_machine_id            = (known after apply)

       + ip_configuration {
           + gateway_load_balancer_frontend_ip_configuration_id = (known after apply)
           + name                                               = "IP-TF-AZ-CC1"
           + primary                                            = (known after apply)
           + private_ip_address                                 = (known after apply)
           + private_ip_address_allocation                      = "Dynamic"
           + private_ip_address_version                         = "IPv4"
           + public_ip_address_id                               = (known after apply)
           + subnet_id                                          = (known after apply)
         }
     }

   # azurerm_network_interface.CC-AZ-TF-DC will be created
   + resource "azurerm_network_interface" "CC-AZ-TF-DC" {
       + applied_dns_servers           = (known after apply)
       + dns_servers                   = [
           + "10.0.0.4",
         ]
       + enable_accelerated_networking = false
       + enable_ip_forwarding          = false
       + id                            = (known after apply)
       + internal_dns_name_label       = (known after apply)
       + internal_domain_name_suffix   = (known after apply)
       + location                      = "eastus"
       + mac_address                   = (known after apply)
       + name                          = "NIC-TF-AZ-DC"
       + private_ip_address            = (known after apply)
       + private_ip_addresses          = (known after apply)
       + resource_group_name           = "RG-CC-AZ-TF-TEST"
       + virtual_machine_id            = (known after apply)

       + ip_configuration {
           + gateway_load_balancer_frontend_ip_configuration_id = (known after apply)
           + name                                               = "IP-TF-AZ-DC"
           + primary                                            = (known after apply)
           + private_ip_address                                 = (known after apply)
           + private_ip_address_allocation                      = "Dynamic"
           + private_ip_address_version                         = "IPv4"
           + public_ip_address_id                               = (known after apply)
           + subnet_id                                          = (known after apply)
         }
     }

   # azurerm_network_interface.CC-AZ-TF2 will be created
   + resource "azurerm_network_interface" "CC-AZ-TF2" {
       + applied_dns_servers           = (known after apply)
       + dns_servers                   = (known after apply)
       + enable_accelerated_networking = false
       + enable_ip_forwarding          = false
       + id                            = (known after apply)
       + internal_dns_name_label       = (known after apply)
       + internal_domain_name_suffix   = (known after apply)
       + location                      = "eastus"
       + mac_address                   = (known after apply)
       + name                          = "NIC-TF-AZ-CC2"
       + private_ip_address            = (known after apply)
       + private_ip_addresses          = (known after apply)
       + resource_group_name           = "RG-CC-AZ-TF-TEST"
       + virtual_machine_id            = (known after apply)

       + ip_configuration {
           + gateway_load_balancer_frontend_ip_configuration_id = (known after apply)
           + name                                               = "IP-TF-AZ-CC2"
           + primary                                            = (known after apply)
           + private_ip_address                                 = (known after apply)
           + private_ip_address_allocation                      = "Dynamic"
           + private_ip_address_version                         = "IPv4"
           + public_ip_address_id                               = (known after apply)
           + subnet_id                                          = (known after apply)
         }
     }

   # azurerm_network_security_group.CC-AZ-TF will be created
   + resource "azurerm_network_security_group" "CC-AZ-TF" {
       + id                  = (known after apply)
       + location            = "eastus"
       + name                = "NSG-TF-AZ-CC"
       + resource_group_name = "RG-CC-AZ-TF-TEST"
       + security_rule       = [
           + {
               + access                                     = "Allow"
               + description                                = ""
               + destination_address_prefix                 = "*"
               + destination_address_prefixes               = []
               + destination_application_security_group_ids = []
               + destination_port_range                     = "3389"
               + destination_port_ranges                    = []
               + direction                                  = "Inbound"
               + name                                       = "RDP"
               + priority                                   = 100
               + protocol                                   = "Tcp"
               + source_address_prefix                      = "*"
               + source_address_prefixes                    = []
               + source_application_security_group_ids      = []
               + source_port_range                          = "*"
               + source_port_ranges                         = []
             },
           + {
               + access                                     = "Allow"
               + description                                = ""
               + destination_address_prefix                 = "*"
               + destination_address_prefixes               = []
               + destination_application_security_group_ids = []
               + destination_port_range                     = "443"
               + destination_port_ranges                    = []
               + direction                                  = "Inbound"
               + name                                       = "HTTPS"
               + priority                                   = 130
               + protocol                                   = "Tcp"
               + source_address_prefix                      = "*"
               + source_address_prefixes                    = []
               + source_application_security_group_ids      = []
               + source_port_range                          = "*"
               + source_port_ranges                         = []
             },
           + {
               + access                                     = "Allow"
               + description                                = ""
               + destination_address_prefix                 = "*"
               + destination_address_prefixes               = []
               + destination_application_security_group_ids = []
               + destination_port_range                     = "5985"
               + destination_port_ranges                    = []
               + direction                                  = "Inbound"
               + name                                       = "WinRM"
               + priority                                   = 110
               + protocol                                   = "Tcp"
               + source_address_prefix                      = "*"
               + source_address_prefixes                    = []
               + source_application_security_group_ids      = []
               + source_port_range                          = "*"
               + source_port_ranges                         = []
             },
           + {
               + access                                     = "Allow"
               + description                                = ""
               + destination_address_prefix                 = "*"
               + destination_address_prefixes               = []
               + destination_application_security_group_ids = []
               + destination_port_range                     = "80"
               + destination_port_ranges                    = []
               + direction                                  = "Inbound"
               + name                                       = "HTTP"
               + priority                                   = 120
               + protocol                                   = "Tcp"
               + source_address_prefix                      = "*"
               + source_address_prefixes                    = []
               + source_application_security_group_ids      = []
               + source_port_range                          = "*"
               + source_port_ranges                         = []
             },
         ]
     }

   # azurerm_public_ip.CC-AZ-TF will be created
   + resource "azurerm_public_ip" "CC-AZ-TF" {
       + allocation_method       = "Static"
       + fqdn                    = (known after apply)
       + id                      = (known after apply)
       + idle_timeout_in_minutes = 4
       + ip_address              = (known after apply)
       + ip_version              = "IPv4"
       + location                = "eastus"
       + name                    = "PUBIP-TF-AZ-CC1"
       + resource_group_name     = "RG-CC-AZ-TF-TEST"
       + sku                     = "Basic"
       + sku_tier                = "Regional"
     }

   # azurerm_public_ip.CC-AZ-TF-DC will be created
   + resource "azurerm_public_ip" "CC-AZ-TF-DC" {
       + allocation_method       = "Static"
       + fqdn                    = (known after apply)
       + id                      = (known after apply)
       + idle_timeout_in_minutes = 4
       + ip_address              = (known after apply)
       + ip_version              = "IPv4"
       + location                = "eastus"
       + name                    = "PUBIP-TF-AZ-CC-DC"
       + resource_group_name     = "RG-CC-AZ-TF-TEST"
       + sku                     = "Basic"
       + sku_tier                = "Regional"
     }

   # azurerm_public_ip.CC-AZ-TF2 will be created
   + resource "azurerm_public_ip" "CC-AZ-TF2" {
       + allocation_method       = "Static"
       + fqdn                    = (known after apply)
       + id                      = (known after apply)
       + idle_timeout_in_minutes = 4
       + ip_address              = (known after apply)
       + ip_version              = "IPv4"
       + location                = "eastus"
       + name                    = "PUBIP-TF-AZ-CC2"
       + resource_group_name     = "RG-CC-AZ-TF-TEST"
       + sku                     = "Basic"
       + sku_tier                = "Regional"
     }

   # azurerm_storage_blob.CC-AZ-SB-DL will be created
   + resource "azurerm_storage_blob" "CC-AZ-SB-DL" {
       + access_tier            = (known after apply)
       + content_type           = "application/octet-stream"
       + id                     = (known after apply)
       + metadata               = (known after apply)
       + name                   = "download.ps1"
       + parallelism            = 8
       + size                   = 0
       + source                 = "download.ps1"
       + storage_account_name   = "saxxxxxxxxxxxxxx"
       + storage_container_name = "scsaxxxxxxxxxxxxxx"
       + type                   = "Block"
       + url                    = (known after apply)
     }

   # azurerm_storage_blob.CC-AZ-SB-Install will be created
   + resource "azurerm_storage_blob" "CC-AZ-SB-Install" {
       + access_tier            = (known after apply)
       + content_type           = "application/octet-stream"
       + id                     = (known after apply)
       + metadata               = (known after apply)
       + name                   = "InstallCWC.ps1"
       + parallelism            = 8
       + size                   = 0
       + source                 = "InstallCWC.ps1"
       + storage_account_name   = "saxxxxxxxxxxxxxx"
       + storage_container_name = "scsaxxxxxxxxxxxxxx"
       + type                   = "Block"
       + url                    = (known after apply)
     }

   # azurerm_storage_blob.CC-AZ-SB-InstallPreReqs-ps1 will be created
   + resource "azurerm_storage_blob" "CC-AZ-SB-InstallPreReqs-ps1" {
       + access_tier            = (known after apply)
       + content_type           = "application/octet-stream"
       + id                     = (known after apply)
       + metadata               = (known after apply)
       + name                   = "InstallPreReqs.ps1"
       + parallelism            = 8
       + size                   = 0
       + source                 = "InstallPreReqs.ps1"
       + storage_account_name   = "saxxxxxxxxxxxxxx"
       + storage_container_name = "scsaxxxxxxxxxxxxxx"
       + type                   = "Block"
       + url                    = (known after apply)
     }

   # azurerm_storage_blob.CC-AZ-SB-JSON will be created
   + resource "azurerm_storage_blob" "CC-AZ-SB-JSON" {
       + access_tier            = (known after apply)
       + content_type           = "application/octet-stream"
       + id                     = (known after apply)
       + metadata               = (known after apply)
       + name                   = "cwc.json"
       + parallelism            = 8
       + size                   = 0
       + source                 = "cwc.json"
       + storage_account_name   = "saxxxxxxxxxxxxxx"
       + storage_container_name = "scsaxxxxxxxxxxxxxx"
       + type                   = "Block"
       + url                    = (known after apply)
     }

   # azurerm_subnet.CC-AZ-TF will be created
   + resource "azurerm_subnet" "CC-AZ-TF" {
       + address_prefixes                               = [
           + "10.0.0.0/24",
         ]
       + enforce_private_link_endpoint_network_policies = false
       + enforce_private_link_service_network_policies  = false
       + id                                             = (known after apply)
       + name                                           = "SN-TF-AZ"
       + resource_group_name                            = "RG-CC-AZ-TF-TEST"
       + virtual_network_name                           = "VNET-TF-AZ-CC1"
     }

   # azurerm_subnet_network_security_group_association.CC-AZ-TF will be created
   + resource "azurerm_subnet_network_security_group_association" "CC-AZ-TF" {
       + id                        = (known after apply)
       + network_security_group_id = (known after apply)
       + subnet_id                 = (known after apply)
     }

   # azurerm_virtual_machine_extension.DownloadPSScriptToLocalVMAndExecuteItOnCC1 will be created
   + resource "azurerm_virtual_machine_extension" "DownloadPSScriptToLocalVMAndExecuteItOnCC1" {
       + id                   = (known after apply)
       + name                 = "DownloadPSScriptToLocalVMAndExecuteIt"
       + publisher            = "Microsoft.Compute"
       + settings             = <<-EOT
             {
                     "commandToExecute": "powershell.exe -ExecutionPolicy Unrestricted -Command \"Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force; Install-Module -Name PowerShellGet -Force;
  Invoke-WebRequest -Uri https://saxxxxxxxxxxxxxx.blob.core.windows.net/scsaxxxxxxxxxxxxxx/download.ps1 -OutFile c:/temp/download.ps1; c:/temp/download.ps1; c:/temp/InstallPreReqs.ps1\""
                 }
         EOT
       + type                 = "CustomScriptExtension"
       + type_handler_version = "1.9"
       + virtual_machine_id   = (known after apply)
     }

   # azurerm_virtual_machine_extension.DownloadPSScriptToLocalVMAndExecuteItOnCC2 will be created
   + resource "azurerm_virtual_machine_extension" "DownloadPSScriptToLocalVMAndExecuteItOnCC2" {
       + id                   = (known after apply)
       + name                 = "DownloadPSScriptToLocalVMAndExecuteIt"
       + publisher            = "Microsoft.Compute"
       + settings             = <<-EOT
             {
                     "commandToExecute": "powershell.exe -ExecutionPolicy Unrestricted -Command \"Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force; Install-Module -Name PowerShellGet -Force;
  Invoke-WebRequest -Uri https://saxxxxxxxxxxxxxx.blob.core.windows.net/scsaxxxxxxxxxxxxxx/download.ps1 -OutFile c:/temp/download.ps1; c:/temp/download.ps1; c:/temp/InstallPreReqs.ps1\""
                 }
         EOT
       + type                 = "CustomScriptExtension"
       + type_handler_version = "1.9"
       + virtual_machine_id   = (known after apply)
     }

   # azurerm_virtual_machine_extension.create-active-directory-forest will be created
   + resource "azurerm_virtual_machine_extension" "create-active-directory-forest" {
       + id                   = (known after apply)
       + name                 = "create-active-directory-forest"
       + publisher            = "Microsoft.Compute"
       + settings             = (sensitive value)
       + type                 = "CustomScriptExtension"
       + type_handler_version = "1.9"
       + virtual_machine_id   = (known after apply)
     }

   # azurerm_virtual_network.CC-AZ-TF will be created
   + resource "azurerm_virtual_network" "CC-AZ-TF" {
       + address_space       = [
           + "10.0.0.0/16",
         ]
       + dns_servers         = (known after apply)
       + guid                = (known after apply)
       + id                  = (known after apply)
       + location            = "eastus"
       + name                = "VNET-TF-AZ-CC1"
       + resource_group_name = "RG-CC-AZ-TF-TEST"
       + subnet              = (known after apply)
     }

   # azurerm_virtual_network_dns_servers.CC-AZ-TF will be created
   + resource "azurerm_virtual_network_dns_servers" "CC-AZ-TF" {
       + dns_servers        = [
           + "10.0.0.4",
         ]
       + id                 = (known after apply)
       + virtual_network_id = (known after apply)
     }

   # azurerm_windows_virtual_machine.CC-AZ-TF will be created
   + resource "azurerm_windows_virtual_machine" "CC-AZ-TF" {
       + admin_password             = (sensitive value)
       + admin_username             = "azxxxxxx"
       + allow_extension_operations = true
       + computer_name              = (known after apply)
       + enable_automatic_updates   = true
       + extensions_time_budget     = "PT1H30M"
       + hotpatching_enabled        = false
       + id                         = (known after apply)
       + location                   = "eastus"
       + max_bid_price              = -1
       + name                       = "VM-TF-CCCC1"
       + network_interface_ids      = (known after apply)
       + patch_mode                 = "AutomaticByOS"
       + 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        = "RG-CC-AZ-TF-TEST"
       + size                       = "Standard_D2s_v5"
       + virtual_machine_id         = (known after apply)

       + os_disk {
           + caching                   = "ReadWrite"
           + disk_size_gb              = (known after apply)
           + name                      = "OsDisk1"
           + storage_account_type      = "Standard_LRS"
           + write_accelerator_enabled = false
         }

       + source_image_reference {
           + offer     = "WindowsServer"
           + publisher = "MicrosoftWindowsServer"
           + sku       = "2022-datacenter-azure-edition"
           + version   = "latest"
         }
     }

   # azurerm_windows_virtual_machine.CC-AZ-TF-DC will be created
   + resource "azurerm_windows_virtual_machine" "CC-AZ-TF-DC" {
       + admin_password             = (sensitive value)
       + admin_username             = "azxxxxxx"
       + allow_extension_operations = true
       + computer_name              = (known after apply)
       + enable_automatic_updates   = true
       + extensions_time_budget     = "PT1H30M"
       + hotpatching_enabled        = false
       + id                         = (known after apply)
       + location                   = "eastus"
       + max_bid_price              = -1
       + name                       = "VM-TF-CC-DC"
       + network_interface_ids      = (known after apply)
       + patch_mode                 = "AutomaticByOS"
       + 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        = "RG-CC-AZ-TF-TEST"
       + size                       = "Standard_D2s_v5"
       + virtual_machine_id         = (known after apply)

       + os_disk {
           + caching                   = "ReadWrite"
           + disk_size_gb              = (known after apply)
           + name                      = "OsDisk-DC"
           + storage_account_type      = "Standard_LRS"
           + write_accelerator_enabled = false
         }

       + source_image_reference {
           + offer     = "WindowsServer"
           + publisher = "MicrosoftWindowsServer"
           + sku       = "2022-datacenter-azure-edition"
           + version   = "latest"
         }
     }

   # azurerm_windows_virtual_machine.CC-AZ-TF2 will be created
   + resource "azurerm_windows_virtual_machine" "CC-AZ-TF2" {
       + admin_password             = (sensitive value)
       + admin_username             = "azxxxxxx"
       + allow_extension_operations = true
       + computer_name              = (known after apply)
       + enable_automatic_updates   = true
       + extensions_time_budget     = "PT1H30M"
       + hotpatching_enabled        = false
       + id                         = (known after apply)
       + location                   = "eastus"
       + max_bid_price              = -1
       + name                       = "VM-TF-CCCC2"
       + network_interface_ids      = (known after apply)
       + patch_mode                 = "AutomaticByOS"
       + 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        = "RG-CC-AZ-TF-TEST"
       + size                       = "Standard_D2s_v5"
       + virtual_machine_id         = (known after apply)

       + os_disk {
           + caching                   = "ReadWrite"
           + disk_size_gb              = (known after apply)
           + name                      = "OsDisk2"
           + storage_account_type      = "Standard_LRS"
           + write_accelerator_enabled = false
         }

       + source_image_reference {
           + offer     = "WindowsServer"
           + publisher = "MicrosoftWindowsServer"
           + sku       = "2022-datacenter-azure-edition"
           + version   = "latest"
         }
     }

   # 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             = "./cwc.json"
       + id                   = (known after apply)
     }

   # local_file.Download-ps1 will be created
   + resource "local_file" "Download-ps1" {
       + content              = <<-EOT
             Install-Module -Name Az -Repository PSGallery -Force
             Start-Sleep -Seconds 180
             $cont = New-AzStorageContext -StorageAccountName saxxxxxxxxxxxxxx -Anonymous -Protocol https
             Get-AzStorageBlobContent -blob cwcconnector.exe -container scsaxxxxxxxxxxxxxx -Context $cont -destination c:/temp
             Get-AzStorageBlobContent -blob cwc.json -container scsaxxxxxxxxxxxxxx -Context $cont -destination c:/temp
             Get-AzStorageBlobContent -blob InstallPreReqs.ps1 -container scsaxxxxxxxxxxxxxx -Context $cont -destination c:/temp
             Get-AzStorageBlobContent -blob InstallCWC.ps1 -container scsaxxxxxxxxxxxxxx -Context $cont -destination c:/temp
             Get-AzStorageBlobContent -blob CitrixPoshSdk.exe -container scsaxxxxxxxxxxxxxx -Context $cont -destination c:/temp
         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             = "./download.ps1"
       + id                   = (known after apply)
     }

   # local_file.InstallCWC-ps1 will be created
   + resource "local_file" "InstallCWC-ps1" {
       + content              = <<-EOT
             $CtxPoSHSDK = Get-WmiObject -Class Win32_Product | Where-Object{$_.Name -Like "Citrix*"}
             if ($CtxPoSHSDK -ne $null) {
                $CtxPoSHSDK.Uninstall()
             }
             cd /temp
             ./CWCConnector.exe /q /ParametersFilePath:c:/temp/cwc.json
         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             = "./InstallCWC.ps1"
       + id                   = (known after apply)
     }

   # local_file.InstallPreReqs-ps1 will be created
   + resource "local_file" "InstallPreReqs-ps1" {
       + 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             = "./InstallPreReqs.ps1"
       + id                   = (known after apply)
     }

   # random_uuid.IDforCCRL will be created
   + resource "random_uuid" "IDforCCRL" {
       + id     = (known after apply)
       + result = (known after apply)
     }

   # restapi_object.CreateRL will be created
   + resource "restapi_object" "CreateRL" {
       + api_data        = (known after apply)
       + api_response    = (known after apply)
       + create_response = (known after apply)
       + data            = (known after apply)
       + id              = (known after apply)
       + path            = "/resourcelocations"
     }

   # terraform_data.Download-CWC-Installer will be created
   + resource "terraform_data" "Download-CWC-Installer" {
       + id = (known after apply)
     }

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

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

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

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

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

 Plan: 34 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\__CConAzureCreation> 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_managed_disk.CreateManagedDiskFromUploadedVHD will be created
   + resource "azurerm_managed_disk" "CreateManagedDiskFromUploadedVHD" {
       + create_option                 = "Import"
       + disk_iops_read_only           = (known after apply)
       + disk_iops_read_write          = (known after apply)
       + disk_mbps_read_only           = (known after apply)
       + disk_mbps_read_write          = (known after apply)
       + disk_size_gb                  = (known after apply)
       + hyper_v_generation            = "V2"
       + id                            = (known after apply)
       + location                      = "eastus"
       + logical_sector_size           = (known after apply)
       + max_shares                    = (known after apply)
       + name                          = "w11-min.vhd"
       + os_type                       = "Windows"
       + public_network_access_enabled = true
       + resource_group_name           = "RG-CC-AZ-TF-TEST"
       + source_uri                    = "https://saxxxxxxxxxxxxxx.blob.core.windows.net/scsaxxxxxxxxxxxxxx/w11-min.vhd"
       + storage_account_id            = "/subscriptions/58xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/RG-CC-AZ-TF-TEST/providers/Microsoft.Storage/storageAccounts/saxxxxxxxxxxxxxx"
       + storage_account_type          = "Standard_LRS"
       + tier                          = (known after apply)
     }

   # azurerm_network_interface.CC-AZ-TF will be created
   + resource "azurerm_network_interface" "CC-AZ-TF" {
       + applied_dns_servers           = (known after apply)
       + dns_servers                   = (known after apply)
       + enable_accelerated_networking = false
       + enable_ip_forwarding          = false
       + id                            = (known after apply)
       + internal_dns_name_label       = (known after apply)
       + internal_domain_name_suffix   = (known after apply)
       + location                      = "eastus"
       + mac_address                   = (known after apply)
       + name                          = "NIC-TF-AZ-CC1"
       + private_ip_address            = (known after apply)
       + private_ip_addresses          = (known after apply)
       + resource_group_name           = "RG-CC-AZ-TF-TEST"
       + virtual_machine_id            = (known after apply)

       + ip_configuration {
           + gateway_load_balancer_frontend_ip_configuration_id = (known after apply)
           + name                                               = "IP-TF-AZ-CC1"
           + primary                                            = (known after apply)
           + private_ip_address                                 = (known after apply)
           + private_ip_address_allocation                      = "Dynamic"
           + private_ip_address_version                         = "IPv4"
           + public_ip_address_id                               = (known after apply)
           + subnet_id                                          = (known after apply)
         }
     }

   # azurerm_network_interface.CC-AZ-TF-DC will be created
   + resource "azurerm_network_interface" "CC-AZ-TF-DC" {
       + applied_dns_servers           = (known after apply)
       + dns_servers                   = [
           + "10.0.0.4",
         ]
       + enable_accelerated_networking = false
       + enable_ip_forwarding          = false
       + id                            = (known after apply)
       + internal_dns_name_label       = (known after apply)
       + internal_domain_name_suffix   = (known after apply)
       + location                      = "eastus"
       + mac_address                   = (known after apply)
       + name                          = "NIC-TF-AZ-DC"
       + private_ip_address            = (known after apply)
       + private_ip_addresses          = (known after apply)
       + resource_group_name           = "RG-CC-AZ-TF-TEST"
       + virtual_machine_id            = (known after apply)

       + ip_configuration {
           + gateway_load_balancer_frontend_ip_configuration_id = (known after apply)
           + name                                               = "IP-TF-AZ-DC"
           + primary                                            = (known after apply)
           + private_ip_address                                 = (known after apply)
           + private_ip_address_allocation                      = "Dynamic"
           + private_ip_address_version                         = "IPv4"
           + public_ip_address_id                               = (known after apply)
           + subnet_id                                          = (known after apply)
         }
     }

   # azurerm_network_interface.CC-AZ-TF2 will be created
   + resource "azurerm_network_interface" "CC-AZ-TF2" {
       + applied_dns_servers           = (known after apply)
       + dns_servers                   = (known after apply)
       + enable_accelerated_networking = false
       + enable_ip_forwarding          = false
       + id                            = (known after apply)
       + internal_dns_name_label       = (known after apply)
       + internal_domain_name_suffix   = (known after apply)
       + location                      = "eastus"
       + mac_address                   = (known after apply)
       + name                          = "NIC-TF-AZ-CC2"
       + private_ip_address            = (known after apply)
       + private_ip_addresses          = (known after apply)
       + resource_group_name           = "RG-CC-AZ-TF-TEST"
       + virtual_machine_id            = (known after apply)

       + ip_configuration {
           + gateway_load_balancer_frontend_ip_configuration_id = (known after apply)
           + name                                               = "IP-TF-AZ-CC2"
           + primary                                            = (known after apply)
           + private_ip_address                                 = (known after apply)
           + private_ip_address_allocation                      = "Dynamic"
           + private_ip_address_version                         = "IPv4"
           + public_ip_address_id                               = (known after apply)
           + subnet_id                                          = (known after apply)
         }
     }

   # azurerm_network_security_group.CC-AZ-TF will be created
   + resource "azurerm_network_security_group" "CC-AZ-TF" {
       + id                  = (known after apply)
       + location            = "eastus"
       + name                = "NSG-TF-AZ-CC"
       + resource_group_name = "RG-CC-AZ-TF-TEST"
       + security_rule       = [
           + {
               + access                                     = "Allow"
               + description                                = ""
               + destination_address_prefix                 = "*"
               + destination_address_prefixes               = []
               + destination_application_security_group_ids = []
               + destination_port_range                     = "3389"
               + destination_port_ranges                    = []
               + direction                                  = "Inbound"
               + name                                       = "RDP"
               + priority                                   = 100
               + protocol                                   = "Tcp"
               + source_address_prefix                      = "*"
               + source_address_prefixes                    = []
               + source_application_security_group_ids      = []
               + source_port_range                          = "*"
               + source_port_ranges                         = []
             },
           + {
               + access                                     = "Allow"
               + description                                = ""
               + destination_address_prefix                 = "*"
               + destination_address_prefixes               = []
               + destination_application_security_group_ids = []
               + destination_port_range                     = "443"
               + destination_port_ranges                    = []
               + direction                                  = "Inbound"
               + name                                       = "HTTPS"
               + priority                                   = 130
               + protocol                                   = "Tcp"
               + source_address_prefix                      = "*"
               + source_address_prefixes                    = []
               + source_application_security_group_ids      = []
               + source_port_range                          = "*"
               + source_port_ranges                         = []
             },
           + {
               + access                                     = "Allow"
               + description                                = ""
               + destination_address_prefix                 = "*"
               + destination_address_prefixes               = []
               + destination_application_security_group_ids = []
               + destination_port_range                     = "5985"
               + destination_port_ranges                    = []
               + direction                                  = "Inbound"
               + name                                       = "WinRM"
               + priority                                   = 110
               + protocol                                   = "Tcp"
               + source_address_prefix                      = "*"
               + source_address_prefixes                    = []
               + source_application_security_group_ids      = []
               + source_port_range                          = "*"
               + source_port_ranges                         = []
             },
           + {
               + access                                     = "Allow"
               + description                                = ""
               + destination_address_prefix                 = "*"
               + destination_address_prefixes               = []
               + destination_application_security_group_ids = []
               + destination_port_range                     = "80"
               + destination_port_ranges                    = []
               + direction                                  = "Inbound"
               + name                                       = "HTTP"
               + priority                                   = 120
               + protocol                                   = "Tcp"
               + source_address_prefix                      = "*"
               + source_address_prefixes                    = []
               + source_application_security_group_ids      = []
               + source_port_range                          = "*"
               + source_port_ranges                         = []
             },
         ]
     }

   # azurerm_public_ip.CC-AZ-TF will be created
   + resource "azurerm_public_ip" "CC-AZ-TF" {
       + allocation_method       = "Static"
       + fqdn                    = (known after apply)
       + id                      = (known after apply)
       + idle_timeout_in_minutes = 4
       + ip_address              = (known after apply)
       + ip_version              = "IPv4"
       + location                = "eastus"
       + name                    = "PUBIP-TF-AZ-CC1"
       + resource_group_name     = "RG-CC-AZ-TF-TEST"
       + sku                     = "Basic"
       + sku_tier                = "Regional"
     }

   # azurerm_public_ip.CC-AZ-TF-DC will be created
   + resource "azurerm_public_ip" "CC-AZ-TF-DC" {
       + allocation_method       = "Static"
       + fqdn                    = (known after apply)
       + id                      = (known after apply)
       + idle_timeout_in_minutes = 4
       + ip_address              = (known after apply)
       + ip_version              = "IPv4"
       + location                = "eastus"
       + name                    = "PUBIP-TF-AZ-CC-DC"
       + resource_group_name     = "RG-CC-AZ-TF-TEST"
       + sku                     = "Basic"
       + sku_tier                = "Regional"
     }

   # azurerm_public_ip.CC-AZ-TF2 will be created
   + resource "azurerm_public_ip" "CC-AZ-TF2" {
       + allocation_method       = "Static"
       + fqdn                    = (known after apply)
       + id                      = (known after apply)
       + idle_timeout_in_minutes = 4
       + ip_address              = (known after apply)
       + ip_version              = "IPv4"
       + location                = "eastus"
       + name                    = "PUBIP-TF-AZ-CC2"
       + resource_group_name     = "RG-CC-AZ-TF-TEST"
       + sku                     = "Basic"
       + sku_tier                = "Regional"
     }

   # azurerm_storage_blob.CC-AZ-SB-DL will be created
   + resource "azurerm_storage_blob" "CC-AZ-SB-DL" {
       + access_tier            = (known after apply)
       + content_type           = "application/octet-stream"
       + id                     = (known after apply)
       + metadata               = (known after apply)
       + name                   = "download.ps1"
       + parallelism            = 8
       + size                   = 0
       + source                 = "download.ps1"
       + storage_account_name   = "saxxxxxxxxxxxxxx"
       + storage_container_name = "scsaxxxxxxxxxxxxxx"
       + type                   = "Block"
       + url                    = (known after apply)
     }

   # azurerm_storage_blob.CC-AZ-SB-Install will be created
   + resource "azurerm_storage_blob" "CC-AZ-SB-Install" {
       + access_tier            = (known after apply)
       + content_type           = "application/octet-stream"
       + id                     = (known after apply)
       + metadata               = (known after apply)
       + name                   = "InstallCWC.ps1"
       + parallelism            = 8
       + size                   = 0
       + source                 = "InstallCWC.ps1"
       + storage_account_name   = "saxxxxxxxxxxxxxx"
       + storage_container_name = "scsaxxxxxxxxxxxxxx"
       + type                   = "Block"
       + url                    = (known after apply)
     }

   # azurerm_storage_blob.CC-AZ-SB-InstallPreReqs-ps1 will be created
   + resource "azurerm_storage_blob" "CC-AZ-SB-InstallPreReqs-ps1" {
       + access_tier            = (known after apply)
       + content_type           = "application/octet-stream"
       + id                     = (known after apply)
       + metadata               = (known after apply)
       + name                   = "InstallPreReqs.ps1"
       + parallelism            = 8
       + size                   = 0
       + source                 = "InstallPreReqs.ps1"
       + storage_account_name   = "saxxxxxxxxxxxxxx"
       + storage_container_name = "scsaxxxxxxxxxxxxxx"
       + type                   = "Block"
       + url                    = (known after apply)
     }

   # azurerm_storage_blob.CC-AZ-SB-JSON will be created
   + resource "azurerm_storage_blob" "CC-AZ-SB-JSON" {
       + access_tier            = (known after apply)
       + content_type           = "application/octet-stream"
       + id                     = (known after apply)
       + metadata               = (known after apply)
       + name                   = "cwc.json"
       + parallelism            = 8
       + size                   = 0
       + source                 = "cwc.json"
       + storage_account_name   = "saxxxxxxxxxxxxxx"
       + storage_container_name = "scsaxxxxxxxxxxxxxx"
       + type                   = "Block"
       + url                    = (known after apply)
     }

   # azurerm_subnet.CC-AZ-TF will be created
   + resource "azurerm_subnet" "CC-AZ-TF" {
       + address_prefixes                               = [
           + "10.0.0.0/24",
         ]
       + enforce_private_link_endpoint_network_policies = false
       + enforce_private_link_service_network_policies  = false
       + id                                             = (known after apply)
       + name                                           = "SN-TF-AZ"
       + resource_group_name                            = "RG-CC-AZ-TF-TEST"
       + virtual_network_name                           = "VNET-TF-AZ-CC1"
     }

   # azurerm_subnet_network_security_group_association.CC-AZ-TF will be created
   + resource "azurerm_subnet_network_security_group_association" "CC-AZ-TF" {
       + id                        = (known after apply)
       + network_security_group_id = (known after apply)
       + subnet_id                 = (known after apply)
     }

   # azurerm_virtual_machine_extension.DownloadPSScriptToLocalVMAndExecuteItOnCC1 will be created
   + resource "azurerm_virtual_machine_extension" "DownloadPSScriptToLocalVMAndExecuteItOnCC1" {
       + id                   = (known after apply)
       + name                 = "DownloadPSScriptToLocalVMAndExecuteIt"
       + publisher            = "Microsoft.Compute"
       + settings             = <<-EOT
             {
                     "commandToExecute": "powershell.exe -ExecutionPolicy Unrestricted -Command \"Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force; Install-Module -Name PowerShellGet -Force;
  Invoke-WebRequest -Uri https://saxxxxxxxxxxxxxx.blob.core.windows.net/scsaxxxxxxxxxxxxxx/download.ps1 -OutFile c:/temp/download.ps1; c:/temp/download.ps1; c:/temp/InstallPreReqs.ps1\""
                 }
         EOT
       + type                 = "CustomScriptExtension"
       + type_handler_version = "1.9"
       + virtual_machine_id   = (known after apply)
     }

   # azurerm_virtual_machine_extension.DownloadPSScriptToLocalVMAndExecuteItOnCC2 will be created
   + resource "azurerm_virtual_machine_extension" "DownloadPSScriptToLocalVMAndExecuteItOnCC2" {
       + id                   = (known after apply)
       + name                 = "DownloadPSScriptToLocalVMAndExecuteIt"
       + publisher            = "Microsoft.Compute"
       + settings             = <<-EOT
             {
                     "commandToExecute": "powershell.exe -ExecutionPolicy Unrestricted -Command \"Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force; Install-Module -Name PowerShellGet -Force;
  Invoke-WebRequest -Uri https://saxxxxxxxxxxxxxx.blob.core.windows.net/scsaxxxxxxxxxxxxxx/download.ps1 -OutFile c:/temp/download.ps1; c:/temp/download.ps1; c:/temp/InstallPreReqs.ps1\""
                 }
         EOT
       + type                 = "CustomScriptExtension"
       + type_handler_version = "1.9"
       + virtual_machine_id   = (known after apply)
     }

   # azurerm_virtual_machine_extension.create-active-directory-forest will be created
   + resource "azurerm_virtual_machine_extension" "create-active-directory-forest" {
       + id                   = (known after apply)
       + name                 = "create-active-directory-forest"
       + publisher            = "Microsoft.Compute"
       + settings             = (sensitive value)
       + type                 = "CustomScriptExtension"
       + type_handler_version = "1.9"
       + virtual_machine_id   = (known after apply)
     }

   # azurerm_virtual_network.CC-AZ-TF will be created
   + resource "azurerm_virtual_network" "CC-AZ-TF" {
       + address_space       = [
           + "10.0.0.0/16",
         ]
       + dns_servers         = (known after apply)
       + guid                = (known after apply)
       + id                  = (known after apply)
       + location            = "eastus"
       + name                = "VNET-TF-AZ-CC1"
       + resource_group_name = "RG-CC-AZ-TF-TEST"
       + subnet              = (known after apply)
     }

   # azurerm_virtual_network_dns_servers.CC-AZ-TF will be created
   + resource "azurerm_virtual_network_dns_servers" "CC-AZ-TF" {
       + dns_servers        = [
           + "10.0.0.4",
         ]
       + id                 = (known after apply)
       + virtual_network_id = (known after apply)
     }

   # azurerm_windows_virtual_machine.CC-AZ-TF will be created
   + resource "azurerm_windows_virtual_machine" "CC-AZ-TF" {
       + admin_password             = (sensitive value)
       + admin_username             = "azxxxxxx"
       + allow_extension_operations = true
       + computer_name              = (known after apply)
       + enable_automatic_updates   = true
       + extensions_time_budget     = "PT1H30M"
       + hotpatching_enabled        = false
       + id                         = (known after apply)
       + location                   = "eastus"
       + max_bid_price              = -1
       + name                       = "VM-TF-CCCC1"
       + network_interface_ids      = (known after apply)
       + patch_mode                 = "AutomaticByOS"
       + 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        = "RG-CC-AZ-TF-TEST"
       + size                       = "Standard_D2s_v5"
       + virtual_machine_id         = (known after apply)

       + os_disk {
           + caching                   = "ReadWrite"
           + disk_size_gb              = (known after apply)
           + name                      = "OsDisk1"
           + storage_account_type      = "Standard_LRS"
           + write_accelerator_enabled = false
         }

       + source_image_reference {
           + offer     = "WindowsServer"
           + publisher = "MicrosoftWindowsServer"
           + sku       = "2022-datacenter-azure-edition"
           + version   = "latest"
         }
     }

   # azurerm_windows_virtual_machine.CC-AZ-TF-DC will be created
   + resource "azurerm_windows_virtual_machine" "CC-AZ-TF-DC" {
       + admin_password             = (sensitive value)
       + admin_username             = "azxxxxxx"
       + allow_extension_operations = true
       + computer_name              = (known after apply)
       + enable_automatic_updates   = true
       + extensions_time_budget     = "PT1H30M"
       + hotpatching_enabled        = false
       + id                         = (known after apply)
       + location                   = "eastus"
       + max_bid_price              = -1
       + name                       = "VM-TF-CC-DC"
       + network_interface_ids      = (known after apply)
       + patch_mode                 = "AutomaticByOS"
       + 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        = "RG-CC-AZ-TF-TEST"
       + size                       = "Standard_D2s_v5"
       + virtual_machine_id         = (known after apply)

       + os_disk {
           + caching                   = "ReadWrite"
           + disk_size_gb              = (known after apply)
           + name                      = "OsDisk-DC"
           + storage_account_type      = "Standard_LRS"
           + write_accelerator_enabled = false
         }

       + source_image_reference {
           + offer     = "WindowsServer"
           + publisher = "MicrosoftWindowsServer"
           + sku       = "2022-datacenter-azure-edition"
           + version   = "latest"
         }
     }

   # azurerm_windows_virtual_machine.CC-AZ-TF2 will be created
   + resource "azurerm_windows_virtual_machine" "CC-AZ-TF2" {
       + admin_password             = (sensitive value)
       + admin_username             = "azxxxxxx"
       + allow_extension_operations = true
       + computer_name              = (known after apply)
       + enable_automatic_updates   = true
       + extensions_time_budget     = "PT1H30M"
       + hotpatching_enabled        = false
       + id                         = (known after apply)
       + location                   = "eastus"
       + max_bid_price              = -1
       + name                       = "VM-TF-CCCC2"
       + network_interface_ids      = (known after apply)
       + patch_mode                 = "AutomaticByOS"
       + 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        = "RG-CC-AZ-TF-TEST"
       + size                       = "Standard_D2s_v5"
       + virtual_machine_id         = (known after apply)

       + os_disk {
           + caching                   = "ReadWrite"
           + disk_size_gb              = (known after apply)
           + name                      = "OsDisk2"
           + storage_account_type      = "Standard_LRS"
           + write_accelerator_enabled = false
         }

       + source_image_reference {
           + offer     = "WindowsServer"
           + publisher = "MicrosoftWindowsServer"
           + sku       = "2022-datacenter-azure-edition"
           + version   = "latest"
         }
     }

   # 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             = "./cwc.json"
       + id                   = (known after apply)
     }

   # local_file.Download-ps1 will be created
   + resource "local_file" "Download-ps1" {
       + content              = <<-EOT
             Install-Module -Name Az -Repository PSGallery -Force
             Start-Sleep -Seconds 180
             $cont = New-AzStorageContext -StorageAccountName saxxxxxxxxxxxxxx -Anonymous -Protocol https
             Get-AzStorageBlobContent -blob cwcconnector.exe -container scsaxxxxxxxxxxxxxx -Context $cont -destination c:/temp
             Get-AzStorageBlobContent -blob cwc.json -container scsaxxxxxxxxxxxxxx -Context $cont -destination c:/temp
             Get-AzStorageBlobContent -blob InstallPreReqs.ps1 -container scsaxxxxxxxxxxxxxx -Context $cont -destination c:/temp
             Get-AzStorageBlobContent -blob InstallCWC.ps1 -container scsaxxxxxxxxxxxxxx -Context $cont -destination c:/temp
             Get-AzStorageBlobContent -blob CitrixPoshSdk.exe -container scsaxxxxxxxxxxxxxx -Context $cont -destination c:/temp
         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             = "./download.ps1"
       + id                   = (known after apply)
     }

   # local_file.InstallCWC-ps1 will be created
   + resource "local_file" "InstallCWC-ps1" {
       + content              = <<-EOT
             $CtxPoSHSDK = Get-WmiObject -Class Win32_Product | Where-Object{$_.Name -Like "Citrix*"}
             if ($CtxPoSHSDK -ne $null) {
                $CtxPoSHSDK.Uninstall()
             }
             cd /temp
             ./CWCConnector.exe /q /ParametersFilePath:c:/temp/cwc.json
         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             = "./InstallCWC.ps1"
       + id                   = (known after apply)
     }

   # local_file.InstallPreReqs-ps1 will be created
   + resource "local_file" "InstallPreReqs-ps1" {
       + 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             = "./InstallPreReqs.ps1"
       + id                   = (known after apply)
     }

   # random_uuid.IDforCCRL will be created
   + resource "random_uuid" "IDforCCRL" {
       + id     = (known after apply)
       + result = (known after apply)
     }

   # restapi_object.CreateRL will be created
   + resource "restapi_object" "CreateRL" {
       + api_data        = (known after apply)
       + api_response    = (known after apply)
       + create_response = (known after apply)
       + data            = (known after apply)
       + id              = (known after apply)
       + path            = "/resourcelocations"
     }

   # terraform_data.Download-CWC-Installer will be created
   + resource "terraform_data" "Download-CWC-Installer" {
       + id = (known after apply)
     }

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

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

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

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

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

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

 terraform_data.Download-CWC-Installer: Creating...
 terraform_data.Download-CWC-Installer: Provisioning with 'local-exec'...
 random_uuid.IDforCCRL: Creating...
 terraform_data.Download-CWC-Installer (local-exec): (output suppressed due to sensitive value in config)
 time_sleep.wait_3_minutes: Creating...
 time_sleep.wait_4_minutes: Creating...
 random_uuid.IDforCCRL: Creation complete after 0s [id=00adb956-95b6-d02b-ac59-dc0ea0be376f]
 time_sleep.wait_5_minutes: Creating...
 time_sleep.wait_1_minutes: Creating...
 local_file.InstallPreReqs-ps1: Creating...
 local_file.Download-ps1: Creating...
 local_file.InstallCWC-ps1: Creating...
 local_file.InstallPreReqs-ps1: Creation complete after 0s [id=899f6d4582959d5a83dfce71e589207e4030aae6]
 local_file.InstallCWC-ps1: Creation complete after 0s [id=a6adfaeb00b7a0bb9d5951ba144b4e16b62b8ce9]
 local_file.Download-ps1: Creation complete after 0s [id=0f2bd28b7dd7c10e07001e8f56d9811bf0d36430]
 restapi_object.CreateRL: Creating...
 restapi_object.CreateRL: Creation complete after 0s [id=00adb956-95b6-d02b-ac59-dc0ea0be376f]
 local_file.CWC-Configuration: Creating...
 local_file.CWC-Configuration: Creation complete after 0s [id=0257d2b92f197fa4d043b6cd4c4959be284dddc2]
 terraform_data.Download-CWC-Installer: Creation complete after 2s [id=0c240015-d833-e198-88ac-49ec756319bf]
 azurerm_public_ip.CC-AZ-TF-DC: Creating...
 azurerm_public_ip.CC-AZ-TF2: Creating...
 azurerm_public_ip.CC-AZ-TF: Creating...
 azurerm_managed_disk.CreateManagedDiskFromUploadedVHD: Creating...
 azurerm_virtual_network.CC-AZ-TF: Creating...
 azurerm_network_security_group.CC-AZ-TF: Creating...
 azurerm_network_security_group.CC-AZ-TF: Creation complete after 3s [id=/subscriptions/58xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/RG-CC-AZ-TF-TEST/providers/Microsoft.Network/networkSecurityGroups/NSG
 -TF-AZ-CC]
 azurerm_public_ip.CC-AZ-TF2: Creation complete after 3s [id=/subscriptions/58xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/RG-CC-AZ-TF-TEST/providers/Microsoft.Network/publicIPAddresses/PUBIP-TF-AZ-CC2]
 azurerm_public_ip.CC-AZ-TF-DC: Creation complete after 3s [id=/subscriptions/58xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/RG-CC-AZ-TF-TEST/providers/Microsoft.Network/publicIPAddresses/PUBIP-TF-AZ-CC-DC
 ]
 azurerm_public_ip.CC-AZ-TF: Creation complete after 3s [id=/subscriptions/58xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/RG-CC-AZ-TF-TEST/providers/Microsoft.Network/publicIPAddresses/PUBIP-TF-AZ-CC1]
 azurerm_managed_disk.CreateManagedDiskFromUploadedVHD: Creation complete after 5s [id=/subscriptions/58xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/RG-CC-AZ-TF-TEST/providers/Microsoft.Compute/disks/w11-m
 in.vhd]
 azurerm_virtual_network.CC-AZ-TF: Creation complete after 6s [id=/subscriptions/58xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/RG-CC-AZ-TF-TEST/providers/Microsoft.Network/virtualNetworks/VNET-TF-AZ-CC1]
 azurerm_virtual_network_dns_servers.CC-AZ-TF: Creating...
 azurerm_subnet.CC-AZ-TF: Creating...
 time_sleep.wait_5_minutes: Still creating... [10s elapsed]
 time_sleep.wait_3_minutes: Still creating... [10s elapsed]
 time_sleep.wait_4_minutes: Still creating... [10s elapsed]
 time_sleep.wait_1_minutes: Still creating... [10s elapsed]
 azurerm_virtual_network_dns_servers.CC-AZ-TF: Creation complete after 6s [id=/subscriptions/58xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/RG-CC-AZ-TF-TEST/providers/Microsoft.Network/virtualNetworks/VNET
 -TF-AZ-CC1/dnsServers/default]
 azurerm_subnet.CC-AZ-TF: Still creating... [10s elapsed]
 time_sleep.wait_1_minutes: Still creating... [20s elapsed]
 time_sleep.wait_5_minutes: Still creating... [20s elapsed]
 time_sleep.wait_4_minutes: Still creating... [20s elapsed]
 time_sleep.wait_3_minutes: Still creating... [20s elapsed]
 azurerm_subnet.CC-AZ-TF: Creation complete after 12s [id=/subscriptions/58xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/RG-CC-AZ-TF-TEST/providers/Microsoft.Network/virtualNetworks/VNET-TF-AZ-CC1/subnets/S
 N-TF-AZ]
 azurerm_subnet_network_security_group_association.CC-AZ-TF: Creating...
 azurerm_network_interface.CC-AZ-TF-DC: Creating...
 azurerm_subnet_network_security_group_association.CC-AZ-TF: Creation complete after 6s [id=/subscriptions/58xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/RG-CC-AZ-TF-TEST/providers/Microsoft.Network/virtua
 lNetworks/VNET-TF-AZ-CC1/subnets/SN-TF-AZ]
 azurerm_network_interface.CC-AZ-TF-DC: Creation complete after 8s [id=/subscriptions/58xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/RG-CC-AZ-TF-TEST/providers/Microsoft.Network/networkInterfaces/NIC-TF-AZ
 -DC]
 azurerm_network_interface.CC-AZ-TF2: Creating...
 azurerm_network_interface.CC-AZ-TF: Creating...
 azurerm_windows_virtual_machine.CC-AZ-TF-DC: Creating...
 time_sleep.wait_3_minutes: Still creating... [30s elapsed]
 time_sleep.wait_4_minutes: Still creating... [30s elapsed]
 time_sleep.wait_5_minutes: Still creating... [30s elapsed]
 time_sleep.wait_1_minutes: Still creating... [30s elapsed]
 azurerm_network_interface.CC-AZ-TF: Creation complete after 2s [id=/subscriptions/58xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/RG-CC-AZ-TF-TEST/providers/Microsoft.Network/networkInterfaces/NIC-TF-AZ-CC
 1]
 azurerm_windows_virtual_machine.CC-AZ-TF: Creating...
 azurerm_network_interface.CC-AZ-TF2: Creation complete after 3s [id=/subscriptions/58xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/RG-CC-AZ-TF-TEST/providers/Microsoft.Network/networkInterfaces/NIC-TF-AZ-C
 C2]
 azurerm_windows_virtual_machine.CC-AZ-TF2: Creating...
 azurerm_windows_virtual_machine.CC-AZ-TF-DC: Still creating... [10s elapsed]
 time_sleep.wait_1_minutes: Still creating... [40s elapsed]
 time_sleep.wait_4_minutes: Still creating... [40s elapsed]
 time_sleep.wait_5_minutes: Still creating... [40s elapsed]
 time_sleep.wait_3_minutes: Still creating... [40s elapsed]
 azurerm_windows_virtual_machine.CC-AZ-TF: Still creating... [10s elapsed]
 azurerm_windows_virtual_machine.CC-AZ-TF2: Still creating... [10s elapsed]
 azurerm_windows_virtual_machine.CC-AZ-TF-DC: Still creating... [20s elapsed]
 time_sleep.wait_3_minutes: Still creating... [50s elapsed]
 time_sleep.wait_5_minutes: Still creating... [50s elapsed]
 time_sleep.wait_4_minutes: Still creating... [50s elapsed]
 time_sleep.wait_1_minutes: Still creating... [50s elapsed]
 azurerm_windows_virtual_machine.CC-AZ-TF: Still creating... [20s elapsed]
 azurerm_windows_virtual_machine.CC-AZ-TF2: Creation complete after 19s [id=/subscriptions/58xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/RG-CC-AZ-TF-TEST/providers/Microsoft.Compute/virtualMachines/VM-TF-
 CCCC2]
 azurerm_windows_virtual_machine.CC-AZ-TF-DC: Creation complete after 24s [id=/subscriptions/58xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/RG-CC-AZ-TF-TEST/providers/Microsoft.Compute/virtualMachines/VM-T
 F-CC-DC]
 azurerm_windows_virtual_machine.CC-AZ-TF: Creation complete after 23s [id=/subscriptions/58xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/RG-CC-AZ-TF-TEST/providers/Microsoft.Compute/virtualMachines/VM-TF-C
 CCC1]
 time_sleep.wait_1_minutes: Creation complete after 1m0s [id=2023-12-21T17:09:00Z]
 azurerm_storage_blob.CC-AZ-SB-Install: Creating...
 azurerm_storage_blob.CC-AZ-SB-DL: Creating...
 azurerm_storage_blob.CC-AZ-SB-JSON: Creating...
 azurerm_storage_blob.CC-AZ-SB-InstallPreReqs-ps1: Creating...
 time_sleep.wait_3_minutes: Still creating... [1m0s elapsed]
 time_sleep.wait_4_minutes: Still creating... [1m0s elapsed]
 time_sleep.wait_5_minutes: Still creating... [1m0s elapsed]
 azurerm_storage_blob.CC-AZ-SB-DL: Creation complete after 4s [id=https://saxxxxxxxxxxxxx.blob.core.windows.net/scsaxxxxxxxxxxxxx/download.ps1]
 azurerm_storage_blob.CC-AZ-SB-Install: Creation complete after 4s [id=https://saxxxxxxxxxxxxx.blob.core.windows.net/scsaxxxxxxxxxxxxx/InstallCWC.ps1]
 azurerm_storage_blob.CC-AZ-SB-InstallPreReqs-ps1: Creation complete after 4s [id=https://saxxxxxxxxxxxxx.blob.core.windows.net/scsaxxxxxxxxxxxxx/InstallPreReqs.ps1]
 azurerm_storage_blob.CC-AZ-SB-JSON: Creation complete after 4s [id=https://saxxxxxxxxxxxxx.blob.core.windows.net/scsaxxxxxxxxxxxxx/cwc.json]
 time_sleep.wait_4_minutes: Still creating... [1m10s elapsed]
… … … 
 time_sleep.wait_5_minutes: Still creating... [4m50s elapsed]
 time_sleep.wait_5_minutes: Creation complete after 5m0s [id=2023-12-21T17:13:00Z]
 azurerm_virtual_machine_extension.create-active-directory-forest: Creating...
 azurerm_virtual_machine_extension.create-active-directory-forest: Still creating... [10s elapsed]
 azurerm_virtual_machine_extension.create-active-directory-forest: Still creating... [20s elapsed]
… … …
 azurerm_virtual_machine_extension.create-active-directory-forest: Still creating... [4m30s elapsed]
 azurerm_virtual_machine_extension.create-active-directory-forest: Creation complete after 4m36s [id=/subscriptions/58xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/RG-CC-AZ-TF-TEST/providers/Microsoft.Compu
 te/virtualMachines/VM-TF-CC-DC/extensions/create-active-directory-forest]
 time_sleep.wait_2_minutes_after_adcompleted: Creating...
 time_sleep.wait_2_minutes_after_adcompleted: Still creating... [10s elapsed]
… … … 
 time_sleep.wait_2_minutes_after_adcompleted: Still creating... [1m51s elapsed]
 time_sleep.wait_2_minutes_after_adcompleted: Creation complete after 2m0s [id=2023-12-21T17:19:37Z]
 azurerm_virtual_machine_extension.DownloadPSScriptToLocalVMAndExecuteItOnCC1: Creating...
 azurerm_virtual_machine_extension.DownloadPSScriptToLocalVMAndExecuteItOnCC2: Creating...
 azurerm_virtual_machine_extension.DownloadPSScriptToLocalVMAndExecuteItOnCC1: Still creating... [10s elapsed]
 azurerm_virtual_machine_extension.DownloadPSScriptToLocalVMAndExecuteItOnCC2: Still creating... [10s elapsed]
… … … 
 azurerm_virtual_machine_extension.DownloadPSScriptToLocalVMAndExecuteItOnCC1: Still creating... [11m11s elapsed]
 azurerm_virtual_machine_extension.DownloadPSScriptToLocalVMAndExecuteItOnCC2: Creation complete after 11m13s [id=/subscriptions/58xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/RG-CC-AZ-TF-TEST/providers/Microsoft.Compute/virtualMachines/VM-TF-CCCC2/extensions/DownloadPSScriptToLocalVMAndExecuteIt]
 azurerm_virtual_machine_extension.DownloadPSScriptToLocalVMAndExecuteItOnCC1: Creation complete after 11m14s [id=/subscriptions/58xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/RG-CC-AZ-TF-TEST/providers/Microsoft.Compute/virtualMachines/VM-TF-CCCC1/extensions/DownloadPSScriptToLocalVMAndExecuteIt]

 Apply complete! Resources: 34 added, 0 changed, 0 destroyed.
 PS C:\_TACG\__CConAzureCreation>

This configuration completes the creation of all initial resources:

  • One VM, used as a dedicated Domain Controller (DC)
  • Two VMs, used as Cloud Controllers (CC)
  • Creating a forest on the DC
  • Putting the 2 CCs into the new domain
  • Installing the needed software on the CCs
  • Creating a Resource Location in Citrix Cloud
  • Configuring the 2 CCs as Cloud Controllers
  • Registering the 2 CCs in the newly created Resource Location

As the current Terraform providers are partially not able to do all needed configurations like joining a VM to the domain, installing, and removing software components, the providers start PowerShell scripts to complete the configuration.

Example: After successful creation of the VMs, Terraform puts this PowerShell script in action.
The script downloads the next software components and joins the CC-VMs to the domain and sets the next PowerShell scripts to be started after the necessary reboot.

Start-Sleep -Seconds 60
Invoke-WebRequest -Uri https://${var.azurerm-storage_account_name}.blob.core.windows.net/${var.azurerm-storage_account_container_name}/cwcconnector.exe -OutFile c:/temp/cwcconnector.exe
Invoke-WebRequest -Uri https://${var.azurerm-storage_account_name}.blob.core.windows.net/${var.azurerm-storage_account_container_name}/cwc.json -OutFile c:/temp/cwc.json
Invoke-WebRequest -Uri https://${var.azurerm-storage_account_name}.blob.core.windows.net/${var.azurerm-storage_account_container_name}/InstallPreReqs.ps1 -OutFile c:/temp/InstallPreReqs.ps1
Invoke-WebRequest -Uri https://${var.azurerm-storage_account_name}.blob.core.windows.net/${var.azurerm-storage_account_container_name}/InstallCWC.ps1 -OutFile c:/temp/InstallCWC.ps1
Invoke-WebRequest -Uri https://${var.azurerm-storage_account_name}.blob.core.windows.net/${var.azurerm-storage_account_container_name}/CitrixPoshSdk.exe -OutFile c:/temp/CitrixPoshSdk.exe
Set-ItemProperty -Path "HKLM:\Software\Microsoft\Windows\CurrentVersion\RunOnce" -Name "install" -Value "powershell.exe -ExecutionPolicy Unrestricted -file c:/temp/InstallPreReqs.ps1"
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name "AutoAdminLogon" -Value "1"
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name "DefaultUsername" -Value "xxxxxxxxxxxx"
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name "DefaultPassword" -Value "xxxxxxxxxxxx"
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name "DefaultDomain" -Value "tf.the-austrian-citrix-guy.at"
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name "AutoLogonCount" -Value "1"

netdom.exe join $env:COMPUTERNAME /domain:tf.the-austrian-citrix-guy.at /UserD:XXXXXXXXXXXXXXXXXXXX /PasswordD:XXXXXXXXXXXXXXXXXXXXXXX /reboot:5

After successful runs of all needed scripts we can see the new Resource Group in Azure, the 3 VMs in Azure, the new Resource Location in Citrix Cloud and the 2 Cloud Controllers bound to the Resource Location:

deployment-guides_citrix-daas-terraform-azure-create-all-azure-resources-ready.png

In Citrix Cloud the new Resource Location with the Cloud Connectors is visible:

deployment-guides_citrix-daas-terraform-azure-create-cc-rl-complete.png

deployment-guides_citrix-daas-terraform-azure-create-cc-rl-complete-1.png

The environment is now ready to deploy a Machine Catalog and a Delivery Group using Module 3.

Module 3: Create all Resources in Microsoft Azure and Citrix Cloud

This module is split into the following configuration parts:

  1. Retrieving the Site- and Zone-ID of the new Resource Location
  2. Creating a Hypervisor Connection to Azure and a Hypervisor Pool
  3. Creating a Machine Catalog (MC) in the newly created Resource Location
  4. Creating a Delivery Group (DG) based on the MC in the newly created Resource Location

The Terraform configuration contains some idle time slots to make sure that background operations on Azure or on the VMs can completed before the next configuration steps occur.
We have seen different elapsed configuration times related to different loads on the Azure systems! Before Terraform can created the Hypervisor Connection and the Hypervisor Pool, Terraform needs to retrieve the Site-ID and Zone-ID of the newly created Resource Location. As the Citrix Terraform Provider currently has no Cloud-level functionalities implemented, Terraform runs PowerShell scripts to retrieve the IDs. It creates the necessary scripts with all needed variables, saves the scripts and executes them.

NOTE: The Citrix Remote PowerShell SDK must be installed on the machine running Terraform as Terraform relies on the SDK!

After retrieving the IDs, Terraform configures a Hypervisor Connection to Azure and a Hypervisor Resource Pool associated with the Hypervisor Connection. As soon as these prerequisites are completed, the Machine Catalog is created. After the successful creation of the Hypervisor Connection, the Hypervisor Resource Pool and the Machine Catalog the last step of the deployment process starts - the creation of the Delivery Group.

NOTE: In the example below we do not use the uploaded Master Image, we use an Azure-based Windows 11-Enterprise-MultiSession VM as Master. To use the uploaded image, you only need to change the corresponding variable azurerm-masterimage-name in the CConAzCitrixCloudStuff-main.auto.tfvars.json file. Be sure to change the variables cc-mc-allocationtype and cc-mc-sessiontype in the CConAzCitrixCloudStuff-main.auto.tfvars.json file accordingly - depending on the VDA type.

The Terraform configuration assumes that all machines in the created Machine Catalog are used in the Delivery Group and that Autoscale will be configured for this Delivery Group.
More information about Autoscale can be found here: https://docs.citrix.com/en-us/tech-zone/learn/tech-briefs/autoscale.html

NOTE: This module has some complexity as many automated actions and many different entities are created.

Please make sure you have configured the variables according to your needs.

NOTE: Each entity can be solely configured using the corresponding .auto.tfvars.json file. The Hypervisor-related entities can be configured using the corresponding CConAzCitrixCloudStuff-hyp.auto.tfvars.json file. The Machine Catalog entity can be configured using the corresponding CConAzCitrixCloudStuff-mc.auto.tfvars.json file. The Delivery Group entity can be configured using the corresponding CConAzCitrixCloudStuff-dg.auto.tfvars.json file.

Caution:

Be sure that your subscription has no quota limitation on the chosen VM type and 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!

Example: Checking the vCPU usage and limit for a D2S_v5 VM which we plan to use in this guide:

 $Location = 'East US'
 $VMSize = 'Standard_D2s_v5'
 $SKU = Get-AzComputeResourceSku -Location $Location | where ResourceType -eq "virtualMachines" | select Name,Family
 $VMFamily = ($SKU | where Name -eq $VMSize | select -Property Family).Family
 Get-AzVMUsage -Location $Location | Where-Object { $_.Name.Value -eq $VMFamily }
PS C:\TACG> $Location = 'East US'
PS C:\TACG> $VMSize = 'Standard_D2s_v5'
PS C:\TACG> $SKU = Get-AzComputeResourceSku -Location $Location | where ResourceType -eq "virtualMachines" | select Name,Family
PS C:\TACG> $VMFamily = ($SKU | where Name -eq $VMSize | select -Property Family).Family
PS C:\TACG> Get-AzVMUsage -Location $Location | Where-Object { $_.Name.Value -eq $VMFamily }

Name                                Current Value Limit  Unit
----                                ------------- -----  ----
Standard DSv5 Family vCPUs          10            16     Count

PS C:\TACG>

This example shows that we can use a maximum of 3 further D2S_v5 VMs before exceeding the limit.
The vCPU quota or any other quota can be increased using the Azure Console or PowerShell.
More information about increasing vCPU quotas can be found here: https://learn.microsoft.com/en-us/azure/quotas/per-vm-quota-requests

The configuration can be started by following the normal Terraform workflow:
terraform init,
terraform plan
and if no errors occur

terraform apply

PS C:\TACG\> 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.local_file.input_site will be read during apply
  # (depends on a resource or a module with changes pending)
 <= data "local_file" "input_site" {
      + content              = (known after apply)
      + content_base64       = (known after apply)
      + content_base64sha256 = (known after apply)
      + content_base64sha512 = (known after apply)
      + content_md5          = (known after apply)
      + content_sha1         = (known after apply)
      + content_sha256       = (known after apply)
      + content_sha512       = (known after apply)
      + filename             = "./GetSiteID.txt"
      + id                   = (known after apply)
    }

  # data.local_file.input_zone will be read during apply
  # (depends on a resource or a module with changes pending)
 <= data "local_file" "input_zone" {
      + 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             = "./GetZoneID.txt"
      + 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             = "./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             = "./GetZoneID.ps1"
      + 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)
    }

  # citrix_daas_delivery_group.TF-CC-DG will be created
  + resource "citrix_daas_delivery_group" "TF-CC-DG" {
      + associated_machine_catalogs = [
          + {
              + machine_catalog = (known after apply)
              + machine_count   = 1
            },
        ]
      + autoscale_enabled           = true
      + autoscale_settings          = {
          + disconnect_off_peak_idle_session_after_seconds      = 120
          + disconnect_peak_idle_session_after_seconds          = 120
          + log_off_off_peak_disconnected_session_after_seconds = 120
          + log_off_peak_disconnected_session_after_seconds     = 120
          + off_peak_buffer_size_percent                        = 1
          + peak_buffer_size_percent                            = 1
          + power_time_schemes                                  = [
              + {
                  + days_of_week          = [
                      + "Monday",
                      + "Tuesday",
                      + "Wednesday",
                      + "Thursday",
                      + "Friday",
                    ]
                  + display_name          = "TF Schedule"
                  + peak_time_ranges      = [
                      + "09:00-17:00",
                    ]
                  + pool_size_schedules   = [
                      + {
                          + pool_size  = 0
                          + time_range = "00:00-09:00"
                        },
                      + {
                          + pool_size  = 1
                          + time_range = "09:00-17:00"
                        },
                      + {
                          + pool_size  = 0
                          + time_range = "17:00-00:00"
                        },
                    ]
                  + pool_using_percentage = false
                },
            ]
        }
      + description                 = "TF-created Delivery Group"
      + id                          = (known after apply)
      + name                        = "TF-CC-DG-MCAZN"
      + total_machines              = (known after apply)
      + users                       = [
          + "xxxxxxxxxx@tf.the-austrian-citrix-guy.at",
          + "xxxxxxxxxx@tf.the-austrian-citrix-guy.at",
        ]
    }

  # citrix_daas_hypervisor.TF-CC-HYPV will be created
  + resource "citrix_daas_hypervisor" "TF-CC-HYPV" {
      + active_directory_id = (sensitive value)
      + application_id      = (sensitive value)
      + application_secret  = (sensitive value)
      + connection_type     = "AzureRM"
      + id                  = (known after apply)
      + name                = "TF-CC-HYP-AZ"
      + subscription_id     = (sensitive value)
      + zone                = "28xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
    }

  # citrix_daas_hypervisor_resource_pool.TF-CC-HYPV-POOL will be created
  + resource "citrix_daas_hypervisor_resource_pool" "TF-CC-HYPV-POOL" {
      + hypervisor                     = (known after apply)
      + hypervisor_connection_type     = (known after apply)
      + id                             = (known after apply)
      + name                           = "TF-CC-HYP-RP-AZ"
      + region                         = "eastus"
      + subnets                        = [
          + "SN-TF-AZ",
        ]
      + virtual_network                = "VNET-TF-AZ-CC1"
      + virtual_network_resource_group = "RG-CC-AZ-TF-TEST"
    }

  # citrix_daas_machine_catalog.TF-CC-MC will be created
  + resource "citrix_daas_machine_catalog" "TF-CC-MC" {
      + allocation_type          = "Random"
      + description              = "AzureRM"
      + id                       = (known after apply)
      + name                     = "TF-CC-MC-MCAZ-MS"
      + provisioning_scheme      = {
          + machine_account_creation_rules = {
              + domain             = "tf.the-austrian-citrix-guy.at"
              + naming_scheme      = "TF-VM-W11N-#"
              + naming_scheme_type = "Numeric"
            }
          + machine_config                 = {
              + hypervisor               = (known after apply)
              + hypervisor_resource_pool = (known after apply)
              + master_image             = "VM-TF-W11-ENT-MIM_OsDisk_1_xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
              + resource_group           = "RG-CC-AZ-TF-TEST"
              + service_offering         = "Standard_D2s_v5"
            }
          + network_mapping                = {
              + network        = "SN-TF-AZ"
              + network_device = "0"
            }
          + number_of_total_machines       = 1
          + storage_type                   = "Standard_LRS"
          + use_managed_disks              = true
        }
      + service_account          = "xxxxxxxxxx"
      + service_account_password = (sensitive value)
      + session_support          = "MultiSession"
      + vda_upgrade_type         = (known after apply)
      + zone                     = "28xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
    }

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

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

Plan: 12 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> 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.local_file.input_site will be read during apply
  # (depends on a resource or a module with changes pending)
 <= data "local_file" "input_site" {
      + content              = (known after apply)
      + content_base64       = (known after apply)
      + content_base64sha256 = (known after apply)
      + content_base64sha512 = (known after apply)
      + content_md5          = (known after apply)
      + content_sha1         = (known after apply)
      + content_sha256       = (known after apply)
      + content_sha512       = (known after apply)
      + filename             = "./GetSiteID.txt"
      + id                   = (known after apply)
    }

  # data.local_file.input_zone will be read during apply
  # (depends on a resource or a module with changes pending)
 <= data "local_file" "input_zone" {
      + 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             = "./GetZoneID.txt"
      + 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             = "./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             = "./GetZoneID.ps1"
      + 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)
    }

  # citrix_daas_delivery_group.TF-CC-DG will be created
  + resource "citrix_daas_delivery_group" "TF-CC-DG" {
      + associated_machine_catalogs = [
          + {
              + machine_catalog = (known after apply)
              + machine_count   = 1
            },
        ]
      + autoscale_enabled           = true
      + autoscale_settings          = {
          + disconnect_off_peak_idle_session_after_seconds      = 120
          + disconnect_peak_idle_session_after_seconds          = 120
          + log_off_off_peak_disconnected_session_after_seconds = 120
          + log_off_peak_disconnected_session_after_seconds     = 120
          + off_peak_buffer_size_percent                        = 1
          + peak_buffer_size_percent                            = 1
          + power_time_schemes                                  = [
              + {
                  + days_of_week          = [
                      + "Monday",
                      + "Tuesday",
                      + "Wednesday",
                      + "Thursday",
                      + "Friday",
                    ]
                  + display_name          = "TF Schedule"
                  + peak_time_ranges      = [
                      + "09:00-17:00",
                    ]
                  + pool_size_schedules   = [
                      + {
                          + pool_size  = 0
                          + time_range = "00:00-09:00"
                        },
                      + {
                          + pool_size  = 1
                          + time_range = "09:00-17:00"
                        },
                      + {
                          + pool_size  = 0
                          + time_range = "17:00-00:00"
                        },
                    ]
                  + pool_using_percentage = false
                },
            ]
        }
      + description                 = "TF-created Delivery Group"
      + id                          = (known after apply)
      + name                        = "TF-CC-DG-MCAZN"
      + total_machines              = (known after apply)
      + users                       = [
          + "xxxxxxxxxx@tf.the-austrian-citrix-guy.at",
          + "xxxxxxxxxx@tf.the-austrian-citrix-guy.at",
        ]
    }

  # citrix_daas_hypervisor.TF-CC-HYPV will be created
  + resource "citrix_daas_hypervisor" "TF-CC-HYPV" {
      + active_directory_id = (sensitive value)
      + application_id      = (sensitive value)
      + application_secret  = (sensitive value)
      + connection_type     = "AzureRM"
      + id                  = (known after apply)
      + name                = "TF-CC-HYP-AZ"
      + subscription_id     = (sensitive value)
      + zone                = "28d7d635-76f1-4604-91d4-68c4af3a88f8"
    }

  # citrix_daas_hypervisor_resource_pool.TF-CC-HYPV-POOL will be created
  + resource "citrix_daas_hypervisor_resource_pool" "TF-CC-HYPV-POOL" {
      + hypervisor                     = (known after apply)
      + hypervisor_connection_type     = (known after apply)
      + id                             = (known after apply)
      + name                           = "TF-CC-HYP-RP-AZ"
      + region                         = "eastus"
      + subnets                        = [
          + "SN-TF-AZ",
        ]
      + virtual_network                = "VNET-TF-AZ-CC1"
      + virtual_network_resource_group = "RG-CC-AZ-TF-TEST"
    }

  # citrix_daas_machine_catalog.TF-CC-MC will be created
  + resource "citrix_daas_machine_catalog" "TF-CC-MC" {
      + allocation_type          = "Random"
      + description              = "AzureRM"
      + id                       = (known after apply)
      + name                     = "TF-CC-MC-MCAZ-MS"
      + provisioning_scheme      = {
          + machine_account_creation_rules = {
              + domain             = "tf.the-austrian-citrix-guy.at"
              + naming_scheme      = "TF-VM-W11N-#"
              + naming_scheme_type = "Numeric"
            }
          + machine_config                 = {
              + hypervisor               = (known after apply)
              + hypervisor_resource_pool = (known after apply)
              + master_image             = "VM-TF-W11-ENT-MIM_OsDisk_1_ea4c5c324f2f477bad3beca64c9489d2"
              + resource_group           = "RG-CC-AZ-TF-TEST"
              + service_offering         = "Standard_D2s_v5"
            }
          + network_mapping                = {
              + network        = "SN-TF-AZ"
              + network_device = "0"
            }
          + number_of_total_machines       = 1
          + storage_type                   = "Standard_LRS"
          + use_managed_disks              = true
        }
      + service_account          = "xxxxxxxxxx"
      + service_account_password = (sensitive value)
      + session_support          = "MultiSession"
      + vda_upgrade_type         = (known after apply)
      + zone                     = "28d7d635-76f1-4604-91d4-68c4af3a88f8"
    }

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

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

local_file.GetSiteIDScript: Creating...
local_file.GetSiteIDScript: Creation complete after 0s [id=6cfaa0a889a04fa7a821366ad524dde703988ec6]
terraform_data.SiteID: Creating...
terraform_data.SiteID: Provisioning with 'local-exec'...
terraform_data.SiteID (local-exec): Executing: ["PowerShell" "-File" "GetSiteID.ps1"]
terraform_data.SiteID: Creation complete after 1s [id=311c1a04-044e-3ee6-68b1-a4c41ecba258]
data.local_file.input_site: Reading...
data.local_file.input_site: Read complete after 0s [id=f684114a2b93cc095c4ac5f81999ee1a111d53b9]
local_file.GetZoneIDScript: Creating...
local_file.GetZoneIDScript: Creation complete after 0s [id=022a8055f784d20aa2964d1a325e496cde3a9258]
terraform_data.ZoneID: Creating...
terraform_data.ZoneID: Provisioning with 'local-exec'...
terraform_data.ZoneID (local-exec): Executing: ["PowerShell" "-File" "GetZoneID.ps1"]
terraform_data.ZoneID: Creation complete after 1s [id=ea9e229f-4f57-8c6b-b263-24af5126f50c]
data.local_file.input_zone: Reading...
data.local_file.input_zone: Read complete after 0s [id=f2acfc022e0892c48bf3f3e258f011b9c96a4617]
time_sleep.wait_2_minutes_dg: Creating...
citrix_daas_hypervisor.TF-CC-HYPV: Creating...
time_sleep.wait_2_minutes_dg: Still creating... [10s elapsed]
citrix_daas_hypervisor.TF-CC-HYPV: Still creating... [10s elapsed]
time_sleep.wait_2_minutes_dg: Still creating... [20s elapsed]
citrix_daas_hypervisor.TF-CC-HYPV: Still creating... [20s elapsed]
citrix_daas_hypervisor.TF-CC-HYPV: Creation complete after 21s [id=0b74bf1e-a77c-4e7e-8910-732e344b26e9]
citrix_daas_hypervisor_resource_pool.TF-CC-HYPV-POOL: Creating...
citrix_daas_hypervisor_resource_pool.TF-CC-HYPV-POOL: Creation complete after 5s [id=f38e9dfc-073e-45ef-b22d-f00fee9c1f3d]
citrix_daas_machine_catalog.TF-CC-MC: Creating...
time_sleep.wait_2_minutes_dg: Still creating... [30s elapsed]
citrix_daas_machine_catalog.TF-CC-MC: Still creating... [10s elapsed]
time_sleep.wait_2_minutes_dg: Still creating... [40s elapsed]
citrix_daas_machine_catalog.TF-CC-MC: Still creating... [20s elapsed]
time_sleep.wait_2_minutes_dg: Still creating... [50s elapsed]
citrix_daas_machine_catalog.TF-CC-MC: Still creating... [30s elapsed]
time_sleep.wait_2_minutes_dg: Still creating... [1m0s elapsed]
citrix_daas_machine_catalog.TF-CC-MC: Still creating... [40s elapsed]
time_sleep.wait_2_minutes_dg: Still creating... [1m10s elapsed]
citrix_daas_machine_catalog.TF-CC-MC: Still creating... [50s elapsed]
time_sleep.wait_2_minutes_dg: Still creating... [1m20s elapsed]
citrix_daas_machine_catalog.TF-CC-MC: Still creating... [1m0s elapsed]
time_sleep.wait_2_minutes_dg: Still creating... [1m30s elapsed]
citrix_daas_machine_catalog.TF-CC-MC: Still creating... [1m10s elapsed]
time_sleep.wait_2_minutes_dg: Still creating... [1m40s elapsed]
citrix_daas_machine_catalog.TF-CC-MC: Still creating... [1m20s elapsed]
time_sleep.wait_2_minutes_dg: Still creating... [1m50s elapsed]
citrix_daas_machine_catalog.TF-CC-MC: Still creating... [1m30s elapsed]
time_sleep.wait_2_minutes_dg: Creation complete after 2m0s [id=2024-01-05T10:33:36Z]
citrix_daas_machine_catalog.TF-CC-MC: Still creating... [1m40s elapsed]
citrix_daas_machine_catalog.TF-CC-MC: Still creating... [1m50s elapsed]
...
citrix_daas_machine_catalog.TF-CC-MC: Still creating... [7m30s elapsed]
citrix_daas_machine_catalog.TF-CC-MC: Creation complete after 7m34s [id=742a3752-def3-44a3-9277-9b9c8ea8df82]
citrix_daas_delivery_group.TF-CC-DG: Creating...
citrix_daas_delivery_group.TF-CC-DG: Creation complete after 5s [id=30ffd8b8-59e7-4a00-b5c9-820a1ffbd58b]

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

This configuration completes the full deployment of a Citrix Cloud Resource Location in Microsoft Azure.

The environment created by Terraform is now ready for usage, all entities are in place:

The Resource Location:

deployment-guides_citrix-daas-terraform-rl-ready.png

The Hypervisor Connection and the Hypervisor Pool:

deployment-guides_citrix-daas-terraform-hyp-ready.png

The Machine Catalog:

deployment-guides_citrix-daas-terraform-mc-ready.png

The Delivery Group:

deployment-guides_citrix-daas-terraform-dg-ready.png

The Azure Resource Group with all Entities:

deployment-guides_citrix-daas-terraform-azure-ready.png

Appendix

Examples of the Terraform scripts

Module 1: CConAzureUpload

This is the Terraform configuration file for Module 1

 # Configure the Providers

provider "azurerm" {
  features {}
  client_id       = "${var.azurerm-clientid}"
  client_secret   = "${var.azurerm-clientsecret}"
  tenant_id       = "${var.azurerm-tenantid}"
  subscription_id = "${var.azurerm-subscriptionid}"
}

## Create the Resource Group on Azure
resource "azurerm_resource_group" "CC-AZ-TF" {
  name     = "${var.azurerm-resource_group_name}"
  location = "${var.azurerm-resource_group_location}"
}

## Create the Storage Account on Azure
resource "azurerm_storage_account" "CC-AZ-SB" {
   depends_on = [azurerm_resource_group.CC-AZ-TF,time_sleep.wait_3_minutes]

  name                     = "sat${var.cc-customerid}"
  resource_group_name      = "${var.azurerm-resource_group_name}"
  location                 = "${var.azurerm-resource_group_location}"
  account_tier             = "${var.azurerm-storageblob_tier}"
  account_replication_type = "${var.azurerm-storageblob_replication}"
}

## Create the Storage Container on Azure
resource "azurerm_storage_container" "CC-AZ-SB" {
  name                  = "scsat${var.cc-customerid}"
  storage_account_name  = azurerm_storage_account.CC-AZ-SB.name
  container_access_type = "blob"
}

## Create object for 3min waiting time - needed for Storage Blob to complete its configuration
resource "time_sleep" "wait_3_minutes" {
  create_duration = "180s"
}

## Create object for 5min waiting time - needed for all to complete before exporting the variables
resource "time_sleep" "wait_5_minutes" {
  create_duration = "300s"
}

## Create upload for CloudConnector software to Storage Blob
resource "azurerm_storage_blob" "CC-AZ-SB-CWC" {
  depends_on = [time_sleep.wait_3_minutes]
  name                   = "cwcconnector.exe"
  storage_account_name   = azurerm_storage_account.CC-AZ-SB.name
  storage_container_name = azurerm_storage_container.CC-AZ-SB.name
  type                   = "Block"
  source                 = "cwcconnector.exe"
}

## Create upload for Citrix Remote PoSH SDK software to Storage Blob
resource "azurerm_storage_blob" "CC-AZ-SB-CTXPOSHSDK" {
  depends_on = [time_sleep.wait_3_minutes]
  name                   = "CitrixPoshSdk.exe"
  storage_account_name   = azurerm_storage_account.CC-AZ-SB.name
  storage_container_name = azurerm_storage_container.CC-AZ-SB.name
  type                   = "Block"
  source                 = "CitrixPoshSdk.exe"
}

## Upload the Master Image VHD
resource "azurerm_storage_blob" "CC-AZ-SB-MI" {
  depends_on = [time_sleep.wait_3_minutes]
  name                   = "${var.azurerm-UploadVHD-name}"
  storage_account_name   = azurerm_storage_account.CC-AZ-SB.name
  storage_container_name = azurerm_storage_container.CC-AZ-SB.name
  type                   = "Page"
  source                 = "${var.azurerm-UploadVHD-source}"
}

## Write the variables into the next following module CConAzCreate.main.auto.tfvars.json
resource "local_file" "OutputAllVars" {
depends_on = [time_sleep.wait_5_minutes]
  content  = jsonencode(
        {
          "azurerm-clientid": "${var.azurerm-clientid}",
          "azurerm-clientsecret": "${var.azurerm-clientsecret}",
          "azurerm-tenantid": "${var.azurerm-tenantid}",
          "azurerm-subscriptionid": "${var.azurerm-subscriptionid}",
          "azurerm-resource_group_name": azurerm_resource_group.CC-AZ-TF.name,
          "azurerm-resource_group_location": azurerm_resource_group.CC-AZ-TF.location,
          "azurerm-storage_account_name": azurerm_storage_account.CC-AZ-SB.name,
          "azurerm-storage_account_location": azurerm_storage_account.CC-AZ-SB.location,
          "azurerm-storage_account_tier": azurerm_storage_account.CC-AZ-SB.account_tier,
          "azurerm-storage_account_replication": azurerm_storage_account.CC-AZ-SB.account_replication_type,
          "azurerm-storage_account_container_name": azurerm_storage_container.CC-AZ-SB.name,
          "cc-customerid": "${var.cc-customerid}",
          "azurerm-masterimage-name": "${var.azurerm-UploadVHD-name}",
          "azurerm-masterimage-storage_account_type": "${var.azurerm-UploadVHD-type}",
          "azurerm-masterimage-create_option": "Import",
          "azurerm-masterimage-source_uri": azurerm_storage_blob.CC-AZ-SB-MI.id,
          "azurerm-masterimage-storage_account_id" :azurerm_storage_account.CC-AZ-SB.id,
          "azurerm-masterimage-os_type": "Windows",
          "azurerm-masterimage-hyper_v_generation": "V2"
        }
      )
  filename = "${path.root}/../__CConAzureCreation/CConAzCreate-main.auto.tfvars.json"
}

Module 2: CConAzureCreation

This is the Terraform configuration file for Module 2

# Begin creation of the Citrix Cloud Resource Location on Azure

# Create all needed Azure-based resources for deploying a Citrix Cloud Resource Location

## Local variables for DC configuration
locals {
  Import_PS_Cmdlet_Command   = "Import-Module ADDSDeployment"
  Secure_Password_Command    = "$password = ConvertTo-SecureString ${var.azurerm-vm-domainadmin-pw} -AsPlainText -Force"
  Install_ADDSonDC_Command   = "Add-WindowsFeature -name ad-domain-services -IncludeManagementTools"
  Configure_ADDSonDC_Command = "Install-ADDSForest -CreateDnsDelegation:$false -DomainNetbiosName ${var.azurerm-vm-netbiosname} -DomainMode Win2012R2 -ForestMode Win2012R2 -InstallDns:$true -SafeModeAdministratorPassword $password -DomainName ${var.azurerm-vm-domainname} -Force:$true"
  Shutdown_VM_Command        = "shutdown -r -t 10"
  Wanted_Exit_Code           = "exit 0"
  Nested_PowerShell_Command  = "${local.Import_PS_Cmdlet_Command}; ${local.Secure_Password_Command}; ${local.Install_ADDSonDC_Command}; ${local.Configure_ADDSonDC_Command}; ${local.Shutdown_VM_Command}; ${local.Wanted_Exit_Code}"
  locvnetlist = tolist(split(", ", var.azurerm-vnet-addressspace-single))
  locsubnetlist = tolist(split(", ", var.azurerm-subnet-addressprefix-single))
}

## Create 2 Cloud Connector VMs in Azure in a dedicated Resource Group
### Create a dedicated Virtual Network 
#### Set the values in the corresponding .auto.tfvars.json file
resource "azurerm_virtual_network" "CC-AZ-TF" {
  name                = "${var.azurerm-vnet-name}"
  address_space       = local.locvnetlist
  location            = "${var.azurerm-resource_group_location}"
  resource_group_name = "${var.azurerm-resource_group_name}"
}

resource "azurerm_virtual_network_dns_servers" "CC-AZ-TF" {
  virtual_network_id = azurerm_virtual_network.CC-AZ-TF.id
  dns_servers        = ["10.0.0.4"]
}

### Create a dedicated Public IP for the CC1-VM
#### Set the values in the corresponding .auto.tfvars.json file
resource "azurerm_public_ip" "CC-AZ-TF" {
  name                = "${var.azurerm-pubip-name}"
  location            = "${var.azurerm-resource_group_location}"
  resource_group_name = "${var.azurerm-resource_group_name}"
  allocation_method   = "${var.azurerm-pubip-allocation}"
}

### Create a dedicated Public IP for the CC2-VM
#### Set the values in the corresponding .auto.tfvars.json file
resource "azurerm_public_ip" "CC-AZ-TF2" {
  name                = "${var.azurerm-pubip-name2}"
  location            = "${var.azurerm-resource_group_location}"
  resource_group_name = "${var.azurerm-resource_group_name}"
  allocation_method   = "${var.azurerm-pubip-allocation}"
}

### Create a dedicated Public IP for the DC-VM
#### Set the values in the corresponding .auto.tfvars.json file
resource "azurerm_public_ip" "CC-AZ-TF-DC" {
  name                = "${var.azurerm-pubip-name-dc}"
  location            = "${var.azurerm-resource_group_location}"
  resource_group_name = "${var.azurerm-resource_group_name}"
  allocation_method   = "${var.azurerm-pubip-allocation}"
}

### Create a dedicated Subnet for the VMs
#### Set the values in the corresponding .auto.tfvars.json file
resource "azurerm_subnet" "CC-AZ-TF" {
  name                 = "${var.azurerm-subnet-name}"
  resource_group_name  = "${var.azurerm-resource_group_name}"
  virtual_network_name = azurerm_virtual_network.CC-AZ-TF.name
  address_prefixes     = ["10.0.0.0/24"]
}

### Create a dedicated Network Security Group and the Firewall rules for the VMs
#### Set the values in the corresponding .auto.tfvars.json file
resource "azurerm_network_security_group" "CC-AZ-TF" {
  name                 = "${var.azurerm-nsg-name}"
  location             = "${var.azurerm-resource_group_location}"
  resource_group_name  = "${var.azurerm-resource_group_name}"

  security_rule {
     name = "${var.azurerm-nsg-securityrule1.name}"                      
     priority  = "${var.azurerm-nsg-securityrule1.priority}"    
     direction = "${var.azurerm-nsg-securityrule1.direction}"    
     access = "${var.azurerm-nsg-securityrule1.access}"    
     protocol = "${var.azurerm-nsg-securityrule1.protocol}"    
     source_port_range = "${var.azurerm-nsg-securityrule1.source_port_range}"    
     destination_port_range = "${var.azurerm-nsg-securityrule1.destination_port_range}"    
     source_address_prefix = "${var.azurerm-nsg-securityrule1.source_address_prefix}"    
     destination_address_prefix = "${var.azurerm-nsg-securityrule1.destination_address_prefix}"    
  }
   security_rule {
     name = "${var.azurerm-nsg-securityrule2.name}"                      
     priority  = "${var.azurerm-nsg-securityrule2.priority}"    
     direction = "${var.azurerm-nsg-securityrule2.direction}"    
     access = "${var.azurerm-nsg-securityrule2.access}"    
     protocol = "${var.azurerm-nsg-securityrule2.protocol}"    
     source_port_range = "${var.azurerm-nsg-securityrule2.source_port_range}"    
     destination_port_range = "${var.azurerm-nsg-securityrule2.destination_port_range}"    
     source_address_prefix = "${var.azurerm-nsg-securityrule2.source_address_prefix}"    
     destination_address_prefix = "${var.azurerm-nsg-securityrule2.destination_address_prefix}"    
  }
    security_rule {
     name = "${var.azurerm-nsg-securityrule3.name}"                      
     priority  = "${var.azurerm-nsg-securityrule3.priority}"    
     direction = "${var.azurerm-nsg-securityrule3.direction}"    
     access = "${var.azurerm-nsg-securityrule3.access}"    
     protocol = "${var.azurerm-nsg-securityrule3.protocol}"    
     source_port_range = "${var.azurerm-nsg-securityrule3.source_port_range}"    
     destination_port_range = "${var.azurerm-nsg-securityrule3.destination_port_range}"    
     source_address_prefix = "${var.azurerm-nsg-securityrule3.source_address_prefix}"    
     destination_address_prefix = "${var.azurerm-nsg-securityrule3.destination_address_prefix}"    
  }
    security_rule {
     name = "${var.azurerm-nsg-securityrule4.name}"                      
     priority  = "${var.azurerm-nsg-securityrule4.priority}"    
     direction = "${var.azurerm-nsg-securityrule4.direction}"    
     access = "${var.azurerm-nsg-securityrule4.access}"    
     protocol = "${var.azurerm-nsg-securityrule4.protocol}"    
     source_port_range = "${var.azurerm-nsg-securityrule4.source_port_range}"    
     destination_port_range = "${var.azurerm-nsg-securityrule4.destination_port_range}"    
     source_address_prefix = "${var.azurerm-nsg-securityrule4.source_address_prefix}"    
     destination_address_prefix = "${var.azurerm-nsg-securityrule4.destination_address_prefix}"    
  }
}

### Associate the Network Security Group and the Firewall rules with the Subnet
resource "azurerm_subnet_network_security_group_association" "CC-AZ-TF" {
 # depends_on = [ azurerm_subnet.CC-AZ-TF ]
  subnet_id                 = azurerm_subnet.CC-AZ-TF.id
  network_security_group_id = azurerm_network_security_group.CC-AZ-TF.id
}

### Create a dedicated Network Interface and IP configuration for the DC-VM
#### Set the values in the corresponding .auto.tfvars.json file
resource "azurerm_network_interface" "CC-AZ-TF-DC" {
  name                = "${var.azurerm-nic-name-dc}"
  location            = "${var.azurerm-resource_group_location}"
  resource_group_name = "${var.azurerm-resource_group_name}"
  dns_servers         = ["10.0.0.4"]

  ip_configuration {
    name                          = "${var.azurerm-nic-ipconfiguration-dc.name}"
    subnet_id                     = azurerm_subnet.CC-AZ-TF.id
    private_ip_address_allocation = "${var.azurerm-nic-ipconfiguration-dc.private_ip_address_allocation}"
    public_ip_address_id = azurerm_public_ip.CC-AZ-TF-DC.id
  }
}

### Create a dedicated Network Interface and IP configuration for the CC1-VM
#### Set the values in the corresponding .auto.tfvars.json file
resource "azurerm_network_interface" "CC-AZ-TF" {
  name                = "${var.azurerm-nic-name}"
  location            = "${var.azurerm-resource_group_location}"
  resource_group_name = "${var.azurerm-resource_group_name}"
  dns_servers = azurerm_network_interface.CC-AZ-TF-DC.private_ip_addresses

  ip_configuration {
    name                          = "${var.azurerm-nic-ipconfiguration.name}"
    subnet_id                     = azurerm_subnet.CC-AZ-TF.id
    private_ip_address_allocation = "${var.azurerm-nic-ipconfiguration.private_ip_address_allocation}"
    public_ip_address_id = azurerm_public_ip.CC-AZ-TF.id
  } 

}

### Create a dedicated Network Interface and IP configuration for the CC2-VM
#### Set the values in the corresponding .auto.tfvars.json file
resource "azurerm_network_interface" "CC-AZ-TF2" {
  name                = "${var.azurerm-nic-name2}"
  location            = "${var.azurerm-resource_group_location}"
  resource_group_name = "${var.azurerm-resource_group_name}"
  dns_servers = azurerm_network_interface.CC-AZ-TF-DC.private_ip_addresses

  ip_configuration {
    name                          = "${var.azurerm-nic-ipconfiguration2.name}"
    subnet_id                     = azurerm_subnet.CC-AZ-TF.id
    private_ip_address_allocation = "${var.azurerm-nic-ipconfiguration2.private_ip_address_allocation}"
    public_ip_address_id = azurerm_public_ip.CC-AZ-TF2.id
  }
}

### Create the DC-VM
resource "azurerm_windows_virtual_machine" "CC-AZ-TF-DC" {
  depends_on = [ azurerm_network_interface.CC-AZ-TF-DC ]
  name                  = "${var.azurerm-vm-name-dc}"
  location              = "${var.azurerm-resource_group_location}"
  resource_group_name   = "${var.azurerm-resource_group_name}"
  network_interface_ids = [azurerm_network_interface.CC-AZ-TF-DC.id]
  size                  = "${var.azurerm-vm-size}"
  admin_username        = "${var.azurerm-vm-adun}"
  admin_password        = "${var.azurerm-vm-adpw}"
    provision_vm_agent = true

  source_image_reference {
    publisher = "${var.azurerm-vm-sourceimage-publisher}"
    offer     = "${var.azurerm-vm-sourceimage-offer}"
    sku       = "${var.azurerm-vm-sourceimage-sku}"
    version   = "${var.azurerm-vm-sourceimage-version}"
  }

  os_disk {
    name              = "${var.azurerm-vm-osdisk-name-dc}"
    caching           = "${var.azurerm-vm-osdisk-cache}"
    storage_account_type = "${var.azurerm-vm-osdisk-storagetype}"
  }

}

### Create the CC1-VM
resource "azurerm_windows_virtual_machine" "CC-AZ-TF" {
  depends_on = [ azurerm_network_interface.CC-AZ-TF ]
  name                  = "${var.azurerm-vm-name}"
  location              = "${var.azurerm-resource_group_location}"
  resource_group_name   = "${var.azurerm-resource_group_name}"
  network_interface_ids = [azurerm_network_interface.CC-AZ-TF.id]
  size                  = "${var.azurerm-vm-size}"
  admin_username        = "${var.azurerm-vm-adun}"
  admin_password        = "${var.azurerm-vm-adpw}"
    provision_vm_agent = true

  source_image_reference {
    publisher = "${var.azurerm-vm-sourceimage-publisher}"
    offer     = "${var.azurerm-vm-sourceimage-offer}"
    sku       = "${var.azurerm-vm-sourceimage-sku}"
    version   = "${var.azurerm-vm-sourceimage-version}"
  }

  os_disk {
    name              = "${var.azurerm-vm-osdisk-name}"
    caching           = "${var.azurerm-vm-osdisk-cache}"
    storage_account_type = "${var.azurerm-vm-osdisk-storagetype}"
  }
}

### Create the CC2-VM
resource "azurerm_windows_virtual_machine" "CC-AZ-TF2" {
  depends_on = [ azurerm_network_interface.CC-AZ-TF2 ]
  name                  = "${var.azurerm-vm-name2}"
  location              = "${var.azurerm-resource_group_location}"
  resource_group_name   = "${var.azurerm-resource_group_name}"
  network_interface_ids = [azurerm_network_interface.CC-AZ-TF2.id]
  size                  = "${var.azurerm-vm-size}"
  admin_username        = "${var.azurerm-vm-adun}"
  admin_password        = "${var.azurerm-vm-adpw}"
    provision_vm_agent = true

  source_image_reference {
    publisher = "${var.azurerm-vm-sourceimage-publisher}"
    offer     = "${var.azurerm-vm-sourceimage-offer}"
    sku       = "${var.azurerm-vm-sourceimage-sku}"
    version   = "${var.azurerm-vm-sourceimage-version}"
  }

  os_disk {
    name              = "${var.azurerm-vm-osdisk-name2}"
    caching           = "${var.azurerm-vm-osdisk-cache}"
    storage_account_type = "${var.azurerm-vm-osdisk-storagetype}"
  }

}

## Create the MasterImage Managed Disk
resource "azurerm_managed_disk" "CreateManagedDiskFromUploadedVHD" {
  name                 = var.azurerm-masterimage-name
  location             = var.azurerm-resource_group_location
  resource_group_name  = var.azurerm-resource_group_name
  storage_account_type = var.azurerm-masterimage-storage_account_type
  create_option        = var.azurerm-masterimage-create_option
  source_uri           = var.azurerm-masterimage-source_uri
  storage_account_id   = var.azurerm-masterimage-storage_account_id
  os_type              = var.azurerm-masterimage-os_type
  hyper_v_generation   = var.azurerm-masterimage-hyper_v_generation
}

### Create an object for 5mins waiting time 
resource "time_sleep" "wait_5_minutes" {
  create_duration = "300s"
}

### Create the Custom Script Extension to create a new Forest and Domain on DC-VM
resource "azurerm_virtual_machine_extension" "create-active-directory-forest" {
  depends_on = [ azurerm_windows_virtual_machine.CC-AZ-TF-DC, time_sleep.wait_5_minutes ]
  name                 = "create-active-directory-forest"
  virtual_machine_id = azurerm_windows_virtual_machine.CC-AZ-TF-DC.id
  publisher            = "Microsoft.Compute"
  type                 = "CustomScriptExtension"
  type_handler_version = "1.9"

  settings = <<SETTINGS
    {
        "commandToExecute": "powershell.exe -Command \"${local.Nested_PowerShell_Command}\""
    }
SETTINGS
}

################################## All Azure-related Resources have been created, now let´s start with Citrix Cloud ##################################

# Create Citrix Environment 

resource "random_uuid" "IDforCCRL" {
}

## Create PowerShell command to be run on the CC machines
locals {
 randomuuid = random_uuid.IDforCCRL.result
 #powershell_command = "New-AzStorageContext -StorageAccountName ${var.azurerm-storage_account_name} -Anonymous -Protocol https | Get-AzStorageBlobContent -blob download.ps1 -container ${var.azurerm-storage_account_container_name} -destination c:/temp;c:/temp/download.ps1; c:/temp/InstallPreReqs.ps1"
 powershell_command = "Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force; Install-Module -Name PowerShellGet -Force; Invoke-WebRequest -Uri https://satuzyo2lp7eh7j.blob.core.windows.net/scsatuzyo2lp7eh7j/download.ps1 -OutFile c:/temp/download.ps1; c:/temp/download.ps1; c:/temp/InstallPreReqs.ps1"
# >powershell.exe -ExecutionPolicy Unrestricted -Command "Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force; Install-Module -Name PowerShellGet -Force; Invoke-WebRequest -Uri https://satuzyo2lp7eh7j.blob.core.windows.net/scsatuzyo2lp7eh7j/download.ps1 -OutFile c:/temp/download.ps1; c:/temp/download.ps1; c:/temp/InstallPreReqs.ps1"
 }

## Create a dedicated Resource Location in Citrix Cloud
resource "restapi_object" "CreateRL" {
    provider = restapi.restapi_rl
    path="/resourcelocations"
      data = jsonencode(
        {
         "id" = "${local.randomuuid}",
        "name" = "${var.cc-restrl-name}",
        "internalOnly" = false,
        "timeZone" = "GMT Standard Time",
        "readOnly" = false
        }
      )
}

## Create PowerShell files with configuration and next steps and save it into Transfer directory
### Create CWC-Installer configuration file based on variables and save it into Transfer directory
resource "local_file" "CWC-Configuration" {
  depends_on = [restapi_object.CreateRL]
  content  = jsonencode(
        {
        "customerName" = "${var.cc-customerid}",
        "clientId" = "${var.cc-apikey-clientId}",
        "clientSecret" = "${var.cc-apikey-clientSecret}",
        "resourceLocationId" = "XXXXXXXXXX",
        "acceptTermsOfService" = true
        }
      )
  filename = "${path.module}/cwc.json"
}

### Create PowerShell file for downloading the CWC files to the local CC-VM
resource "local_file" "Download-ps1" {
  content  = <<-EOT
    Start-Sleep -Seconds 60
    Invoke-WebRequest -Uri https://${var.azurerm-storage_account_name}.blob.core.windows.net/${var.azurerm-storage_account_container_name}/cwcconnector.exe -OutFile c:/temp/cwcconnector.exe
    Invoke-WebRequest -Uri https://${var.azurerm-storage_account_name}.blob.core.windows.net/${var.azurerm-storage_account_container_name}/cwc.json -OutFile c:/temp/cwc.json
    Invoke-WebRequest -Uri https://${var.azurerm-storage_account_name}.blob.core.windows.net/${var.azurerm-storage_account_container_name}/InstallPreReqs.ps1 -OutFile c:/temp/InstallPreReqs.ps1
    Invoke-WebRequest -Uri https://${var.azurerm-storage_account_name}.blob.core.windows.net/${var.azurerm-storage_account_container_name}/InstallCWC.ps1 -OutFile c:/temp/InstallCWC.ps1
    Invoke-WebRequest -Uri https://${var.azurerm-storage_account_name}.blob.core.windows.net/${var.azurerm-storage_account_container_name}/CitrixPoshSdk.exe -OutFile c:/temp/CitrixPoshSdk.exe
    Set-ItemProperty -Path "HKLM:\Software\Microsoft\Windows\CurrentVersion\RunOnce" -Name "install" -Value "powershell.exe -ExecutionPolicy Unrestricted -file c:/temp/InstallPreReqs.ps1"
    Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name "AutoAdminLogon" -Value "1"
    Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name "DefaultUsername" -Value "xxxxxxxxxx"
    Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name "DefaultPassword" -Value "xxxxxxxxxx"
    Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name "DefaultDomain" -Value "tf.the-austrian-citrix-guy.at"
    Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name "AutoLogonCount" -Value "1"

    netdom.exe join $env:COMPUTERNAME /domain:tf.the-austrian-citrix-guy.at /UserD:xxxxxxxxxx@tf /PasswordD:xxxxxxxxxx /reboot:5
  EOT
  filename = "${path.module}/download.ps1"
}

### Create PowerShell file for installing the Citrix Remote PoSH SDK - we need to determine the correct RL-ID
resource "local_file" "InstallPreReqs-ps1" {
  content  = <<-EOT
  cd /temp
  Add-Content C:\temp\log.txt "`nScript started."
  # Install Citrix Remote PowerShell SDK
  ./CitrixPoSHSDK.exe -quiet
  Add-Content C:\temp\log.txt "`nPowerShell SDK installed."
  # Timeout to settle all processes
  Start-Sleep -Seconds 180
  Add-Content C:\temp\log.txt "`nTimeout elapsed."
  # Correct the Resource Location ID in cwc.json file
  add-pssnapin Citrix.*
  Set-XDCredentials -StoreAs default -ProfileType CloudApi -CustomerId ${var.cc-customerid} -APIKey ${var.cc-apikey-clientId} -SecretKey ${var.cc-apikey-clientSecret}
  $filter = "${var.cc-restrl-name}"
  $var = Get-Configzone |Select Name, ExternalUID | Where-Object {$_.Name -like $filter}
  $uid = $var.ExternalUid
  (Get-Content cwc.json -Raw) -replace 'XXXXXXXXXX', $uid | Set-Content cwc.json
  Add-Content C:\temp\log.txt "`ncwc.json corrected."
  # Remove Citrix Remote PowerShell SDK as it is not compatible with Cloud Controller
  $CtxPoSHSDK = Get-WmiObject -Class Win32_Product | Where-Object{$_.Name -Like "Citrix*"}
  if ($CtxPoSHSDK -ne $null) {
     $CtxPoSHSDK.Uninstall()
  }
  Add-Content C:\temp\log.txt "`nPowerShell SDK removed."
  # Install Citrix Cloud Controller based on the cwc.json configuration file
  ./CWCConnector.exe /q /ParametersFilePath:c:/temp/cwc.json

  EOT
  filename = "${path.module}/InstallPreReqs.ps1"
}

### Create PowerShell file for silently uninstalling the Citrix Remote PoSH SDK as this would break CWCConnector-Installation
resource "local_file" "InstallCWC-ps1" {
  content  = <<-EOT
    $CtxPoSHSDK = Get-WmiObject -Class Win32_Product | Where-Object{$_.Name -Like "Citrix*"}
    if ($CtxPoSHSDK -ne $null) {
       $CtxPoSHSDK.Uninstall()
    }
    cd /temp
    ./CWCConnector.exe /q /ParametersFilePath:c:/temp/cwc.json
  EOT
  filename = "${path.module}/InstallCWC.ps1"
}

### Download CWC installer and save it into Transfer directory
resource "terraform_data" "Download-CWC-Installer" {
  provisioner "local-exec" {
    command = "curl -s -o ${path.module}/cwcconnector.exe https://downloads.cloud.com/${var.cc-customerid}/connector/cwcconnector.exe"
  }
}

## Create object for 3min waiting time - needed for Azure backend to complete its configuration
resource "time_sleep" "wait_1_minutes" {
  create_duration = "60s"
}

## Create object for 3min waiting time - needed for Azure backend to complete its configuration
resource "time_sleep" "wait_3_minutes" {
  create_duration = "180s"
}

## Create object for 4min waiting time - needed for Azure backend to complete its configuration
resource "time_sleep" "wait_4_minutes" {
  create_duration = "240s"
}

resource "time_sleep" "wait_2_minutes_after_adcompleted" {
  depends_on = [azurerm_virtual_machine_extension.create-active-directory-forest]
  create_duration = "120s"
}

## Create upload for Download-Script to Storage Blob
resource "azurerm_storage_blob" "CC-AZ-SB-DL" {
  depends_on = [time_sleep.wait_1_minutes]
  name                   = "download.ps1"
  storage_account_name   = "${var.azurerm-storage_account_name}"
  storage_container_name = "${var.azurerm-storage_account_container_name}"
  type                   = "Block"
  source                 = "download.ps1"
}

## Create upload for Install-CloudConnector-Script to Storage Blob
resource "azurerm_storage_blob" "CC-AZ-SB-Install" {
  depends_on = [time_sleep.wait_1_minutes]
  name                   = "InstallCWC.ps1"
  storage_account_name   = "${var.azurerm-storage_account_name}"
  storage_container_name = "${var.azurerm-storage_account_container_name}"
  type                   = "Block"
  source                 = "InstallCWC.ps1"
}

## Create upload for CloudConnector-Configuration Script to Storage Blob
resource "azurerm_storage_blob" "CC-AZ-SB-JSON" {
  depends_on = [time_sleep.wait_1_minutes]
  name                   = "cwc.json"
  storage_account_name   = "${var.azurerm-storage_account_name}"
  storage_container_name = "${var.azurerm-storage_account_container_name}"
  type                   = "Block"
  source                 = "cwc.json"
}

## Create upload for Prerequisites-Script to Storage Blob
resource "azurerm_storage_blob" "CC-AZ-SB-InstallPreReqs-ps1" {
  depends_on = [time_sleep.wait_1_minutes]
  name                   = "InstallPreReqs.ps1"
  storage_account_name   = "${var.azurerm-storage_account_name}"
  storage_container_name = "${var.azurerm-storage_account_container_name}"
  type                   = "Block"
  source                 = "InstallPreReqs.ps1"
}

## Write the variables into the next following module CConAzureCitrixCloudStuff.main.auto.tfvars.json
resource "local_file" "OutputAllVars" {
depends_on = [time_sleep.wait_5_minutes]
  content  = jsonencode(
        {
          "azurerm-clientid": "${var.azurerm-clientid}",
          "azurerm-clientsecret": "${var.azurerm-clientsecret}",
          "azurerm-tenantid": "${var.azurerm-tenantid}",
          "azurerm-subscriptionid": "${var.azurerm-subscriptionid}",
          "azurerm-resource_group_name": "${var.azurerm-resource_group_name}",
          "azurerm-resource_group_location": "${var.azurerm-resource_group_location}"
          "azurerm-vnet-id": azurerm_virtual_network.CC-AZ-TF.id,
          "azurerm-subnet-id": azurerm_subnet.CC-AZ-TF.id,
          "cc-restrl-name": "${var.cc-restrl-name}",
          "cc-customerid": "${var.cc-customerid}",
          "cc-apikey-clientId": "${var.cc-apikey-clientId}",
          "cc-apikey-clientSecret": "${var.cc-apikey-clientSecret}",
          "cc-apikey-type": "client_credentials",
        }
      )
  filename = "${path.root}/../__CConAzureCitrixCloudStuff/CConAzCitrixCloudStuff-main.auto.tfvars.json"
}

## Create VM extension to download the PowerShell script containing the next steps and execute them on CC1
resource "azurerm_virtual_machine_extension" "DownloadPSScriptToLocalVMAndExecuteItOnCC1" {
  depends_on = [time_sleep.wait_2_minutes_after_adcompleted]
  name                 = "DownloadPSScriptToLocalVMAndExecuteIt"
  virtual_machine_id = azurerm_windows_virtual_machine.CC-AZ-TF.id
  publisher            = "Microsoft.Compute"
  type                 = "CustomScriptExtension"
  type_handler_version = "1.9"

  settings = <<SETTINGS
    {
        "commandToExecute": "powershell.exe -ExecutionPolicy Unrestricted -Command \"${local.powershell_command}\""
    }
SETTINGS
}

## Create VM extension to download the PowerShell script containing the next steps and execute them on CC2
resource "azurerm_virtual_machine_extension" "DownloadPSScriptToLocalVMAndExecuteItOnCC2" {
  depends_on = [time_sleep.wait_2_minutes_after_adcompleted]
  name                 = "DownloadPSScriptToLocalVMAndExecuteIt"
  virtual_machine_id = azurerm_windows_virtual_machine.CC-AZ-TF2.id
  publisher            = "Microsoft.Compute"
  type                 = "CustomScriptExtension"
  type_handler_version = "1.9"

  settings = <<SETTINGS
    {
        "commandToExecute": "powershell.exe -ExecutionPolicy Unrestricted -Command \"${local.powershell_command}\""
    }
SETTINGS
}

Module 3: CConAzureCitrixCloudStuff

This is the Terraform configuration file for Module 3

# Creation of the Citrix Cloud Machine Catalog
## Local variables for Zone retrieval

### Create PowerShell file for determining the SiteID
resource "local_file" "GetSiteIDScript" {
  content  = <<-EOT
$requestUri = "https://api-eu.cloud.com/cvad/manage/me"
$headers = @{ "Accept"="application/json"; "Authorization" = "${var.cc-apikey-bearer}"; "Citrix-CustomerId" = "${var.cc-customerid}" }
$response = Invoke-RestMethod -Uri $requestUri -Method GET -Headers $headers | Select-Object Customers 
$responsetojson = $response | Convertto-Json -Depth 3 
$responsekorr = $responsetojson -replace("null","""empty""")
$responsefromjson = $responsekorr | Convertfrom-json
$SitesObj=$responsefromjson.Customers[0].Sites[0]
$Export1 = $SitesObj -replace("@{Id=","")
$SplittedString = $Export1.Split(";")
$SiteID= $SplittedString[0]
$PathCompl = "${path.module}/GetSiteID.txt"
Set-Content -Path $PathCompl -Value $SiteID
  EOT
  filename = "${path.module}/GetSiteID.ps1"
}

### Running the SiteID-Script to generate the SiteID
resource "terraform_data" "SiteID" {
  depends_on = [ local_file.GetSiteIDScript ]
  provisioner "local-exec" {
     command = "GetSiteID.ps1"
    interpreter = ["PowerShell", "-File"]

  }
}

### Retrieving the SiteID
data "local_file" "input_site" {
  depends_on = [ terraform_data.SiteID ]
  filename = "${path.module}/GetSiteID.txt"
}

### Create PowerShell file for determining the ZoneID
resource "local_file" "GetZoneIDScript" {
  depends_on = [ data.local_file.input_site ]
  content  = <<-EOT
$requestUri = "https://api-eu.cloud.com/cvad/manage/Zones"
$headers = @{ "Accept"="application/json"; "Authorization" = "${var.cc-apikey-bearer}"; "Citrix-CustomerId" = "${var.cc-customerid}"; "Citrix-InstanceId" = "${data.local_file.input_site.content}" }
$response = Invoke-RestMethod -Uri $requestUri -Method GET -Headers $headers | Convertto-Json
$responsedejson = $response | ConvertFrom-Json
$ZoneId = $responsedejson.Items | Where-Object { $_.Name -eq "${var.cc-restrl-name}" } | Select-Object id 
$Export1 = $ZoneId -replace("@{Id=","")
$ZoneID = $Export1 -replace("}","")
$PathCompl = "${path.module}/GetZoneID.txt"
Set-Content -Path $PathCompl -Value $ZoneID
  EOT
  filename = "${path.module}/GetZoneID.ps1"
}

### Running the ZoneID-Script to generate the ZoneID
resource "terraform_data" "ZoneID" {
  depends_on = [ local_file.GetZoneIDScript ]
  provisioner "local-exec" {
     command = "GetZoneID.ps1"
    interpreter = ["PowerShell", "-File"]

  }
}

### Retrieving the ZoneID
data "local_file" "input_zone" {
  depends_on = [ terraform_data.ZoneID ]
  filename = "${path.module}/GetZoneID.txt"
}

# Creation of the Citrix Cloud Hypervisor Connection and Hypervisor Connection Resource Pool
## Local variables for MC configuration
locals {
}

## Create a Hypervisor Connection Resource Pool based on the previously created Hypervisor Connection
### Set the values in the corresponding CConAzCitrixCloudStuff-main.auto.tfvars.json file
resource "citrix_daas_hypervisor" "TF-CC-HYPV" {
    name                                    = "${var.cc-hyp-name}"
    connection_type                         = "${var.cc-hyp-connectiontype}"
    zone                                    = "${var.cc-restrl-id}"
    active_directory_id                     = "${var.azurerm-tenantid}"
    subscription_id                         = "${var.azurerm-subscriptionid}"
    application_secret                      = "${var.azurerm-clientsecret}"
    application_id                          = "${var.azurerm-clientid}"
}

## Create a Hypervisor Connection Resource Pool based on the previously created Hypervisor Connection
### Set the values in the corresponding CConAzCitrixCloudStuff-hyp.auto.tfvars.json file
resource "citrix_daas_hypervisor_resource_pool" "TF-CC-HYPV-POOL" {
    name                                    = "${var.cc-hyp-pool-name}"
    hypervisor                              = citrix_daas_hypervisor.TF-CC-HYPV.id
    region                                  = "${var.azurerm-resource_group_location}"
    virtual_network_resource_group          = "${var.azurerm-resource_group_name}"
    virtual_network                         = "${var.azurerm-vnet-name}"
    subnets                                 = [
                                                 "${var.azurerm-subnet-name}"
                                              ]
}

# Creation of the Citrix Cloud Machine Catalog
## Local variables for MC configuration
locals {
}

## Create a Machine Catalog based on the previously created entities
### Set the values in the corresponding CConAzCitrixCloudStuff-mc.auto.tfvars.json file
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}"
            #storage_account          = "${var.azurerm-storage_account_name}"
            #container                = "${var.azurerm-storage_account_container_name}"
            master_image              = "${var.azurerm-masterimage-name}"
            #machine_snapshot         = "${var.azurerm-masterimage-name-ss}"
        }
    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}"
     #main_ou                         = "${var.cc-dg-domain-ou}"
                                   }           
    os_type                             = "${var.cc-mc-os_type}" 
    storage_type                        = "${var.cc-mc-storage_type}" 
    use_managed_disks                   = "${var.cc-mc-use_managed_disks}" 

    /* writeback_cache                  = {
      wbc_disk_storage_type             = "${var.cc-mc-wbc_disk_storage_type}" 
      persist_wbc                       = "${var.cc-mc-wbc_persistence}" 
      persist_os_disk                   = "${var.cc-mc-wbc_persistence_os}" 
      persist_vm                        = "${var.cc-mc-wbc_persistence_vm}" 
      writeback_cache_disk_size_gb      = "${var.cc-mc-wbc_disk_size_gb}" 
      storage_cost_saving               = "${var.cc-mc-wbc_cost_saving}" 
                                   } */
  }
}

# Creation of the Citrix Cloud Delivery Group
## Local variables for DG configuration
locals {
}

## Create object for 2min waiting time - needed for backend operations to complete its configurations
resource "time_sleep" "wait_2_minutes_dg" {
  create_duration                           = "120s"
}

## Create a Delivery Group based on the previously created entities
### Set the values in the corresponding dg.auto.tfvars.json file
resource "citrix_daas_delivery_group" "TF-CC-DG" {
  depends_on = [ citrix_daas_machine_catalog.TF-CC-MC, time_sleep.wait_2_minutes_dg ]
    name                                    = "${var.cc-dg-name}"
    description                             = "${var.cc-dg-description}"
    associated_machine_catalogs             = [
        {
       machine_catalog                         = citrix_daas_machine_catalog.TF-CC-MC.id
       machine_count                           = "${var.cc-mc-machine_count}"
        }
    ]
    autoscale_enabled                                       = "${var.cc-dg-autoscale_usage}"
    users                                                   = var.cc-dg-users-upn
    autoscale_settings = {
        autoscale_display_name                              = "${var.cc-dg-autoscale_display_name}"
        disconnect_peak_idle_session_after_seconds          = "${var.cc-dg-autoscale_disconnect_peak_idle_session_after_seconds}"
        disconnect_off_peak_idle_session_after_seconds      = "${var.cc-dg-autoscale_disconnect_off_peak_idle_session_after_seconds}"
        log_off_peak_disconnected_session_after_seconds     = "${var.cc-dg-autoscale_log_off_peak_disconnected_session_after_seconds}"
        log_off_off_peak_disconnected_session_after_seconds = "${var.cc-dg-autoscale_log_off_off_peak_disconnected_session_after_seconds}"
        #peak_disconnect_action                              = "${var.cc-dg-autoscale_peak_disconnect_action}"
        #off_peak_disconnect_action                          = "${var.cc-dg-autoscale_off_peak_disconnect_action}"
        #peak_log_off_action                                 = "${var.cc-dg-autoscale_peak_log_off_action}"
        #off_peak_log_off_action                             = "${var.cc-dg-autoscale_off_peak_log_off_action}"
        #peak_disconnect_timeout_minutes                     = "${var.cc-dg-autoscale_peak_disconnect_timeout_minutes}"
        #off_peak_disconnect_timeout_minutes                 = "${var.cc-dg-autoscale_off_peak_disconnect_timeout_minutes}"
        peak_buffer_size_percent                            = "${var.cc-dg-autoscale_peak_buffer_size_percent}"
        off_peak_buffer_size_percent                        = "${var.cc-dg-autoscale_off_peak_buffer_size_percent}"
        power_time_schemes = [
            {
                days_of_week                                = var.cc-dg-autoscale_days_of_week
                display_name                                = "${var.cc-dg-autoscale_display_name}"
                peak_time_ranges                            = var.cc-dg-autoscale_peak_time_ranges
                pool_size_schedules = [
                {
                    "TimeRange": "00:00-09:00",
                    "PoolSize": 0
                },
                {
                    "TimeRange": "09:00-17:00",
                    "PoolSize": 1
                },
                {
                    "TimeRange": "17:00-00:00",
                    "PoolSize": 0
                }
                ],
                pool_using_percentage                       = "${var.cc-dg-autoscale_pool_using_percentage}"
            },
        ]
    }
}

User Feedback


@Gerhard Krenn this is great! Just one request, if I may, please can you share the provider config that you used for the restapi? i.e. to create the RL in module 2. I was hoping to use a similar method to bind the WEM configuration set. Many thanks 👍

Share this comment


Link to comment
Share on other sites



Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×
×
  • Create New...