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.
If you want to establish a network connection that allows communication between the Windows 365 Cloud PC and the existing Azure Virtual Network (ANC), then keep following this post. Today, I will demonstrate the Powershell method of creating the Azure Network Connection (ANC). Note that we need information from the Azure Portal to make sure you have all the necessary information handy or/or involve the necessary teams who can provide you with the information on Azure Networking.
Overview
Create the ANC first before creating the Win365 – Cloud Provisioning Policy (CPP)
If the ANC precreated then during the cloud provisioning of the Cloud PC desktops it will create them on the Azure VNET on your desired subnet
Make sure you have a working DNS configured on the VNET which can communicate with your on-premise network using express route or other Azure VNETs
Open necessary firewall ports based on your requirements on the NSG or Azure Firewall for the communication to your on-premise network using express route or other Azure VNETs
Permissions
Intune Administrator in Azure AD
Cloud PC Administrator
Global Administrator
If you decide to alter or change the ANC, you will have to reprovision the Cloud PC, and it’s a destructive activity. Make sure you architect it properly
You can delete your ANC however, you will have to update your cloud provisioning policy with the new ANC first, and then you can delete the existing ANC.
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 want to authenticate. If you are only doing read-only operations, I suggest you connect to “CloudPC.Read.All” in our case, we are creating the ANC, 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!
Step 3 – Choose between v1.0 (Generally Available) and Beta API versions. Note for Windows 365 Cloud PC, the API calls are BETA.
We are logging into Azure to grab all the details regarding to Resource Group, Subscription ID/Name, VNET and Subnets
Connect to the Azure Portal using the necessary credentials
Select the Azure Subscription that holds all the networking information
A display name of the Azure Network Connection – ANC – (ANC-W365-Sub01)
What is the join type of the ANC of the golden image virtual machine (azureADJoin)
Resource Group ID of the existing resource group. You will have to enter the resource group name (W365-AVD-RG01), and it will get us the ID we need.
Name of the existing subnet within the vNET (W365Workload-Sub01), and it will get us the ID we need.
Name of the existing VNET used for the connection. You will have to enter the VNET name (W365-AVD-VNET01), and it will get us the ID we need.
Connection to the MS Graph API and ensure you have the necessary write permissions.
We are using the beta API for Cloud PC
# 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
# Your subscription. This command gets your current subscription name
$subscriptionName = $currentAzContext.Subscription.Name
# ANC Display Name
$ancdname = "ANC-W365-Sub01"
# Join Ype for the Azure Network Connection
# Two types Azure AD and Hyrbird "azureADJoin" or "hybridAzureADJoin"
$ancjointype = "azureADJoin"
# Get your Win365 Resouce Group id for RG Name - W365-AVD-RG01
# Put your RG Name
$win365RGID = Get-AzResourceGroup -Name "W365-AVD-RG01" | Select-Object -ExpandProperty ResourceId
# Get your Azure VNET id used for Windows 365 Cloud PC
# Put your VNET Name
$win365VNETID = Get-AzVirtualNetwork -Name "W365-AVD-VNET01" | Select-Object -ExpandProperty Id
# Get your Subnet ID within the Azure VNET for Windows 365 Cloud PC
# Put your VNET Name
$win365VNET = Get-AzVirtualNetwork -Name "W365-AVD-VNET01"
# Enter your Subnet Name
$win365SubID = Get-AzVirtualNetworkSubnetConfig -Name "W365Workload-Sub01" -VirtualNetwork $win365VNET | Select-Object -ExpandProperty Id
# Connec to MS Graph for Cloud PC W365
Connect-MgGraph -Scopes "CloudPC.ReadWrite.All"
# Select Beta Profile for Cloud PC APIs
Select-MgProfile -Name "beta"
We shall pass the above variable into the final ANC creation.
Create the Azure Network Connection
We are creating a Azure Network Connection that includes the following:
Display Name of the network – $ancdname
Azure Subscription ID – $subscriptionID
Azure Subscription Name – $subscriptionName
Type – There are two types we are selecting Azure AD join – azureADJoin
Resource Group ID – The resource group within Azure – $win365RGID
Virtual Network ID – The VNET within Azure – $win365VNETID
Subnet ID – The subnet for W365 within VNET – $win365SubID
# Create the ANC for Windows 365 with AAD join type
try
{
write-host "Create the ANC for Windows 365 with AAD join type"
$params = @{
displayName = "$ancdname"
subscriptionId = "$subscriptionID"
type = "$ancjointype"
subscriptionName = "$subscriptionName"
resourceGroupId = "$win365RGID"
virtualNetworkId = "$win365VNETID"
subnetId = "$win365SubID"
}
New-MgDeviceManagementVirtualEndpointOnPremisesConnection -BodyParameter $params -Debug
}
catch
{
Write-Host $_.Exception.Message -ForegroundColor Yellow
}
# Import module Az and MS Graph
Import-Module Az.Accounts
Install-Module Microsoft.Graph
# 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
# Your subscription. This command gets your current subscription name
$subscriptionName = $currentAzContext.Subscription.Name
# ANC Display Name
$ancdname = "ANC-W365-Sub01"
# Join Ype for the Azure Network Connection
# Two types Azure AD and Hyrbird "azureADJoin" or "hybridAzureADJoin"
$ancjointype = "azureADJoin"
# Get your Win365 Resouce Group id for RG Name - W365-AVD-RG01
# Put your RG Name
$win365RGID = Get-AzResourceGroup -Name "W365-AVD-RG01" | Select-Object -ExpandProperty ResourceId
# Get your Azure VNET id used for Windows 365 Cloud PC
# Put your VNET Name
$win365VNETID = Get-AzVirtualNetwork -Name "W365-AVD-VNET01" | Select-Object -ExpandProperty Id
# Get your Subnet ID within the Azure VNET for Windows 365 Cloud PC
# Put your VNET Name
$win365VNET = Get-AzVirtualNetwork -Name "W365-AVD-VNET01"
# Enter your Subnet Name
$win365SubID = Get-AzVirtualNetworkSubnetConfig -Name "W365Workload-Sub01" -VirtualNetwork $win365VNET | Select-Object -ExpandProperty Id
# Connec to MS Graph for Cloud PC W365
Connect-MgGraph -Scopes "CloudPC.ReadWrite.All"
# Select Beta Profile for Cloud PC APIs
Select-MgProfile -Name "beta"
# Create the ANC for Windows 365 with AAD join type
try
{
write-host "Create the ANC for Windows 365 with AAD join type"
$params = @{
displayName = "$ancdname"
subscriptionId = "$subscriptionID"
type = "$ancjointype"
subscriptionName = "$subscriptionName"
resourceGroupId = "$win365RGID"
virtualNetworkId = "$win365VNETID"
subnetId = "$win365SubID"
}
New-MgDeviceManagementVirtualEndpointOnPremisesConnection -BodyParameter $params -Debug
}
catch
{
Write-Host $_.Exception.Message -ForegroundColor Yellow
}
I hope you will find this helpful information for creating Azure Network Connection using PowerShell. Please let me know if I have missed any steps or details, and I will be happy to update the post.
The enterprise has been rolling out the application packages using various available methods (GPOs, SCCM, WS1 UEM etc.) in the industry. Today we are going to take a step further and see how to deploy the VMware Horizon Client using the new Micrososft Windows Package Manager (Winget)
Available Commands for various verison of Horizon Client
Following are the commands however, it’s recommended only to install the latest or the matching version based on your VMware Horizon environment.
VMware Horizon 8.x
VMware Horizon Client 8.x
VMware Horizon 7.x
VMware Horizon Client 5.x
# Latest GA version VMware Horizon Client version 8.7.0.31805
winget install -e --id VMware.HorizonClient
Installing, Listing, Upgrading and Un-installing the latest version HZ Client
Open the PowerShell with administrative privileges
Installing
winget install -e --id VMware.HorizonClient
Listing the installed package
List the package and its details of the previous installation step
winget list --name 'VMware Horizon Client'
Upgrading from version 5.5.4 to 8.7.0
winget upgrade --id VMware.HorizonClient
Un-installing
Following is the command to uninstall the Horizon Client.
winget uninstall --id VMware.HorizonClient
Note after running the above command, the Windows endpoint rebooted immediately. I am not whether the product team has included the /norestart switches to the packages. If you come across the same leave a comment down below.
I hope you will find this helpful post about the winget and VMware Horizon Client details. Give it a spin in your lab and production environment, if you find anything interesting. I hope you can share it back with me?
Assign the Azure Active Directory Group to the (AG)
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.
RemoteApp
RemoteApp – This is a way to provide end-users with the business applications alone without giving them an entire desktop. They can access their applications anywhere on any device.
Pre-requisites
Following are the pre-requisites before you begin
PowerShell 5.1 and above
Azure Subscription
Permissions within the Azure Subscription for the creation of AVD – Host Pools
To start working with Azure PowerShell, sign in with your Azure credentials.
Connect-AzAccount
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 Host Pool (Australia East)
Name of the Host Pool (RA-HP01)
Host Pool Type (Pooled) as it will be shared with multiple end-users
Load balancing method for the Host Pool (DepthFirst)
Maximum users per session host VM (10)
The type of Application Group (RailApplications). As we are only giving out end-users Apps
Application Group Name ($HPName-RAG)
Workspace grouping name ($HPName-WRK01)
Azure AD group that will be assigned to the application group (XXXX4b896-XXXX-XXXX-XXXX-33768d8XXXXX)
# Get existing context
$currentAzContext = Get-AzContext
# Your subscription. This command gets your current subscription
$subscriptionID = $currentAzContext.Subscription.Id
# Existing Resource Group to deploy the Host Pool
$rgName = "AZ104-RG"
# Geo Location to deploy the Host Pool
$location = "australiaeast"
# Host Pool name
$HPName = "RA-HP01"
# Host Pool Type Pooled|Personal
$HPType = "Pooled"
# Host Pool Load Balancing BreadthFirst|DepthFirst|Persistent
$HPLBType = "DepthFirst"
# Max number or users per session host
$Maxusers = "10"
# Preffered App group type Desktop|RailApplications
$AppGrpType = "RailApplications"
# ApplicationGroup Name
$AppGrpName = "$HPName-RAG"
# Workspace Name
$Wrkspace = "$HPName-WRK01"
# AAD Group used to assign the Application Group
# Copy the Object ID GUID from AAD Groups Blade
$AADGroupObjId = "XXXX4b896-XXXX-XXXX-XXXX-33768d8XXXXX"
Execution block
Execution code block within this section. Lets take a look at what we are we executing within the script:
Create the host pool with all the mentioned variables, tags and whether the validation enivornment yes/no.
Create the application group and tie it to the host pool
Finally, we create the workspace and tie it to the application group and hostpool
Last step, we assign the AAD group object ID to the Application Group for all entitlement purposes.
# Create the Host Pool with RemoteApp Configurations
try
{
write-host "Create the Host Pool with Pooled RemoteApp Configurations"
$DeployHPWRA = New-AzWvdHostPool -ResourceGroupName $rgName `
-SubscriptionId $subscriptionID `
-Name $HPName `
-Location $location `
-ValidationEnvironment:$true `
-HostPoolType $HPType `
-LoadBalancerType $HPLBType `
-MaxSessionLimit $Maxusers `
-PreferredAppGroupType $AppGrpType `
-Tag:@{"Billing" = "IT"; "Department" = "IT"; "Location" = "AUS-East" } `
-ErrorAction STOP
}
catch
{
Write-Host $_.Exception.Message -ForegroundColor Yellow
}
# Create the Application Group for the Remote App Host Pool
try
{
write-host "Create the Application Group for the Remote App Host Pool"
$CreateAppGroupRA = New-AzWvdApplicationGroup -ResourceGroupName $rgName `
-Name $AppGrpName `
-Location $location `
-HostPoolArmPath $DeployHPWRA.Id `
-ApplicationGroupType 'RemoteApp' `
-ErrorAction STOP
}
catch
{
Write-Host $_.Exception.Message -ForegroundColor Yellow
}
# Create the Workspace for the RemoteApp Host Pool
try
{
write-host "Create the Workspace for the RemoteApp Host Pool"
$CreateWorkspaceRA = New-AzWvdWorkspace -ResourceGroupName $rgName `
-Name $Wrkspace `
-Location $location `
-ApplicationGroupReference $CreateAppGroupRA.Id `
-ErrorAction STOP
}
catch
{
Write-Host $_.Exception.Message -ForegroundColor Yellow
}
# Assign the AAD group (Object ID) to the Application Group
try
{
write-host "Assigning the AAD Group to the Application Group"
$AssignAADGrpAG = New-AzRoleAssignment -ObjectId $AADGroupObjId `
-RoleDefinitionName "Desktop Virtualization User" `
-ResourceName $CreateAppGroupRA.Name `
-ResourceGroupName $rgName `
-ResourceType 'Microsoft.DesktopVirtualization/applicationGroups' `
-ErrorAction STOP
}
catch
{
Write-Host $_.Exception.Message -ForegroundColor Yellow
}
# 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
# Existing Resource Group to deploy the Host Pool
$rgName = "AZ104-RG"
# Geo Location to deploy the Host Pool
$location = "australiaeast"
# Host Pool name
$HPName = "RA-HP01"
# Host Pool Type Pooled|Personal
$HPType = "Pooled"
# Host Pool Load Balancing BreadthFirst|DepthFirst|Persistent
$HPLBType = "DepthFirst"
# Max number or users per session host
$Maxusers = "10"
# Preffered App group type Desktop|RailApplications
$AppGrpType = "RailApplications"
# ApplicationGroup Name
$AppGrpName = "$HPName-RAG"
# Workspace Name
$Wrkspace = "$HPName-WRK01"
# AAD Group used to assign the Application Group
# Copy the Object ID GUID from AAD Groups Blade
$AADGroupObjId = "dcc4b896-2f2d-49d9-9854-33768d8b65ba"
# Create the Host Pool with RemoteApp Configurations
try
{
write-host "Create the Host Pool with Pooled RemoteApp Configurations"
$DeployHPWRA = New-AzWvdHostPool -ResourceGroupName $rgName `
-SubscriptionId $subscriptionID `
-Name $HPName `
-Location $location `
-ValidationEnvironment:$true `
-HostPoolType $HPType `
-LoadBalancerType $HPLBType `
-MaxSessionLimit $Maxusers `
-PreferredAppGroupType $AppGrpType `
-Tag:@{"Billing" = "IT"; "Department" = "IT"; "Location" = "AUS-East" } `
-ErrorAction STOP
}
catch
{
Write-Host $_.Exception.Message -ForegroundColor Yellow
}
# Create the Application Group for the Remote App Host Pool
try
{
write-host "Create the Application Group for the Remote App Host Pool"
$CreateAppGroupRA = New-AzWvdApplicationGroup -ResourceGroupName $rgName `
-Name $AppGrpName `
-Location $location `
-HostPoolArmPath $DeployHPWRA.Id `
-ApplicationGroupType 'RemoteApp' `
-ErrorAction STOP
}
catch
{
Write-Host $_.Exception.Message -ForegroundColor Yellow
}
# Create the Workspace for the RemoteApp Host Pool
try
{
write-host "Create the Workspace for the RemoteApp Host Pool"
$CreateWorkspaceRA = New-AzWvdWorkspace -ResourceGroupName $rgName `
-Name $Wrkspace `
-Location $location `
-ApplicationGroupReference $CreateAppGroupRA.Id `
-ErrorAction STOP
}
catch
{
Write-Host $_.Exception.Message -ForegroundColor Yellow
}
# Assign the AAD group (Object ID) to the Application Group
try
{
write-host "Assigning the AAD Group to the Application Group"
$AssignAADGrpAG = New-AzRoleAssignment -ObjectId $AADGroupObjId `
-RoleDefinitionName "Desktop Virtualization User" `
-ResourceName $CreateAppGroupRA.Name `
-ResourceGroupName $rgName `
-ResourceType 'Microsoft.DesktopVirtualization/applicationGroups' `
-ErrorAction STOP
}
catch
{
Write-Host $_.Exception.Message -ForegroundColor Yellow
}
Next Steps on the Host Pool
Now that the host pool, application group and workspaces are ready following are the next steps involved:
Generate a registration token
Add the session host virtual machine to the host pool
Create Applications within the Application Group. You can create multiple Applications in single AG or 1 AG per Application.
I hope you will find this helpful information for deploying a host pools, application group and workspaces within Azure Virtual Desktop. If you want to see a Powershell version of the applications & session host 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.
In the previous blog post, I demonstrate how to create a Windows 11 Multi-session golden image for AVD. In today’s post, I want to showcase how to create a custom Windows 11 Enterprise 22H2 + Microsoft 365 for Windows 365 Cloud PC. (Note its not multi-session and instead, its Enterprise edition for 1×1 mapping of desktop/user aka Full Clone)
Why will you create a custom Windows 11 Ent Windows 365 Cloud PC Golden Image?
There are situations where you want to create a custom image with all corporate applications pre-installed (VPN or Zero trust agent, EDR/XDR Solutions agents or Anti-virus agent pre-installed). You may argue we can deploy those applications later using Win32 app deployment via Intune. But still, few security teams and corporations would like to have it available from the start.
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
To start working with Azure PowerShell, sign in with your Azure credentials.
Connect-AzAccount
Identify the Windows 11 Multi-session (Marketplace Image)
Many versions of Windows 365 Cloud PC – Windows 11/10 Enterprise edition marketplace images from Microsoft. The operating systems is already optimized (Microsoft VDI Optimizations) for Cloud PC, and the only difference is with or without Microsoft 365.
Let’s identify what is available within the marketplace.
We are going to use the Windows 11 22H2 Enterprise + Microsoft 365 Apps 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 (Win365-GI01)
NIC Interface name for the virtual machine (Win365-GI01-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-ent-cpc
SKU – win11-22h2-ent-cpc-m365
version – Offcourse latest
Get credentials – A local admin account is created on the golden image (A input box to capture the uisername and password of your choice)
# Existing Resource Group to deploy the VM
$rgName = "AZ104-RG"
# Geo Location to deploy the VM
$location = "Australia East"
# Image template name
$vmName = "Win365-GI01"
# 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-ent-cpc"
# The SKY ending with avd are the multi-session
$sku = "win11-22h2-ent-cpc-m365"
# 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 (Win365-GI01)
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-ent-cpc
# Existing Resource Group to deploy the VM
$rgName = "AZ104-RG"
# Geo Location to deploy the VM
$location = "Australia East"
# Image template name
$vmName = "Win365-GI01"
# 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-ent-cpc"
# The SKY ending with avd are the multi-session
$sku = "win11-22h2-ent-cpc-m365"
# 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 install the required applications
Zero Trust Agent
EDR/XDR Agent
Antivirus Software Agent
Line of Business Apps
Generalize and sysprep and shutdown the image
Capture the image to the Azure Compute Galleries
Add the image within Microsoft Intune
I hope you will find this helpful information for deploying a golden image within Azure – Virtual Machine to deploy the custom Image for Windows 365 Cloud PC. Please let me know if I have missed any steps or details, and I will be happy to update the post.
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.
In my previous post, I had demonstrated the new reports (in-preview) Windows 365 Cloud PC – New Reports – Connection quality & Low Utilization. Today, I will showcase how to generate the report of “Cloud PCs with low utilization” using PowerShell and MS Graph API with beta modules on Windows 365 Cloud PC.
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 want to authenticate. 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!
Step 3 – Choose between v1.0 (Generally Available) and Beta API versions. Note for Windows 365 Cloud PC, the API calls are BETA.
Note – You will have to enter the OutFile path where you want to save the report in my example C:\Temp\abc.csv
The actual report in the Intune Portal looks like the following – The same result is now available within the Value section of the CSV (Note – The formatting of the output is terrible, some excel work will be required to format the data properly)
I hope you will find this helpful information for generating low utilization report for Cloud PC using PowerShell. Please let me know if I have missed any steps or details, and I will be happy to update the post.
When you deploy your Windows 365 Cloud PC, you can use a Microsoft Gallery Image or a Custom Image from Azure. The Microsoft Gallery image is a good starting point for most deployments, as it’s already optimized for Cloud PC. In my scenario, I have leveraged the Windows 11 Enterprise + OS Optimizations – 22H2 gallery image.
Note – These steps are only applicable in situations where-in you are not using Microsoft Teams.
Microsoft Teams is an excellent collaboration tool if the organization leverages it.
Post the Cloud PC Provisioning, the end-users see the Chat Icon for MS Team and the Microsoft Teams applications are installed within the Apps & Features. In this scenario, I am not using Microsoft Teams hence I have decided to Disable the Icon & Uninstall MS Team from the Cloud PC fleet.
Disable Chat Icon – Intune
Let’s see how to disable the Chat Icon gracefully for all end-users using Microsoft Intune
Go to Devices and then scroll down to Configuration Profiles
Click on Create New Profile
Select Platform – Windows 10 and later
Profile type – Settings Catalog
Enter a Name – Disable Win11ChatIcon
Settings picker type – configure chat icon
Category select – Experience
Results select – Configure Chat Icon
Set the value as disable
Assign the policy to the AAD group – In my case, I have assigned to the “Win365-DeviceGroup”
Set the Configure Chat Icon – Disabled
After the sync-up, I noticed the Chat Icon from the taskbar disappeared on all Windows 365 Cloud PC devices.
Un-install MS Team – Scripts Intune
Within the Microsoft gallery image, you will notice the Microsoft Team is installed by default, and we want to uninstall the software using Powershell Scripts. A quick check within Apps & Features shows that Microsoft Teams is already installed.
Assign to the policy to the AAD group – In my case I have assigned to the “Win365-DeviceGroup”
# Remove Teams Application
try {
$fetchteamsapp = @(Get-AppxPackage -name '*teams' -ErrorAction stop)
}
catch {
$ErrorMessage = $_.Exception.message
write-error ('Error getting the teams app ' + $ErrorMessage)
Exit
}
if ($fetchteamsapp -ne $null) {
$uninstallteamsapp = @(remove-appxpackage -package "MicrosoftTeams_22287.702.1670.9453_x64__8wekyb3d8bbwe" -ErrorAction stop)
}
else {
write-host 'Successfully un-install the MS Teams.'
exit
}
Note – Replace your MS Team version with whatever you have within your environment
I hope you will find this helpful information for disabling icon and uninstalling Microsoft teams using Intune + Powershell scripts. Please let me know if I have missed any steps or details, and I will be happy to update the post.
We have numerous articles showcasing how to create the golden master image to deploy within the Host Pool. This blog post will showcase how to perform recurring monthly security patch updates within the golden master image and push that into your Host Pools within Azure Virtual Desktop.
Pre-requisites
Azure Compute Galleries – Create VM
Console to Golden Image (RDP or Azure Bastion)
Install the Microsoft Latest Cumulative Update (LCU)
Sysprep (Generalize and Shutdown)
Capture the Virtual Machine
Azure Compute Galleries – New Version
Drain and remove old session host vms
Feature Whishlist
If Microsoft is listening – requesting the feature of leveraging the Update Rings from Micorosft Intune can be integrated into applying the quality updates from #Intune on Microsoft Windows 10/11 Multi-session
Pre-requisites
The assumption here is that you already have a golden image and existing versions available. Below is an example from Azure Computer Galleries of a Windows 11 Multi-session and current running version 0.0.2 within my Host Pools. (Note its an already generalized image – See the OS State)
Azure Compute Galleries – Create VM
The first step here is to update the golden image with the monthly Microsoft Cumulative Security update, and we want to create a new virtual machine from the existing version of 0.0.2. (Background version 0.0.2 include the October 2022 Latest Cumulative Updates)
Now you will be presented with a Create VM wizard
Select Next – Disk Settings
Select Next – Networking Settings
Select Next – Management Settings
Select Next – Monitoring Settings
Select Tags
Select Review & Create the Virtual Machine – Golden Image.
#Tip – On the rare occasion that the creation of the virtual machine fails, in one instance, I had forgotten to perform Sysprep on the existing version in Azure Compute Galleries (e.g. 0.0.1). In such scenarios, create a virtual machine from the previous version number you know that works well.
Console to Golden Image (RDP or Azure Bastion)
We now have the newly created golden image from the existing version 0.0.2 within the Azure – Virtual Machines blade listed and status=Running.
Download the RDP file and console into the Virtual machine – Win11MSGI04 (Note, as previously mentioned, this is a bad practice in a production environment as it needs public IP for access. The best approach here is to leverage Azure Bastion and click on the Bastion option, and securely console the VM via browser)
Install the Microsoft Latest Cumulative Update (LCU)
In our scenario, we shall install the November 8, 2022—KB5019980 (OS Build 22621.819) for Windows 11 22H2 Multi-session. Note I am using the Powershell pswindowsupdate module, but you can download and offline install the Windows update (LCU, which also includes SSU)
#Tip – Make sure the end state of the virtual machine status = Stopped (deallocated) before following the next step of Capture. Sysprep is the most crucial step if you forget this, your provisioning will fail with an error.
Capture the Virtual Machine
We will capture this image into Azure Compute Galleries
Next Capture wizard
#Tip – As we selected “Delete” post creation, the virtual machine will not appear within the Azure – Virtual Machines. Below is the task for the deletion.
Azure Compute Galleries – New Version
We can now see the latest version showing up 0.0.3 post the capture process. This version is now ready to be added to the AVD – Host Pools
Add Session Host Virtual Machine (New security patch version 0.0.3)
After clicking on Add, it will open the “Add virtual machine to a host pool” wizard
Select Next – Virtual Machines
Next enter the tags of your choice and hit Create
Drain and remove old session host vms
Put the old session host virtual machines in the drain and remove the virtual machine. This step will depend on how much time log-off all the end-user sessions will take on the VM.
Next, if all the sessions are drained. Select the old virtual machine and select Remove.
Note – When you hit remove for the session host virtual machine within the Host Pools blade, it will only remove the virtual machine from there. You will have to go into the Azure – Virtual Machines blade and stop and delete the virtual machine from there. The good thing here is that as we had selected delete disk/network (checkbox) during creation, it will delete everything associated with the VM.
#Tip – As a precautionary step, you can delete the virtual machine after 2-3 days after production stabilizes in case you have to revert and manually add the VM’s back into the host pool
A big thanks to Mahammad Kubaib for reviewing this blog post based on his previous experience. I hope you will find this helpful information for performing monthly cumulative security updates on your Azure Virtual Desktop – Host Pools. If you want to see a Powershell version of the same activity, 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.
There are numerous posts that talk about creating the Windows 365 Cloud PC – User Settings. In this blog post, I will demonstrate how to create user settings using PowerShell and MS Graph API with beta modules on Windows 365 Cloud PC.
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 want to authenticate. 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.
Now that we have the User Settings created, it’s time to assign it to an AAD group. We need to follow the following procedure
AAD Group (Copy – Object ID)
I have an existing AAD (Azure Active Directory) group called “Win365-Users” and I plan to use this group for assignment to this User Settings. The important step here is to make a note of the “Object ID” of the AAD group you are planning to assign. Please make sure you copy this ID.
User Settings (Copy ID)
Copy the ID of the previously created User Settings. We need to copy this ID for the assignment. Use the command – Get-MgDeviceManagementVirtualEndpointUserSetting | FT. Note if multiple CPC user settings, select the relevant ID.
I hope you will find this helpful information for creating/assigning the user settings using PowerShell. Please let me know if I have missed any steps or details, and I will be happy to update the post.
Recent Comments