At Citrix Synergy 2018, Citrix announced Azure Quick Deployment capability from the Citrix Cloud environment (more on Quick Deployment from Christiaan Brinkhoff, one of Citrix’s CTPs). If you use quick deployment, Citrix Cloud will automatically make the resource groups and virtual networks in Azure. And by using Citrix Cloud itself, you automatically get a Citrix site, Delivery Controller, Workspace (Storefront), and more.

What’s left to automate? The Master Image/ VDA, of course.

During a Synergy session, I heard a presenter say: “The last part is easy. Just create a master image and you’re done.” But creating a good master image can take a lot of time, especially if there is a need for a lot of local applications. It is a best practice to automate the creation of your master image. That’s why I created the Ultimate Golden Image Automation guide. But this guide uses an on-prem VMware and Citrix Provisioning environment. For the cloud, we need a new process, one that uses Microsoft Azure and Citrix Machine Creation Services. A great thing about this is that they both can be managed with Microsoft PowerShell. If we combine the right PowerShell commands we can make a new automatic master image deployment scenario.

The new master image deployment scenario goes as follows:

  1. Create a new Azure Virtual Machine
  2. Deploy VDA and software on the Virtual Machine
  3. Update Machine Catalog from the new Virtual Machine
  4. Delete the Virtual Machine and its resources

Deleting the VM is not entirely necessary but helps reducing cost. Consistency is key during the automation process so starting with a new Azure VM will help.

Install Azure PowerShell Modules

The first thing we need to do is to install the Azure remote management PowerShell modules. This requires PowerShell 5, so if you have a Windows 2012R2 or Windows 8.1 machine you need to update to PowerShell 5, which you can download here. After that you can run the following script to install the modules.

  1. #### Install Azure PowerShell Module
  2. InstallModule PowerShellGet -Force
  3. InstallModule -Name AzureRM

Import Azure PowerShell Modules

After the installation is completed you need to import the module with the following script.

  1. #### Import Azure PowerShell Module
  2. ImportModule -Name AzureRM

Connect to your Azure tenant

Now, we need to connect to the Azure tenant so that we can deploy a virtual machine. The script below will ask for your credentials. You could change the script to make an object from your credentials, so it connects automatically (also refer to the last paragraph in this post: Security in the Cloud).

  1. #### Connect to Azure Tenant
  2. $Azurecred = getcredentials
  3.  ConnectAzureRmAccount -Credential $Azurecred

Create new Azure Virtual Machine

Now that we’re connected, we need to create the new Virtual Machine that is to become the new master image. We do this with the following script.

  1. #### Create Azure VM
  2. $ResourceGroup = “myRG”          ### Resource group name
  3. $VMName = “myVM”                 ### VM Name, example VDI-Master
  4. $Location = ‘myLocation’         ### Azure RG location, example westeurope
  5. $VirtualNetwork = “myNetwork”    ### Azure Virtual Network name
  6. $Subnet = “mySubnet”             ### Azure Subnet name
  7. $VMimage = “myImage”             ### Imagename, example VDI = MicrosoftWindowsDesktop:Windows-10:RS3-Pro:latest, XenApp = MicrosoftWindowsServer:WindowsServer:2016-Datacenter:latest
  8. $VMSize = “mySize”               ### VM Size, example Standard_DS2_v2
  9. $VMUserName = “myUser”           ### VM Admin user, example AzureUser
  10. $VMPassword = “myPass”           ### VM Admin password, example Welkom12345!
  11. $VMPass = ConvertTo-SecureString -String $VMPassword -AsPlainText -Force
  12. $VMCred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList ($VMUserName, $VMPass)
  13. NewAzureRmVm ResourceGroupName $ResourceGroup -Name $VMName Location $Location VirtualNetworkName $VirtualNetwork SubnetName $Subnet ImageName $vmimage -Credential $VMcred Size $VMSize

Get Azure Virtual Machine Private IP

Because we’re going to use this new Azure virtual machine in a software deployment scenario, we need to know the private IP address of the virtual machine. This IP address is assigned randomly (DHCP). We can get the private IP address with the following script.

  1. #### Get Azure VM Private IP
  2. $ResourceGroup = “myRG”          ### Resource group name
  3. $VMName = “myVM”                 ### VM Name, example VDI-Master
  4. $nic = GetAzureRmNetworkInterface -name $VMname ResourceGroupName $ResourceGroup
  5. $AzureIP = $nic.IpConfigurations.privateIpAddress
  6. Write-host “The VM $VMname has IP Address $AzureIP”

Disable Firewall on the Azure Virtual Machine

We need to push an automation agent to the Azure Virtual machine so that we can deploy our software. That’s why we temporarily need to disable the Windows Firewall on the Azure virtual machine. It is not possible to run a script directly on an Azure virtual machine, but you can use a Custom Script extension. The script below will create a small PowerShell script in c:\temp. Then upload it to your Azure storage account and then create a Custom Script extension to be run on the Azure Virtual Machine which uses the script from your storage account. After this the Virtual Machine will be rebooted. And now you can push your Automation agent (Ivanti, SCCM, etc..). Don’t forget to enable the firewall once you’re done.

  1. #### Disable Firewall on the machine
  2. $StorageAccountName = “mySA”     ### Azure Storage Account name
  3. $StorageSAKey = ‘mySAKey’        ### Azure Storage Account key, you can find this under keys at your storage account
  4. $ResourceGroup = “myRG”          ### Resource group name
  5. $VMName = “myVM”                 ### VM Name, example VDI-Master
  6. $Location = ‘myLocation’         ### Azure RG location, example westeurope
  7. $ContainerName = “script”
  8. $temp = “C:\Temp”
  9. $testtemp = Test-Path $temp
  10. if (!$Testtemp)
  11.     {
  12.     New-Item -ItemType Directory -Path $temp | out-null
  13.     }
  14. New-Item -ItemType File -Path “$temp\disablefw.ps1” | out-null
  15. $Filecontent = ‘Set-ItemProperty -Path “HKLM:\SYSTEM\CurrentControlSet\services\SharedAccess\Parameters\FirewallPolicy\DomainProfile” -name “EnableFirewall” -Value 0
  16. Set-ItemProperty -Path “HKLM:\SYSTEM\CurrentControlSet\services\SharedAccess\Parameters\FirewallPolicy\PublicProfile” -name “EnableFirewall” -Value 0
  17. Set-ItemProperty -Path “HKLM:\SYSTEM\CurrentControlSet\services\SharedAccess\Parameters\FirewallPolicy\Standardprofile” -name “EnableFirewall” -Value 0′
  18. add-content “$temp\disablefw.ps1” $filecontent
  19. $Localpath = “$temp\disablefw.ps1”
  20. $context = NewAzureStorageContext StorageAccountName $StorageAccountname StorageAccountKey $StorageSAKey
  21. SetAzureRmCurrentStorageAccount Context $Context
  22. NewAzureStorageContainer -name $ContainerName
  23. SetAzureStorageBlobContent -File $LocalPath -container $ContainerName -Force
  24. SetAzureRmVMCustomScriptExtension -Name ‘myScript’ ContainerName $ContainerName FileName “disablefw.ps1” StorageAccountName $StorageAccountName ResourceGroupName $ResourceGroup VMName $VMname Run “disablefw.ps1” Location $Location
  25. RestartAzureRmVM ResourceGroupName $ResourceGroup -Name $VMName

Delete Azure Virtual Machine Public IP

The Azure virtual machine is created with a public IP address by default but we’re not going to use this. The following script will delete the public IP.

  1. #### Delete Public IP Address
  2. $ResourceGroup = “myRG”          ### Resource group name
  3. $VMName = “myVM”                 ### VM Name, example VDI-Master
  4. $nic = GetAzureRmNetworkInterface -name $VMname ResourceGroupName $ResourceGroup
  5. $nic.IpConfigurations.publicipaddress.id = $null
  6. SetAzureRmNetworkInterface NetworkInterface $nic
  7. RemoveAzureRmPublicIpAddress ResourceGroupName $ResourceGroup -Name $VMName -Force

Installation of Software and VDA

Now that we have a new Azure virtual machine, the private IP address, credentials, and the firewall is temporarily turned off. We can start deploying software and, of course, the Citrix VDA Software. You can do this with PowerShell or your own favorite automation product. You can also use my Ultimate Golden Image Automation Guide for software deployment tips and silent parameters. To install the VDA, you can check out this great article by Dennis Span or see the Citrix install command web page. And of course, don’t forget to optimize your image with the Citrix Optimizer and to seal your master image the right way with  BIS-F (Base Image Sealing Framework).

Stop Azure Virtual Machine

After deploying all the software and installing the VDA, it’s important to stop the Azure virtual machine before you update your machine catalog. You can do this with the following script.

  1. #### Stop Azure VM
  2. $ResourceGroup = “myRG”          ### Resource group name
  3. $VMName = “myVM”                 ### VM Name, example VDI-Master
  4. StopAzureRmVM ResourceGroupName $ResourceGroup -name $VMName -Force

Update the Citrix Machine Catalog

Now that we have created a new master image, we need to update the Machine Catalog. If you run your own delivery controller in Azure or even on-premises, you can use the script below on any machine with Citrix Studio installed. If you use Citrix Cloud services you need to install the Citrix Cloud Remote PowerShell SDK. After installing the SDK, you can connect to your Citrix Cloud environment with the Get-XdAuthentication and then run the script.

  1. #### Update MCS Machine Catalog
  2. Add-PSSnapin Citrix*
  3. $ResourceGroup = “myRG”          ### Resource group name
  4. $VMName = “myVM”                 ### VM Name, example VDI-Master
  5. $DDC = “myddc.company.com”       ### Delivery controller FQDN or the Citrix Cloud Connector FQDN if you use Citrix Cloud
  6. $MCName = “myMC”                 ### Machine Catalog name
  7. $AzureNetworkName = “myNetwork”  ### Network name under Azure Hosting Connection in Citrix Studio
  8. $DDC = $DDC+“:80”
  9. $VM = GetAzureRmVM ResourceGroupName $ResourceGroup -Name $VMName
  10. $Diskname = $VM.StorageProfile.OsDisk.Name
  11. $MasterImage = “XDHyp:\HostingUnits\$AzureNetworkName\image.folder\$ResourceGroup.resourcegroup\$Diskname.manageddisk”
  12. SetProvSchemeMetadata  AdminAddress $DDC -Name “ImageManagementPrep_DoImagePreparation” ProvisioningSchemeName $MCname -Value“True”
  13. $ProvScheme = GetProvScheme  AdminAddress $DDC ProvisioningSchemeName $MCName
  14. $ProvSchemeGUID = $ProvScheme.ProvisioningSchemeUid
  15. PublishProvMasterVMImage  AdminAddress $DDC MasterImageVM $MasterImage ProvisioningSchemeName $MCName
  16. GetProvSchemeMasterVMImageHistory  AdminAddress $DDC ProvisioningSchemeUid $ProvSchemeGUID SortBy “Date”
  17. StartBrokerNaturalRebootCycle  AdminAddress $DDC -InputObject @(“$MCName”)

Delete the VM

Now that the catalog is updated, there is no need to keep the Azure Virtual Machine and its resources; we can delete it with the following script:

  1. #### Delete Azure VM and Resources
  2. $ResourceGroup = “myRG”          ### Resource group name
  3. $VMName = “myVM”                 ### VM Name, example VDI-Master
  4. $Diskname = $VM.StorageProfile.OsDisk.Name
  5. RemoveAzureRmVM ResourceGroupName $ResourceGroup -Name $VMName -force
  6. RemoveAzureRmDisk ResourceGroupName $ResourceGroup -Name $Diskname -force
  7. RemoveAzureRmNetworkInterface ResourceGroupName $ResourceGroup -Name $VMname -Force
  8. RemoveAzureRmNetworkSecurityGroup ResourceGroupName $ResourceGroup -Name $VMname -Force

Security in the cloud

When you use the cloud and automation we need to enter credentials a few times. Make sure to never save your credentials as plain text in your scripts. Recently, there has been an increase in Azure account hijackings for mining cryptocurrency. Try to use a password vault or, at the very least, encrypted passwords. I have my script repository in my Ivanti automation library and I use password variables that are stored, encrypted, in the automation database. Another important thing is the Azure virtual machine credentials we created in the new virtual machine step. Once created, these admin credentials are on every machine you produce from the master image. So, don’t forget to disable the credentials with Disable-LocalUser or to remove them with Remove-LocalUser and add a new secure account.

I hope this was informative. For questions or comments you can always give a reaction in the comment section or find me on LinkedIn or Twitter.


Citrix TechBytes – Created by Citrix Experts, made for Citrix Technologists! Learn from passionate Citrix Experts and gain technical insights into the latest Citrix Technologies.

Click here for more TechBytes and subscribe.

Want specific TechBytes? Let us know! tech-content-feedback@citrix.com