I have a blog post about creating a Windows 365 Cloud PC Provisioning Policy using PowerShell. In this post blog, I will demonstrate how to create the provisioning policy using PowerShell and MS Graph API with beta modules for Windows 365 Cloud PC – Frontline Workers.
Example – Each Windows 365 Frontline license can be shared with up to three employees. This means that if you have 30 employees, you only need to purchase 10 licenses to provision the CloudPC for all 30 employees with access over the day. However, note you are buying the frontline license based on the active sessions. You must purchase the license accordingly if you have more than 10 active workers in a shift.
What happens when license are exhausted?
In my demo tenant, I have two licenses for Frontline workers. When I try to log in to the third one (Note I have already logged into 2 active sessions and running them.) Get the following message.
Connect to MS Graph API
Step 1 – Install the MS Graph Powershell Module
#Install Microsoft Graph Module
PS C:WINDOWSsystem32> Install-Module Microsoft.Graph
Step 2 – Connect to scopes and specify which API you wish to authenticate to. If you are only doing read-only operations, I suggest you connect to “CloudPC.Read.All” in our case, we are creating the policy, so we need to change the scope to “CloudPC.ReadWrite.All”
#Read-only
PS C:WINDOWSsystem32> Connect-MgGraph -Scopes "CloudPC.Read.All"
Welcome To Microsoft Graph!
OR
#Read-Write
PS C:WINDOWSsystem32> Connect-MgGraph -Scopes "CloudPC.ReadWrite.All"
Welcome To Microsoft Graph!
Permissions for MS Graph API
Step 3 – Choose between v1.0 (Generally Available) and Beta API versions. Note for Windows 365 Cloud PC the API calls are BETA.
If you are doing on-premise network integration (Azure Network Connection) , then the following additional property and value is required. In my lab, I am leveraging the Microsoft Managed Network, so this is not required.
I hope you will find this helpful information for creating a frontline worker provisioning policy using PowerShell. Please let me know if I have missed any steps or details, and I will be happy to update the post.
We are going to create the following three types of configurations using Terraform:
Azure Virtual Desktop – Personal Desktop (1×1) – Part 1
Azure Virtual Desktop – Pooled Desktop (Multi-Session Full Desktop Experience) – Part 2
Azure Virtual Desktop – Remote App (Multi-Session Application aka Published Apps) – Part 3
Note – We are creating the Pooled RemoteApp in this post and in the subsequent post the other types were. In this post In this post I will not show case the creation of service principal and secret please refer for the Part 1 for that activity.
Pre-requisites
Following are the pre-requisites before you begin
An Azure subscription
The Terraform CLI
The Azure CLI
Permissions within the Azure Subscription for using Terraform
Terraform – Authenticating via Service Principal & Client Secret
Before running any Terraform code the following powershell (Make sure run as administrator) we will execute and store the credentials as enviornment variables. If we do this via the environment variable we dont have to store the below information within the providers.tf file. In the future blog post there are better way to store the below details and I hope to showcase them:
Azure Subcription ID – Azure Portal Subcription copy the ID
Client ID – From the above step you will have the details
Client Secret – From the above step you will have the details
Tenant ID – While creating the Enterprise Apps in ADD you will have the details
Terraform Folder Structure
The following is the folder structure for the terrraform code:
Azure Virtual Desktop Pooled RemoteApp – Create a directory in which the below Terraform code will be published (providers.tf, main.tf, variables.tf and output.tf)
Create a file named main.tf and insert the following code. Let me explain what all we are attempting to accomplish here:
Create a Resource Group
Create a Workspace
Create a Host Pool
Create a Remote Application Group (RAG)
Associate Workspace and RAG
Assign Azure AD Group to the Desktop Application Group (RAG)
Assign Azure AD Group to the Resource Group for RBAC for the Session Host (Virtual Machine User Login)
# Resource group name is output when execution plan is applied.
resource "azurerm_resource_group" "rg" {
name = var.rg_name
location = var.resource_group_location
tags = var.tags
}
# Create AVD workspace
resource "azurerm_virtual_desktop_workspace" "workspace" {
name = var.workspace
resource_group_name = azurerm_resource_group.rg.name
location = azurerm_resource_group.rg.location
friendly_name = "${var.prefix} Workspace"
description = "${var.prefix} Workspace"
tags = var.tags
}
# Create AVD host pool
resource "azurerm_virtual_desktop_host_pool" "hostpool" {
resource_group_name = azurerm_resource_group.rg.name
location = azurerm_resource_group.rg.location
name = var.hostpool
friendly_name = var.hostpool
validate_environment = true #[true false]
start_vm_on_connect = true
custom_rdp_properties = "targetisaadjoined:i:1;drivestoredirect:s:*;audiomode:i:0;videoplaybackmode:i:1;redirectclipboard:i:1;redirectprinters:i:1;devicestoredirect:s:*;redirectcomports:i:1;redirectsmartcards:i:1;usbdevicestoredirect:s:*;enablecredsspsupport:i:1;redirectwebauthn:i:1;use multimon:i:1;enablerdsaadauth:i:1;"
description = "${var.prefix} HostPool"
type = "Pooled" #[Pooled or Personal]
preferred_app_group_type = "RailApplications" #[Desktop or RailApplications]
maximum_sessions_allowed = 5 #[Tweak based on your vm tshirt size]
load_balancer_type = "DepthFirst" #[BreadthFirst or DepthFirst]
tags = var.tags
scheduled_agent_updates {
enabled = true
timezone = "AUS Eastern Standard Time" # Update this value with your desired timezone
schedule {
day_of_week = "Saturday"
hour_of_day = 1 #[1 here means 1:00 am]
}
}
}
resource "azurerm_virtual_desktop_host_pool_registration_info" "registrationinfo" {
hostpool_id = azurerm_virtual_desktop_host_pool.hostpool.id
expiration_date = var.rfc3339
}
# Create AVD RAG
resource "azurerm_virtual_desktop_application_group" "rag" {
resource_group_name = azurerm_resource_group.rg.name
host_pool_id = azurerm_virtual_desktop_host_pool.hostpool.id
location = azurerm_resource_group.rg.location
type = "RemoteApp"
name = var.app_group_name
friendly_name = "RemoteApp AppGroup"
description = "${var.prefix} AVD RemoteApp application group"
depends_on = [azurerm_virtual_desktop_host_pool.hostpool, azurerm_virtual_desktop_workspace.workspace]
tags = var.tags
}
# Associate Workspace and DAG
resource "azurerm_virtual_desktop_workspace_application_group_association" "ws-dag" {
application_group_id = azurerm_virtual_desktop_application_group.rag.id
workspace_id = azurerm_virtual_desktop_workspace.workspace.id
}
# Assign AAD Group to the Remote Application Group (RAG)
resource "azurerm_role_assignment" "AVDGroupRemoteAppAssignment" {
scope = azurerm_virtual_desktop_application_group.rag.id
role_definition_name = "Desktop Virtualization User"
principal_id = data.azuread_group.AVDGroup.object_id
}
# Assign AAD Group to the Resource Group for RBAC for the Session Host
resource "azurerm_role_assignment" "RBACAssignment" {
scope = azurerm_resource_group.rg.id
role_definition_name = "Virtual Machine User Login"
principal_id = data.azuread_group.AVDGroup.object_id
}
Note – The individual applications are not published yet. They can be published once you have the session host created. After which, using Terraform, the individual applications can be published too. The exe path of apps needs to be mapped within the operating system. I plan to create a separate blog post on session host creation via Terraform.
Configure AVD – Pooled RemoteApp – variables.tf
Create a file named variables.tf and insert the following code:
variable "resource_group_location" {
default = "australiaeast"
description = "Location of the resource group - Australia East"
}
variable "rg_name" {
type = string
default = "AE-DEV-AVD-01-PO-A-RG"
description = "Name of the Resource group in which to deploy service objects"
}
variable "workspace" {
type = string
description = "Name of the Azure Virtual Desktop workspace"
default = "AE-DEV-AVD-01-WS"
}
variable "hostpool" {
type = string
description = "Name of the Azure Virtual Desktop host pool"
default = "AE-DEV-AVD-01-PO-A-HP"
}
variable "app_group_name" {
description = "Name of the Azure Virtual Desktop application group"
type = string
default = "AE-DEV-AVD-01-RAG"
}
variable "rfc3339" {
type = string
default = "2023-05-20T12:43:13Z" #Update this value with a future date
description = "Registration token expiration"
}
variable "prefix" {
type = string
default = "AE-DEV-AVD-01-HP-"
description = "Prefix of the name of the AVD HostPools"
}
variable "tags" {
type = map(string)
default = {
Environment = "Dev"
Department = "IT"
Location = "AustraliaEast"
ServiceClass = "DEV"
Workload = "Host Pool 01"
}
}
data "azuread_client_config" "AzureAD" {}
data "azuread_group" "AVDGroup" {
display_name = "Win365-Users"
}
Configure AVD – Pooled RemoteApp – output.tf
Create a file named output.tf and insert the following code. This will showcase in the console what is getting deployed in form of a output.
output "azure_virtual_desktop_compute_resource_group" {
description = "Name of the Resource group in which to deploy session host"
value = azurerm_resource_group.rg.name
}
output "azure_virtual_desktop_host_pool" {
description = "Name of the Azure Virtual Desktop host pool"
value = azurerm_virtual_desktop_host_pool.hostpool.name
}
output "azurerm_virtual_desktop_application_group" {
description = "Name of the Azure Virtual Desktop DAG"
value = azurerm_virtual_desktop_application_group.rag.name
}
output "azurerm_virtual_desktop_workspace" {
description = "Name of the Azure Virtual Desktop workspace"
value = azurerm_virtual_desktop_workspace.workspace.name
}
output "location" {
description = "The Azure region"
value = azurerm_resource_group.rg.location
}
data "azuread_group" "aad_group" {
display_name = "Win365-Users"
}
output "AVD_user_groupname" {
description = "Azure Active Directory Group for AVD users"
value = data.azuread_group.aad_group.display_name
}
Intialize Terraform – AVD – Pooled RemoteApp
Run terraform init to initialize the Terraform deployment. This command downloads the Azure provider required to manage your Azure resources. (Its pulling the AzureRM and AzureAD)
terraform init -upgrade
Create Terraform Execution Plan – AVD – Pooled RemoteApp
Run terraform plan to create an execution plan.
terraform plan -out mainavdremoteapp.tfplan
Apply Terraform Execution Plan – AVD – Pooled RemoteApp
Run terraform apply to apply the execution plan to your cloud infrastructure.
terraform apply mainavdremoteapp.tfplan
Validate the Output in Azure Portal
Go to the Azure portal, Select Azure Virtual Desktop and Select Host pools, Application Group and Workspace created using Terraform.
Clean-up the above resources (Optional)
If you want to delete all the above resources then you can use the following commands to destroy. Run terraform plan and specify the destroy flag.
terraform plan -destroy -out mainavdremoteapp.destroy.tfplan
Run terraform apply to apply the execution plan.
terraform apply mainavdremoteapp.destroy.tfplan
Quick Start Links
The intention here is to get you quickly started with Terraform on Azure Virtual Desktop Solution:
Description
Links
Setting up your computer to get started with Terrafor using Powershell
I hope you will find this helpful information for getting started with Terraform to deploy the Azure Virtual Desktop – Pooled Remote App. Please let me know if I have missed any steps or details, and I will be happy to update the post.
We are going to create the following three types of configurations using Terraform:
Azure Virtual Desktop – Personal Desktop (1×1) – Part 1
Azure Virtual Desktop – Pooled Desktop (Multi-Session Full Desktop Experience) – Part 2
Azure Virtual Desktop – Remote App (Multi-Session Application aka Published Apps) – Part 3
Note – We are creating the Pooled Desktop in this post and in the subsequent post the other types will be created. In this post In this post I will not show case the creation of service principal and secret please refer for the Part 1 for that activity.
Pre-requisites
Following are the pre-requisites before you begin
An Azure subscription
The Terraform CLI
The Azure CLI
Permissions within the Azure Subscription for using Terraform
Terraform – Authenticating via Service Principal & Client Secret
Before running any Terraform code the following powershell (Make sure run as administrator) we will execute and store the credentials as enviornment variables. If we do this via the environment variable we dont have to store the below information within the providers.tf file. In the future blog post there are better way to store the below details and I hope to showcase them:
Azure Subcription ID – Azure Portal Subcription copy the ID
Client ID – From the above step you will have the details
Client Secret – From the above step you will have the details
Tenant ID – While creating the Enterprise Apps in ADD you will have the details
Terraform Folder Structure
The following is the folder structure for the terrraform code:
Azure Virtual Desktop Pooled Desktop – Create a directory in which the below Terraform code will be published (providers.tf, main.tf, variables.tf and output.tf)
Create a file named main.tf and insert the following code. Let me explain what all we are attempting to accomplish here:
Create a Resource Group
Create a Workspace
Create a Host Pool (Pooled Desktops and Depth first load balancing)
Create a Desktop Application Group (DAG)
Associate Workspace and DAG
Assign Azure AD Group to the Desktop Application Group (DAG)
Assign Azure AD Group to the Resource Group for RBAC for the Session Host (Virtual Machine User Login)
# Resource group name is output when execution plan is applied.
resource "azurerm_resource_group" "rg" {
name = var.rg_name
location = var.resource_group_location
tags = var.tags
}
# Create AVD workspace
resource "azurerm_virtual_desktop_workspace" "workspace" {
name = var.workspace
resource_group_name = azurerm_resource_group.rg.name
location = azurerm_resource_group.rg.location
friendly_name = "${var.prefix} Workspace"
description = "${var.prefix} Workspace"
tags = var.tags
}
# Create AVD host pool
resource "azurerm_virtual_desktop_host_pool" "hostpool" {
resource_group_name = azurerm_resource_group.rg.name
location = azurerm_resource_group.rg.location
name = var.hostpool
friendly_name = var.hostpool
validate_environment = true #[true false]
start_vm_on_connect = true
custom_rdp_properties = "targetisaadjoined:i:1;drivestoredirect:s:*;audiomode:i:0;videoplaybackmode:i:1;redirectclipboard:i:1;redirectprinters:i:1;devicestoredirect:s:*;redirectcomports:i:1;redirectsmartcards:i:1;usbdevicestoredirect:s:*;enablecredsspsupport:i:1;redirectwebauthn:i:1;use multimon:i:1;enablerdsaadauth:i:1;"
description = "${var.prefix} HostPool"
type = "Pooled" #[Pooled or Personal]
maximum_sessions_allowed = 5
load_balancer_type = "DepthFirst" #[BreadthFirst DepthFirst]
tags = var.tags
scheduled_agent_updates {
enabled = true
timezone = "AUS Eastern Standard Time" # Update this value with your desired timezone
schedule {
day_of_week = "Saturday"
hour_of_day = 1 #[1 here means 1:00 am]
}
}
}
resource "azurerm_virtual_desktop_host_pool_registration_info" "registrationinfo" {
hostpool_id = azurerm_virtual_desktop_host_pool.hostpool.id
expiration_date = var.rfc3339
}
# Create AVD DAG
resource "azurerm_virtual_desktop_application_group" "dag" {
resource_group_name = azurerm_resource_group.rg.name
host_pool_id = azurerm_virtual_desktop_host_pool.hostpool.id
location = azurerm_resource_group.rg.location
type = "Desktop"
name = var.app_group_name
friendly_name = "Desktop AppGroup"
description = "${var.prefix} AVD application group"
depends_on = [azurerm_virtual_desktop_host_pool.hostpool, azurerm_virtual_desktop_workspace.workspace]
tags = var.tags
}
# Associate Workspace and DAG
resource "azurerm_virtual_desktop_workspace_application_group_association" "ws-dag" {
application_group_id = azurerm_virtual_desktop_application_group.dag.id
workspace_id = azurerm_virtual_desktop_workspace.workspace.id
}
# Assign AAD Group to the Desktop Application Group (DAG)
resource "azurerm_role_assignment" "AVDGroupDesktopAssignment" {
scope = azurerm_virtual_desktop_application_group.dag.id
role_definition_name = "Desktop Virtualization User"
principal_id = data.azuread_group.AVDGroup.object_id
}
# Assign AAD Group to the Resource Group for RBAC for the Session Host
resource "azurerm_role_assignment" "RBACAssignment" {
scope = azurerm_resource_group.rg.id
role_definition_name = "Virtual Machine User Login"
principal_id = data.azuread_group.AVDGroup.object_id
}
Configure AVD – Pooled Desktop Pool – variables.tf
Create a file named variables.tf and insert the following code:
variable "resource_group_location" {
default = "australiaeast"
description = "Location of the resource group - Australia East"
}
variable "rg_name" {
type = string
default = "AE-DEV-AVD-01-PO-D-RG"
description = "Name of the Resource group in which to deploy service objects"
}
variable "workspace" {
type = string
description = "Name of the Azure Virtual Desktop workspace"
default = "AE-DEV-AVD-01-WS"
}
variable "hostpool" {
type = string
description = "Name of the Azure Virtual Desktop host pool"
default = "AE-DEV-AVD-01-PO-D-HP"
}
variable "app_group_name" {
description = "Name of the Azure Virtual Desktop application group"
type = string
default = "AE-DEV-AVD-01-DAG"
}
variable "rfc3339" {
type = string
default = "2023-05-20T12:43:13Z" #Update this value with a future date
description = "Registration token expiration"
}
variable "prefix" {
type = string
default = "AE-DEV-AVD-01-HP-"
description = "Prefix of the name of the AVD machine(s)"
}
variable "tags" {
type = map(string)
default = {
Environment = "Dev"
Department = "IT"
Location = "AustraliaEast"
ServiceClass = "DEV"
Workload = "Host Pool 01"
}
}
data "azuread_client_config" "AzureAD" {}
data "azuread_group" "AVDGroup" {
display_name = "Win365-Users"
}
Configure AVD – Pooled Desktop Pool – output.tf
Create a file named output.tf and insert the following code. This will showcase in the console what is getting deployed in form of a output.
output "azure_virtual_desktop_compute_resource_group" {
description = "Name of the Resource group in which to deploy session host"
value = azurerm_resource_group.rg.name
}
output "azure_virtual_desktop_host_pool" {
description = "Name of the Azure Virtual Desktop host pool"
value = azurerm_virtual_desktop_host_pool.hostpool.name
}
output "azurerm_virtual_desktop_application_group" {
description = "Name of the Azure Virtual Desktop DAG"
value = azurerm_virtual_desktop_application_group.dag.name
}
output "azurerm_virtual_desktop_workspace" {
description = "Name of the Azure Virtual Desktop workspace"
value = azurerm_virtual_desktop_workspace.workspace.name
}
output "location" {
description = "The Azure region"
value = azurerm_resource_group.rg.location
}
data "azuread_group" "aad_group" {
display_name = "Win365-Users"
}
output "AVD_user_groupname" {
description = "Azure Active Directory Group for AVD users"
value = data.azuread_group.aad_group.display_name
}
Intialize Terraform – AVD – Pooled Desktop Pool
Run terraform init to initialize the Terraform deployment. This command downloads the Azure provider required to manage your Azure resources. (Its pulling the AzureRM and AzureAD)
terraform init -upgrade
Create Terraform Execution Plan – AVD – Pooled Desktop Pool
Run terraform plan to create an execution plan.
terraform plan -out mainavdpooled.tfplan
Apply Terraform Execution Plan – AVD – Pooled Desktop Pool
Run terraform apply to apply the execution plan to your cloud infrastructure.
terraform apply mainavdpooled.tfplan
Validate the Output in Azure Portal
Go to the Azure portal, Select Azure Virtual Desktop and Select Host pools, Application Group and Workspace created using Terraform.
Clean-up the above resources (Optional)
If you want to delete all the above resources then you can use the following commands to destroy. Run terraform plan and specify the destroy flag.
terraform plan -destroy -out mainavdpooled.destroy.tfplan
Run terraform apply to apply the execution plan.
terraform apply mainavdpooled.destroy.tfplan
Quick Start Links
The intention here is to get you quickly started with Terraform on Azure Virtual Desktop Solution:
Description
Links
Setting up your computer to get started with Terrafor using Powershell
I hope you will find this helpful information for getting started with Terraform to deploy the Azure Virtual Desktop – Pooled Desktop. Please let me know if I have missed any steps or details, and I will be happy to update the post.
In the past, I have written blog posts on creating the Azure Virtual Desktop (AVD) solution using PowerShell. In this blog post series, I will demonstrate how to create the AVD Host Pool, Application Group and Workspace using Terraform. Terraform is an open-source infrastructure as code (IaC) software tool that enables you to safely and predictably create, change, and improve infrastructure. Terraform can be used to manage infrastructure on various cloud providers, including Azure.
We are going to create the following three types of configurations using Terraform:
Azure Virtual Desktop – Personal Desktop (1×1)
Azure Virtual Desktop – Pooled Desktop (Multi-Session Full Desktop Experience)
Note – We are creating the Personal Desktop in this post, and the other desktop/app types will be created in the subsequent post. In this post, I will showcase the creation of service principal and secret. In the next part, we shall move straight onto the Terraform code. Referring to part 1 in the series will be essential if you are doing the basics.
Permissions within the Azure Subscription for using Terraform
Terraform Service Principal and Secret (Azure AD – App Registrations)
Let’s pre-create the application ID and client secret we will use to connect and leverage the Terraform code in VScode.
Connect to Azure Portal and go to Azure Active Directory
Click on App Registrations and select – New Registration
Give the App a Name – Terraform
You will get two important information created for later use within Terraform
Application ID
Tenant ID
Now let’s grant this App Terraform Permission. Click on Add a permission and select MS Graph and search for AppRoleAssignment.ReadWrite.All and select read/write permissions and Add Permissions
Select Grant admin consent for domain
We are using client secret so now lets enable that. Click on Certificates & Secrets – Client Secrets and select New client secret\
Give it a name (Terra-secret) and expiry date (12 months)
Copy the Secret Value
Terraform – Authenticating via Service Principal & Client Secret
In the above step, we created the Service Principal and Client secret. We will use it before running any Terraform code in PowerShell (Ensure to run as administrator). We will execute and store the credentials as environment variables. If we do this via the environment variable, we don’t have to store the below information within the providers.tf file. In a future blog post, there are better ways to keep the below details, and I hope to showcase them:
Azure Subscription ID – Azure Portal Subscription copy the ID.
Client ID – From the above step, you will have the details
Client Secret – From the above step, you will have the details
Tenant ID – While creating the Enterprise Apps in Azure AD, you will have the details
Terraform Folder Structure
The following is the folder structure for the Terraform code:
Azure Virtual Desktop Personal Pool – Create a directory in which the below Terraform code will be published (providers.tf, main.tf, variables.tf and output.tf)
Create a file named main.tf and insert the following code. Let me explain what we are attempting to accomplish here: (Note I have # commented the lines with additional info)
Create a Resource Group
Create a Workspace
Create a Host Pool
Create a Desktop Application Group (DAG)
Associate Workspace and DAG
Assign Azure AD Group to the Desktop Application Group (DAG)
Assign Azure AD Group to the Resource Group for RBAC for the Session Host (Virtual Machine User Login)
# Resource group name is output when execution plan is applied.
resource "azurerm_resource_group" "rg" {
name = var.rg_name
location = var.resource_group_location
tags = var.tags
}
# Create AVD workspace
resource "azurerm_virtual_desktop_workspace" "workspace" {
name = var.workspace
resource_group_name = azurerm_resource_group.rg.name
location = azurerm_resource_group.rg.location
friendly_name = "${var.prefix} Workspace"
description = "${var.prefix} Workspace"
tags = var.tags
}
# Create AVD host pool
resource "azurerm_virtual_desktop_host_pool" "hostpool" {
resource_group_name = azurerm_resource_group.rg.name
location = azurerm_resource_group.rg.location
name = var.hostpool
friendly_name = var.hostpool
validate_environment = true #[true false]
start_vm_on_connect = true
custom_rdp_properties = "targetisaadjoined:i:1;drivestoredirect:s:*;audiomode:i:0;videoplaybackmode:i:1;redirectclipboard:i:1;redirectprinters:i:1;devicestoredirect:s:*;redirectcomports:i:1;redirectsmartcards:i:1;usbdevicestoredirect:s:*;enablecredsspsupport:i:1;redirectwebauthn:i:1;use multimon:i:1;enablerdsaadauth:i:1;"
description = "${var.prefix} HostPool"
type = "Personal" #[Pooled or Personal]
personal_desktop_assignment_type = "Automatic"
load_balancer_type = "Persistent"
tags = var.tags
scheduled_agent_updates {
enabled = true
timezone = "AUS Eastern Standard Time" # Update this value with your desired timezone
schedule {
day_of_week = "Saturday"
hour_of_day = 1 #[1 here means 1:00 am]
}
}
}
resource "azurerm_virtual_desktop_host_pool_registration_info" "registrationinfo" {
hostpool_id = azurerm_virtual_desktop_host_pool.hostpool.id
expiration_date = var.rfc3339
}
# Create AVD DAG
resource "azurerm_virtual_desktop_application_group" "dag" {
resource_group_name = azurerm_resource_group.rg.name
host_pool_id = azurerm_virtual_desktop_host_pool.hostpool.id
location = azurerm_resource_group.rg.location
type = "Desktop"
name = var.app_group_name
friendly_name = "Desktop AppGroup"
description = "${var.prefix} AVD application group"
depends_on = [azurerm_virtual_desktop_host_pool.hostpool, azurerm_virtual_desktop_workspace.workspace]
tags = var.tags
}
# Associate Workspace and DAG
resource "azurerm_virtual_desktop_workspace_application_group_association" "ws-dag" {
application_group_id = azurerm_virtual_desktop_application_group.dag.id
workspace_id = azurerm_virtual_desktop_workspace.workspace.id
}
# Assign AAD Group to the Desktop Application Group (DAG)
resource "azurerm_role_assignment" "AVDGroupDesktopAssignment" {
scope = azurerm_virtual_desktop_application_group.dag.id
role_definition_name = "Desktop Virtualization User"
principal_id = data.azuread_group.AVDGroup.object_id
}
# Assign AAD Group to the Resource Group for RBAC for the Session Host
resource "azurerm_role_assignment" "RBACAssignment" {
scope = azurerm_resource_group.rg.id
role_definition_name = "Virtual Machine User Login"
principal_id = data.azuread_group.AVDGroup.object_id
}
Configure AVD – Personal Desktop Pool – variables.tf
Create a file named variables.tf and insert the following code. I have followed a naming convention that includes the following:
AE – Australia East
Environment – PROD or DEV
Instance – 01
RG – Resource Group
WS – Workspace
DAG – Desktop Application Group
variable "resource_group_location" {
default = "australiaeast"
description = "Location of the resource group - Australia East"
}
variable "rg_name" {
type = string
default = "AE-DEV-AVD-01-RG"
description = "Name of the Resource group in which to deploy service objects"
}
variable "workspace" {
type = string
description = "Name of the Azure Virtual Desktop workspace"
default = "AE-DEV-AVD-01-WS"
}
variable "hostpool" {
type = string
description = "Name of the Azure Virtual Desktop host pool"
default = "AE-DEV-AVD-01-PE-D-HP"
}
variable "app_group_name" {
description = "Name of the Azure Virtual Desktop application group"
type = string
default = "AE-DEV-AVD-01-DAG"
}
variable "rfc3339" {
type = string
default = "2023-05-20T12:43:13Z" #Update this value with a future date
description = "Registration token expiration"
}
variable "prefix" {
type = string
default = "AE-DEV-AVD-01-HP-"
description = "Prefix of the name of the AVD machine(s)"
}
variable "tags" {
type = map(string)
default = {
Environment = "Dev"
Department = "IT"
Location = "AustraliaEast"
ServiceClass = "DEV"
Workload = "Host Pool 01"
}
}
data "azuread_client_config" "AzureAD" {}
data "azuread_group" "AVDGroup" {
display_name = "Win365-Users"
}
Configure AVD – Personal Desktop Pool – output.tf
Create a file named output.tf and insert the following code. This will showcase in the console what is getting deployed as output.
output "azure_virtual_desktop_compute_resource_group" {
description = "Name of the Resource group in which to deploy session host"
value = azurerm_resource_group.rg.name
}
output "azure_virtual_desktop_host_pool" {
description = "Name of the Azure Virtual Desktop host pool"
value = azurerm_virtual_desktop_host_pool.hostpool.name
}
output "azurerm_virtual_desktop_application_group" {
description = "Name of the Azure Virtual Desktop DAG"
value = azurerm_virtual_desktop_application_group.dag.name
}
output "azurerm_virtual_desktop_workspace" {
description = "Name of the Azure Virtual Desktop workspace"
value = azurerm_virtual_desktop_workspace.workspace.name
}
output "location" {
description = "The Azure region"
value = azurerm_resource_group.rg.location
}
data "azuread_group" "aad_group" {
display_name = "Win365-Users"
}
output "AVD_user_groupname" {
description = "Azure Active Directory Group for AVD users"
value = data.azuread_group.aad_group.display_name
}
Intialize Terraform – AVD – Personal Desktop Pool
Run the following command to initialize the Terraform deployment. This command downloads the Azure provider required to manage your Azure resources.
terraform init -upgrade
Create Terraform Execution Plan – AVD – Personal Desktop Pool
Run the following command to create an execution plan.
terraform plan -out mainavdpersonal.tfplan
Apply Terraform Execution Plan – AVD – Personal Desktop Pool
Run the following command to apply the execution plan to your cloud infrastructure.
terraform apply mainavdpersonal.tfplan
Validate the Output in Azure Portal
Go to the Azure portal, Select Azure Virtual Desktop and Select Host pools, Application Group and Workspace created using Terraform.
Clean-up the above resources (Optional)
If you want to delete all the above resources then you can use the following commands to destroy. Run terraform plan and specify the destroy flag.
terraform plan -destroy -out mainavdpersonal.destroy.tfplan
Run terraform apply to apply the execution plan.(Destroy)
terraform apply mainavdpersonal.destroy.tfplan
Quick Start Links
The intention here is to get you quickly started with Terraform on Azure Virtual Desktop Solution:
Description
Links
Setting up your computer to get started with Terrafor using Powershell
I hope you will find this helpful information for getting started with Terraform to deploy the Azure Virtual Desktop – Personal Desktop Pool. Please let me know if I have missed any steps or details, and I will be happy to update the post.
Microsoft Security Response Center (MSRC) publishes a monthly consolidated report for all the Critical, Important, Moderate and Low security vulnerabilities affecting Microsoft products. The information posted there helps organizations manage security risks and keep their systems protected.
Looking at the overall release notes for all the affected products (30+ products) and filtering the OS you are interested in can become overwhelming. E.g. You are only interested in products Operating Systems – Windows 11 22h2 or Windows Server 2016/2019/2022. Not saying other information is not essential, but imagine you are only responsible for the Operating Sytems. In such situations, you can use the below script to get a monthly report of CVE (Critical & Important) for a particular OS over to you in an email.
Prerequsites
You will need the MSRCSecurity PowerShell module. Run the command to install the module; further, you can import the module within the script.
Following are the Operating System (OS) products you can fetch the information against. If you want details for any other operating systems, copy that value and, in my script, paste it under the variable $ClientOS_Type. In my demonstration, we used “Windows 11 Version 22H2 for x64-based Systems”
$ID = Get-MsrcCvrfDocument -ID $Month
$ID.ProductTree.FullProductName
ProductID Value
--------- -----
10049 Windows Server 2008 R2 for x64-based Systems Service Pack 1 (Server Core installation)
10051 Windows Server 2008 R2 for x64-based Systems Service Pack 1
10287 Windows Server 2008 for 32-bit Systems Service Pack 2 (Server Core installation)
10378 Windows Server 2012
10379 Windows Server 2012 (Server Core installation)
10407 Microsoft Outlook 2013 RT Service Pack 1
10483 Windows Server 2012 R2
10543 Windows Server 2012 R2 (Server Core installation)
10601 Microsoft Office 2013 Service Pack 1 (32-bit editions)
10602 Microsoft Office 2013 Service Pack 1 (64-bit editions)
10603 Microsoft Office 2013 RT Service Pack 1
10611 Microsoft Office Web Apps Server 2013 Service Pack 1
10612 Microsoft SharePoint Foundation 2013 Service Pack 1
10654 Microsoft Excel 2013 Service Pack 1 (32-bit editions)
10655 Microsoft Excel 2013 Service Pack 1 (64-bit editions)
10656 Microsoft Excel 2013 RT Service Pack 1
10729 Windows 10 for 32-bit Systems
10735 Windows 10 for x64-based Systems
10739 Microsoft Excel 2016 (32-bit edition)
10740 Microsoft Excel 2016 (64-bit edition)
10753 Microsoft Office 2016 (32-bit edition)
10754 Microsoft Office 2016 (64-bit edition)
10765 Microsoft Outlook 2016 (32-bit edition)
10766 Microsoft Outlook 2016 (64-bit edition)
10810 Microsoft Outlook 2013 Service Pack 1 (32-bit editions)
10811 Microsoft Outlook 2013 Service Pack 1 (64-bit editions)
10816 Windows Server 2016
10852 Windows 10 Version 1607 for 32-bit Systems
10853 Windows 10 Version 1607 for x64-based Systems
10855 Windows Server 2016 (Server Core installation)
10950 Microsoft SharePoint Enterprise Server 2016
11099 Microsoft SharePoint Enterprise Server 2013 Service Pack 1
11568 Windows 10 Version 1809 for 32-bit Systems
11569 Windows 10 Version 1809 for x64-based Systems
11570 Windows 10 Version 1809 for ARM64-based Systems
11571 Windows Server 2019
11572 Windows Server 2019 (Server Core installation)
11573 Microsoft Office 2019 for 32-bit editions
11574 Microsoft Office 2019 for 64-bit editions
11575 Microsoft Office 2019 for Mac
11585 Microsoft SharePoint Server 2019
11600 Microsoft Visual Studio 2017 version 15.9 (includes 15.0 - 15.8)
11605 Microsoft Office Online Server
11655 Microsoft Edge (Chromium-based)
11664 Microsoft Dynamics 365 (on-premises) version 9.0
11726 OneDrive for Android
11762 Microsoft 365 Apps for Enterprise for 32-bit Systems
11763 Microsoft 365 Apps for Enterprise for 64-bit Systems
11800 Windows 10 Version 20H2 for x64-based Systems
11801 Windows 10 Version 20H2 for 32-bit Systems
11802 Windows 10 Version 20H2 for ARM64-based Systems
11902 Microsoft Malware Protection Engine
11921 Microsoft Dynamics 365 (on-premises) version 9.1
11923 Windows Server 2022
11924 Windows Server 2022 (Server Core installation)
11926 Windows 11 version 21H2 for x64-based Systems
11927 Windows 11 version 21H2 for ARM64-based Systems
11929 Windows 10 Version 21H2 for 32-bit Systems
11930 Windows 10 Version 21H2 for ARM64-based Systems
11931 Windows 10 Version 21H2 for x64-based Systems
11935 Microsoft Visual Studio 2019 version 16.11 (includes 16.0 - 16.10)
11951 Microsoft Office LTSC for Mac 2021
11952 Microsoft Office LTSC 2021 for 64-bit editions
11953 Microsoft Office LTSC 2021 for 32-bit editions
11961 Microsoft SharePoint Server Subscription Edition
11969 Microsoft Visual Studio 2022 version 17.0
11987 Azure HDInsights
12051 Microsoft Visual Studio 2022 version 17.2
12085 Windows 11 Version 22H2 for ARM64-based Systems
12086 Windows 11 Version 22H2 for x64-based Systems
12097 Windows 10 Version 22H2 for x64-based Systems
12098 Windows 10 Version 22H2 for ARM64-based Systems
12099 Windows 10 Version 22H2 for 32-bit Systems
12129 Microsoft Visual Studio 2022 version 17.4
12137 CBL Mariner 1.0 x64
12138 CBL Mariner 1.0 ARM
12139 CBL Mariner 2.0 x64
12140 CBL Mariner 2.0 ARM
12142 Microsoft Edge (Chromium-based) Extended Stable
12155 Microsoft Office for Android
12156 Microsoft Office for Universal
12167 Microsoft Visual Studio 2022 version 17.5
12169 OneDrive for MacOS Installer
12170 OneDrive for iOS
12171 Azure Service Fabric 9.1 for Windows
12172 Azure Service Fabric 9.1 for Ubuntu
12173 Snipping Tool
12174 Snip & Sketch for Windows 10
9312 Windows Server 2008 for 32-bit Systems Service Pack 2
9318 Windows Server 2008 for x64-based Systems Service Pack 2
9344 Windows Server 2008 for x64-based Systems Service Pack 2 (Server Core installation)
Variable Region
Delcare all the variable within this section. Lets take a look at what we are declaring within the script:
The MSRC website releases the montly report in the following yyyy-MM format
#Month Format for MSRC
$Month = Get-Date -Format 'yyyy-MMM'
The operating system we want to focus on and leave the rest. If you are interested in any other OS, change the value from the above prerequisites, and it will give you Critical/Import vulnerabilities for that OS or product.
# Enter the Operating System you specifically want to focus on
$ClientOS_Type = "Windows 11 Version 22H2 for x64-based Systems"
Enter the details for sending the email (subject line, to and from etc.) for the report
#Email Details
$Recipients = "askaresh@askaresh.com", "someone@askaresh.com"
$Sender = "cve-report@askaresh.com"
$SMTP_Server = "smtp.askaresh.com"
$Subject = 'CVE List for Windows 11 22H2 on '+$Month
HTML report formatting (CSS, Title of the report, Logo and Header) information
I hope you will find this helpful information to gather Microsoft Security vulnerability reports for a specific operating system using PowerShell. Please let me know if I have missed any steps or details, and I will be happy to update the post.
I have written various individual blog posts on PowerShell creation of all configurational task for Windows 365 Cloud PC under Microsoft Endpoint Portal (MEM).
Based on public demand, I want to create a consolidated post for all the scripts and configuration items that can get you started with Windows 365 Cloud PC using PowerShell: (Of course all the below features can also be configured using the UI, however below is the guidance strictly using PowerShell)
PowerShell links to my blog post
Following are the links to my blog post for each and individual task:
I promise you once you have done the hard work, you can get up and running in a few hours using all the above PowerShell scripts with Windows 365 Cloud PC.
GitHub Link
Here is the repo with all the scripts and more – askaresh/avdwin365mem (github.com). A big thanks to Andrew Taylor for collabrating and updating the Provisioning policy script with the SSO details that was release in late Nov 2022.
I hope you will find this helpful information for all things PowerShell w.r.t Windows 365 Cloud PC. I will update the post if I publish or update more information.
Do you want to deploy an Azure Virtual Desktop – Host pools quickly and want a starting point for a golden image? Look no further in this blog post. I will show you how to create a golden image using PowerShell in no more than 10 min.
I will break down the code block into smaller chunks first to explain the critical bits, and in the end, I will post the entire code block that can be run all at once. In this way, explaining block by block becomes easier than pasting one single block.
Pre-requisites
Following are the pre-requisites before you begin
PowerShell 5.1 and above
Azure Subscription
Permissions within the Auzre Subscription for Azure Compute
Assumption
You have an existing Resource Group (RG)
You have an existing Azure Virtual Network (VNET)
You have an existing workload subnet within the VNET
Identify the VM Size you will be using for the golden image
We are going to use the Windows 11 22H2 Mutli-session – win11-22h2-avd within this script
Variable Region
Delcare all the variable within this section. Lets take a look at what we are declaring within the script:
Existing Resource Group within the Azure Subscription (AZ104-RG)
A location where you are deploying this virtual machine (Australia East)
Name of the golden image virtual machine (VM03)
NIC Interface name for the virtual machine (VM03-nic)
RG of the VNET (In my case they are same AZ104-RG, they can be seperate too and hence a independent variable)
Name of the existing subnet within the vNET (AZ104-VDI-Workload-L1)
Name of the existing VNET (AZ104-RG-vnet)
Mapping of the exisitng VNET
Mapping of the existing subnet
T-shirt size of the golden image we are deploying (Standard_D2s_v3)
Gallery details of the image
Published – MicrosoftWindowsDesktop
Offer – windows-11
SKU – win11-22h2-avd
version – Offcourse latest
Get credentials – A local admin account is created on the golden image (A input box to capture the uisername and password)
# Existing Resource Group to deploy the VM
$rgName = "AZ104-RG"
# Geo Location to deploy the VM
$location = "Australia East"
# Image template name
$vmName = "VM03"
# Networking Interfance Name for the VM
$nicName = "$vmName-nic"
# Resource Group for VNET
$vnetrgName = "AZ104-RG"
# Existing Subnet Name
$Existsubnetname = "AZ104-VDI-Workload-L1"
# Existing VNET Name
$Existvnetname = "AZ104-RG-vnet"
# Existing VNET where we are deploying this Virtual Machine
$vnet = Get-AzVirtualNetwork -Name $Existvnetname -ResourceGroupName $vnetrgName
# Existing Subnet within the VNET for the this virtual machine
$subnet = Get-AzVirtualNetworkSubnetConfig -Name $Existsubnetname -VirtualNetwork $vnet
# T-shirt size of the VM
$vmSize = "Standard_D2s_v3"
# Gallery Publisher of the Image - Microsoft
$publisher = "MicrosoftWindowsDesktop"
# Version of Windows 10/11
$offer = "windows-11"
# The SKY ending with avd are the multi-session
$sku = "win11-22h2-avd"
# Choosing the latest version
$version = "latest"
# Setting up the Local Admin on the VM
$cred = Get-Credential `
-Message "Enter a username and password for the virtual machine."
Execution block
Execution code block within this section. Lets take a look at what we are we executing within the script:
First its creating the network interface for the virtual machine (VM03)
Next, under the variable $VM all virtual machine configurations
Tshirt size of the virtual machine
Credentials for the local admin (username/password)
The network interface assignment along with the delete option (Note delete option is essential or/else during deletion of VM it will not delete the network interface)
The gallery image, sku, offer from the Microsoft Market Place gallery
The os disk assignment along with the delete option (Note delete option is essential or/else during deletion of VM it will not delete the disk)
The configuration around “Trusted Platform” and enabling of TPM and Secure Boot
The final command to create the virtual machine with all the above configurations
# Create New network interface for the virtual machine
$NIC = New-AzNetworkInterface -Name $nicName -ResourceGroupName $vnetrgName -Location $location -Subnet $subnet
# Creation of the new virtual machine with delete option for Disk/NIC together
$vm = New-AzVMConfig -VMName $vmName -VMSize $vmSize
$vm = Set-AzVMOperatingSystem `
-VM $vm -Windows `
-ComputerName $vmName `
-Credential $cred `
-ProvisionVMAgent `
-EnableAutoUpdate
# Delete option for NIC
$vm = Add-AzVMNetworkInterface -VM $vm `
-Id $NIC.Id `
-DeleteOption "Delete"
$vm = Set-AzVMSourceImage -VM $vm `
-PublisherName $publisher `
-Offer $offer `
-Skus $sku `
-Version $version
# Delete option for Disk
$vm = Set-AzVMOSDisk -VM $vm `
-StorageAccountType "StandardSSD_LRS" `
-CreateOption "FromImage" `
-DeleteOption "Delete"
# The sauce around enabling the Trusted Platform
$vm = Set-AzVmSecurityProfile -VM $vm `
-SecurityType "TrustedLaunch"
# The sauce around enabling TPM and Secure Boot
$vm = Set-AzVmUefi -VM $vm `
-EnableVtpm $true `
-EnableSecureBoot $true
New-AzVM -ResourceGroupName $rgName -Location $location -VM $vm
# Step 1: Import module
#Import-Module Az.Accounts
# Connect to the Azure Subcription
#Connect-AzAccount
# Get existing context
$currentAzContext = Get-AzContext
# Your subscription. This command gets your current subscription
$subscriptionID=$currentAzContext.Subscription.Id
# Command to get the Multi-session Image in Gallery
# Details from this command will help in filling out variables below on Gallery Image
# Get-AzVMImageSku -Location australiaeast -PublisherName MicrosoftWindowsDesktop -Offer windows-11
# Existing Resource Group to deploy the VM
$rgName = "AZ104-RG"
# Geo Location to deploy the VM
$location = "Australia East"
# Image template name
$vmName = "VM03"
# Networking Interfance Name for the VM
$nicName = "$vmName-nic"
# Resource Group for VNET
$vnetrgName = "AZ104-RG"
# Existing Subnet Name
$Existsubnetname = "AZ104-VDI-Workload-L1"
# Existing VNET Name
$Existvnetname = "AZ104-RG-vnet"
# Existing VNET where we are deploying this Virtual Machine
$vnet = Get-AzVirtualNetwork -Name $Existvnetname -ResourceGroupName $vnetrgName
# Existing Subnet within the VNET for the this virtual machine
$subnet = Get-AzVirtualNetworkSubnetConfig -Name $Existsubnetname -VirtualNetwork $vnet
# T-shirt size of the VM
$vmSize = "Standard_D2s_v3"
# Gallery Publisher of the Image - Microsoft
$publisher = "MicrosoftWindowsDesktop"
# Version of Windows 10/11
$offer = "windows-11"
# The SKY ending with avd are the multi-session
$sku = "win11-22h2-avd"
# Choosing the latest version
$version = "latest"
# Setting up the Local Admin on the VM
$cred = Get-Credential `
-Message "Enter a username and password for the virtual machine."
# Create New network interface for the virtual machine
$NIC = New-AzNetworkInterface -Name $nicName -ResourceGroupName $vnetrgName -Location $location -Subnet $subnet
# Creation of the new virtual machine with delete option for Disk/NIC together
$vm = New-AzVMConfig -VMName $vmName -VMSize $vmSize
$vm = Set-AzVMOperatingSystem `
-VM $vm -Windows `
-ComputerName $vmName `
-Credential $cred `
-ProvisionVMAgent `
-EnableAutoUpdate
# Delete option for NIC
$vm = Add-AzVMNetworkInterface -VM $vm `
-Id $NIC.Id `
-DeleteOption "Delete"
$vm = Set-AzVMSourceImage -VM $vm `
-PublisherName $publisher `
-Offer $offer `
-Skus $sku `
-Version $version
# Delete option for Disk
$vm = Set-AzVMOSDisk -VM $vm `
-StorageAccountType "StandardSSD_LRS" `
-CreateOption "FromImage" `
-DeleteOption "Delete"
# The sauce around enabling the Trusted Platform
$vm = Set-AzVmSecurityProfile -VM $vm `
-SecurityType "TrustedLaunch"
# The sauce around enabling TPM and Secure Boot
$vm = Set-AzVmUefi -VM $vm `
-EnableVtpm $true `
-EnableSecureBoot $true
New-AzVM -ResourceGroupName $rgName -Location $location -VM $vm
Note – It will give you a pop-up box for entering the username and password for the local account, and in under 10 mins you will see your virtual machine within the Azure portal
Next Steps on Golden Image
Now that the virtual machine is ready following are the next steps involved:
Using Azure Bastion console and installing all the required applications
Generalize and sysprep and shutdown the image
Capture the image to the Azure Compute Galleries
Deploy within the Azure Virtual Desktop
I hope you will find this helpful information for deploying a golden image within Azure – Virtual Machine to deploy the Azure Virtual Desktop – Host Pools. If you want to see a Powershell version of the host pool activities, leave me a comment below or on my socials. Please let me know if I have missed any steps or details, and I will be happy to update the post.
You have a large VMware App Volumes environment and have backed up your writable volumes using the capabilities provided in the App Volumes Manager. (You are doing the right thing!)
AV Manager – WV Backup Config
We decided to perform an audit on the backup of the writable volumes within the App Volumes Manager 2.18.10 and the VSAN Datastore. You can export all the writable volumes to a CSV using the API. My script here will provide you with a complete outlook for conducting your analysis. Now exclude your group entitlements from the list, leaving you with the total number of writable volumes within your environment. Ideally, you are after the same number of writable volumes on the VSAN datastore. (Of course, if everything is going well in the backup world!)
In my case, we observed more than 300+ missing writable volumes between the exported CSV and the VSAN datastore. Let the investigations begin – within the production.log, we could see the backup was happening, but the challenge of a large environment is impossible to track all the backup occurring just by looking at the logs. Feature request to VMW – A dedicated backup log showcases the entire environment’s status. We eventually ended up with a GSS case after few months of back and forth and the logs exchange, we finally got a working solution. This closed the mystery of the missing backup of the writable volumes.
Solution
Go the the SQL database of the App Volumes Manager. Select the DB and New Query.
AV Database – Microsoft SQL
Enter the following query and hit execute. Now this will change the default writable volumes batch size(writables_backup_batch_size) from 5 to 25. Note the value of the batch size was tweaked multiple times, we first went with 10, which drastically reduced the missing backup. However, a few were still missing and not getting backup. The final number for our environment was 25 got all the writable volumes backup.
Disclaimer – This tweak was required for a large App Volumes environment. Please consult with VMware Support before making any changes to your setup or Database. If it works for me doesn’t mean it will work for you. The value can differ based on the size of the enivronment.
I hope you will find this helpful information on your VMware App Volumes backup strategy. Please let me know if you have observed any issues like these, and would like to share your story?
Often within the VMware App Volumes Manager (AVM), Writable Volumes will show up as Status – Orphaned. Let’s take a look at the following topics:
What is Orphaned Writable Volumes?
Script to delete them from the App Volumes Managers
What is Orphaned Writable Volumes?
App Volumes Manager is integrated with Microsoft Active Directory (AD), and it’s in continuous synchronization. Whenever an end-user account gets disabled into the AD during the next sync activity of App Volumes Manager, it will mark the writable volumes with Writable Status = Orphaned.
Now in the ideal world, these accounts have been disabled and should be okay to delete? Maybe, if you don’t have the obligation of data retention, then you are ready to delete them. If you need to retain them, keep them as-is for compliance purposes.
Script to delete them for App Volumes Manager
Before we talk about the script, the deletion is very straightforward within the App Volumes Manager. Select the volumes with Status = Orphaned and select the Delete button. However, when you have to do the same against multiple POD, which becomes challenging, and as always, if it’s not automated, there is the scope of human error.
Pre-requisites
You need the App Volumes Manager URL
You need the username and password of the App Volumes Manager
You need to enter y/Y to proceed further with the deletion
The script was tested on PowerShell V5.x with App Volumes Manager version 2.18.10 (The logic will be the same however, the API call for App Volumes 4.x will be different)
###########################################################################
# Get List of Wrtiable Volumes from AppVolumes Manager for Status=Orphaned
# Delete the Orphaned Wrtiable Volumes
# You need username and password for the App Volumes Manager
# Author - Aresh Sarkari (Twitter - @askaresh)
# Version - V5.0
###########################################################################
#App Volumes Manager Name or IP Address
$AVManager = "https://avm001.askaresh.local"
# Run at the start of each script to import the credentials
$RESTAPIUser = "domain\username"
$RESTAPIPassword = "enteryourpassword"
#Ignore cert errors
add-type @"
using System.Net;
using System.Security.Cryptography.X509Certificates;
public class TrustAllCertsPolicy : ICertificatePolicy {
public bool CheckValidationResult(
ServicePoint srvPoint, X509Certificate certificate,
WebRequest request, int certificateProblem) {
return true;
}
}
"@
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]'Ssl3,Tls,Tls11,Tls12'
#Login AV Manager Body
$body = @{
username = “$RESTAPIUser"
password = “$RESTAPIPassword”
}
#Login API call to the AV Manager
Invoke-RestMethod -SessionVariable DaLogin -Method Post -Uri "$AVManager/cv_api/sessions” -Body $body
#Get the list of Writbale Volumes from the AV Manager
$output = Invoke-RestMethod -WebSession $DaLogin -Method Get -Uri "$AVManager/cv_api/writables" -ContentType "application/json"
#Selecting the WV with status Orphaned into a variable
$WVouput = $output.datastores.writable_volumes | Select-Object id, owner_name, owner_upn, title, status | Where-Object {[string]$_.status -match "Orphaned"}
#Output on the console (Validate carefully before proceeding ahead)
$WVouput | Format-Table | Out-String | % {Write-Host $_}
#Confirmation logic to proceed with the deletion
$confirmation = Read-Host -Prompt "Are you Sure You Want To Proceed with the deletion:"
if ($confirmation -match "[yY]" ) {
# proceed
# The WV Deletion API call only looks for IDs. We are filtering the ids only
$WVOutputIDs = $WVouput.id
#Looping to delete each Writable Volumes via its ID
foreach ($WVOutputIDss in $WVOutputIDs) {
# Writable Volumes deletion Parameters body
$jsonbody = @{
bg = "0"
volumes = "$WVOutputIDss"
} | ConvertTo-Json
#API call to delete the Wrtiable Volumes
#We are using Invoke-webrequest for getting the Content of the deletion (Success) in oneline
$WVdeletecall = Invoke-WebRequest -WebSession $DaLogin -Method Post -Uri "$AVManager/cv_api/volumes/delete_writable" -Body $jsonbody -ContentType "application/json"
}
#Dig into the exception to get the Response details.
Write-Host $WVdeletecall.StatusCode
Write-Host $WVdeletecall.StatusDescription
Write-Host $WVdeletecall.Content
}
When you run the script, it will identify all the end-users with Status = Orphaned. If you like, you can copy and paste the output in an editior (Notepad++) to verify the output.
Once you press y/Y it will go ahead and delete the Orphaned writable volumes.
I hope you will find this script useful to bulk delete orphaned Writable Volumes in App Volumes Manager. A small request if you further enhance the script or make it more creative, I hope you can share it back with me?
Horizon Reach is a potent tool, and Andrew Morgan has put in a lot of blood, sweat and tears to develope it. What suprises me is why isnt this fling included into the Horizon product? We haven’t gathered here to talk about the product management and roadmap aspects 😉
Horizon Reach fling aggregates all the various Horizon POD information into its database. Typically, running Horizon API calls or Horizon Powershell modules might have to run them against individual pods to fetch information about that POD. The beauty with Horizon Reach is it aggregates all the information, we can write scripts/API calls to request information from there instead of writing Horizon POD specific scripts.
Let’s take a look at the following information from the Horizon Reach fling:
What API’s are available with Horizon Reach?
What all options are available to interact with Horizon Reach API?
Script – Get a consolidated list of Horizon Farm details (Display the Name, Base Image details, Snapshot Version, Health and If provisioning is enabled)
Note the above can also be fetched using the old Horizon Powershell modules but trust me it’s pretty tricky to run a foreach loop for every object on the SOAP method.
Script – Get a consolidated list of Horizon Desktop Pools details (Display the Name, Base Image details, Snapshot Version, Health and If provisioning is enabled)
What API’s are avilable with Horizon Reach?
After you have installed the Horizon Reach fling, go to the following URL to check out all the avilable API’s. Its the UI Swagger interface to simplify and understand each calls.
Scripts to get consolidated Horizon Farms/Desktops information
Pre-requsites:
You need the Horizon Reach Server URL
You need the password of the Horizon Reach Server
The script provides you with the details of all Horizon PODs in your setup.
The script was tested on PowerShell V5.x
#Horizon Reach Server Name or IP Address
$HZReachServer = "https://horizonreach.domain:9443"
#Ignore the self signed cert errors
add-type @"
using System.Net;
using System.Security.Cryptography.X509Certificates;
public class TrustAllCertsPolicy : ICertificatePolicy {
public bool CheckValidationResult(
ServicePoint srvPoint, X509Certificate certificate,
WebRequest request, int certificateProblem) {
return true;
}
}
"@
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]'Ssl3,Tls,Tls11,Tls12'
#API Call to make the intial connection to the Horizon Reach Server##
$HZReachLogonAPIcall = "$HZReachServer`/api/Logon"
#The body payload that comprises of the login API request
$body = @{
username = "administrator"
password = "enteryourpassword"
} | ConvertTo-Json
$HZReachlogin = Invoke-RestMethod -Method Post -uri $HZReachLogonAPIcall -Body $body -ContentType "application/json"
#Header along with the JWT token is now saved for future API calls
#You need to call this header in all subsequent calls as it has the token
$Headers = @{ Authorization = "Bearer $($HZReachlogin.jwt)" }
#API Call to fetch the consolidated (as many pods you have) Horizon Farm information##
$HZReachFarms = Invoke-RestMethod -Method Get -uri "$HZReachServer/api/Farms" -Headers $Headers -ContentType "application/json" -UseBasicParsing | Format-Table -Property displayname, baseimage, snapshot, enabled, health, isProvisioningEnabled
Write-Output $HZReachFarms
#API Call to fetch the consolidated (as many pods you have) Horizon desktop pool information##
$HZReachPools = Invoke-RestMethod -Method Get -uri "$HZReachServer/api/pools" -Headers $Headers -ContentType "application/json" -UseBasicParsing | Format-Table -Property displayname, baseimage, snapshot, enabled, healthDetail, isProvisioningEnabled
Write-Output $HZReachPools
The following information (Display Name, Snapshot, Base Image, Health, Provisioning Mode) is pulled using the above scripts. I was much interested to see the snapshot versions of each Farms/Pools along with Health and provisioning status. Feel free to extract whatever details you are looking for there are plenty of other properties.
I hope you will find this script useful to fetch helpful information from Horizon Reach. A small request if you further enhance the script or make it more creative, I hope you can share it back with me?
Recent Comments