This article is a 4th part of a series regarding WVD deployment.

Previous articles from the series could be found here:

  1. https://www.azureblog.pl/2020/09/19/windows-virtual-desktop-deployment-1-5/
  2. https://www.azureblog.pl/2020/10/07/windows-virtual-desktop-deployment-2-5/
  3. https://www.azureblog.pl/2020/10/09/windows-virtual-desktop-deployment-3-5/

Today I’d like to walk you through the custom image optimization using:

  • Prerequisites
  • OS Configuration
  • Virtual Desktop Optimization Script
  • FSLogix configuration

Prerequisites

Before we proceed please deploy 1 VM described below:

  1. VM1OS: Windows 10 Enterprise multi-session, Version 2004., Disk Type: Managed
  2. Authentication source: Azure AD DS / Active Directory synchronized with Azure AD

OS configuration

This will be a easy configuration as we are going to

  • Disable Windows updates
  • Configure Time Zone redirection
  • Disable Storage Sense

Of course, you can configure as much as you wish. I’m showing only the part of the configuration.

In order to do that we need log in to the VM1 and run the following code:

$VerbosePreference = "continue"
Write-Verbose "Disabling Windows updates"
reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" /v NoAutoUpdate /t REG_DWORD /d 1 /f
Write-Verbose "Configuring Time zone redirection"
reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services" /v fEnableTimeZoneRedirection /t REG_DWORD /d 1 /f
Write-Verbose "Disabling Storage Sense"
reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\StorageSense\Parameters\StoragePolicy" /v 01 /t REG_DWORD /d 0 /f

Picture below presents the output of the code

OS confiugration

This is all about the OS configuration part.

Virtual desktop optimization script

In this part we are going to use the script available on GitHub under:

Download and run script into our VM1 machine using code below

#region Variables

$verboseSettings = $VerbosePreference
$VerbosePreference = 'Continue'
$toolsPath = "C:\Tools"
$optimalizationScriptURL = 'https://github.com/przybylskirobert/Virtual-Desktop-Optimization-Tool/archive/master.zip'
$optimalizationScriptZIP = "$toolsPath\WVDOptimalization.zip"
$OptimalizationFolderName = "$toolsPath\" + [System.IO.Path]::GetFileNameWithoutExtension("$optimalizationScriptZIP")

$toolsTest = Test-Path -Path $toolsPath
if ($toolsTest -eq $false){
    Write-Verbose "Creating '$toolsPath' directory"
    New-Item -ItemType Directory -Path $toolsPath | Out-Null
}
else {
    Write-Verbose "Directory '$toolsPath' already exists."
}

#endregion

#region Optimalization

Write-Verbose "Downloading '$optimalizationScriptURL' into '$optimalizationScriptZIP'"
Invoke-WebRequest -Uri $optimalizationScriptURL -OutFile $optimalizationScriptZIP
New-Item -ItemType Directory -Path "$OptimalizationFolderName"
Write-Verbose "Expanding Archive '$optimalizationScriptZIP ' into '$OptimalizationFolderName'"
Expand-Archive -LiteralPath $optimalizationScriptZIP -DestinationPath $OptimalizationFolderName
Set-Location "$OptimalizationFolderName\Virtual-Desktop-Optimization-Tool-master"
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned
#endregion

Image below outlines variables region output

Initial Configuration
Optimization script preparation

The last step is to run the script using the following line of code.

You could notice that some parts of the code might end up with errors, you could lose your RDP connection for a couple of seconds but don’t worry it is the desired result.

.\Win10_VirtualDesktop_Optimize.ps1 -WindowsVersion 2004 -Verbose

When the script will finish you will be notified that a system reboot is required. After the reboot optimization will be finished.

FSLogix configuration

This part have some prerequisites:

  • VM1 has to be domain joined
  • Storage Account has to be created for Azure Files usage
  • Storage Account has to be domain joined
  • AZ module installed on the VM1
  • Active Directory module installed on the VM1
  • Domain Users SID is required for the FSLogix configuration

The first point is pretty easy so we will skip it and focus on the storage Account deployment and configuration

FSLogix overview

Before we will start the deployment of the resources let me just show you the diagrams of why we need a storage account

Azure file share integration with Active Directory

As you can see we are going to integrate Azure Files (storage account) with Active AD DS. This will create a computer account with the same name as your storage account.

Someone could ask: Why we are going to use Private endpoint?

The private endpoint will limit access to this Azure File share only for the specific network. It will be inaccessible from the Internet.

FSLogix usage overview

The diagram above describes the usage of the FSLogix with Azure Files.
All user profile related files will be stored on a dedicated VHD file placed on the Azure Files. It will allow to re-deploy session hosts without any blockers related to the user data stored on them.

Storage Account deployment

Let’s go to the Azure portal and go inside resource group rg-wvd-mgmt dedicated to WVD management resources.

Adding new resource

Click Add on the top menu and on the next screen chose Storage Account

Resource selection

On the Basic screen fill the following:

  • Storage Account Name: this name has to be globally unique
  • Location: Same as the Resource Group
  • Performance: Premium
  • Account kind: FileStorage
  • Replication: Locally-redundant storage (LRS)
Storage Account configuration

On the Networking screen fill the following:

  • Connectivity method: Private endpoint
    Click Add to add new Private endpoint.
Network configuration

On the Create private endpoint screen fill the followings:

  • Location: Same as the resource group
  • Name: Private endpoint name according to your naming convention
  • Storage sub-resource File
  • Virtual network: Virtual network that should be used for your WVD deployment. It should also be integrated with AD / Azure AD DS
  • Subnet: subnet for the private endpoint usage
  • Integrate with private DNS zone: No
Private Endpoint configuration
Network configuration

When the private endpoint configuration is done click Review+Create and deploy the resources.

Now we can go to the our storage account and proceed with the configuration from the Azure portal perspective.

Find our private endpoint and copy its FQDN and Private IP

Private Endpoint configuration

Go to the storage account that we just created and click on the Access Control (IAM)

On the Access Control screen we need to add the following roles assignment:

  • Storage File Data SMB Share Contributor – In my case I’m assigning it to the WVD_users group
  • Storage File Data SMB Share Elevated Contributor: In my case, I’m assigning it to the domop@lab.azureblog.pl user
Role assignments 

This is it for now. Let’s switch to the VM1 and join our storage account to the domain

Storage Account domain join

Log in to the VM1 using your account that has privileges to add objects to the domain.
Run the following code using PowerShell

$VerbosePreference = 'continue'
$ToolsPath = 'C:\Tools'
$AzFilesPath = 'https://github.com/Azure-Samples/azure-files-samples/releases/download/v0.2.2/AzFilesHybrid.zip'
$StorageAccountName = 'stfsazureblog'
$StorageAccountResourceGroupName = 'rg-wvd-mgmt'

$moduleName = [System.IO.Path]::GetFileNameWithoutExtension("$AzFilesPath")
$azFilesZip = "$ToolsPath\$($moduleName).zip"
$azFilesFolderName = "$ToolsPath\$moduleName"

$toolsTest = Test-Path -Path $ToolsPath
if ($toolsTest -eq $false) {
    Write-Verbose "Creating '$ToolsPath' directory"
    New-Item -ItemType Directory -Path $ToolsPath | Out-Null
}else {
    Write-Verbose "Directory '$ToolsPath' already exists."
}

Write-Verbose "Downloading '$AzFilesPath' into '$azFilesZip'"
Invoke-WebRequest -Uri $AzFilesPath -OutFile $azFilesZip
New-Item -ItemType Directory -Path "$azFilesFolderName"
Write-Verbose "Expanding Archive '$azFilesZip ' into '$azFilesFolderName'"
Expand-Archive -LiteralPath $azFilesZip -DestinationPath $azFilesFolderName

$IP = Read-Host "Please provide storage account private endpoint IP"
$storageAccountName = Read-Host "Please provide storage account name"
Add-Content -Path C:\Windows\System32\drivers\etc\hosts -Value "$IP``$($storageAccountName).file.core.windows.net" -Force
notepad C:\Windows\System32\drivers\etc\hosts

Connect-AzAccount
Set-Location $azFilesFolderName
.\CopyToPSPath.ps1

$moduleTest = Get-Module -Name 'AzFilesHybrid'
if ($moduleTest -eq $null) {
    Write-Verbose "Importing module 'AzFilesHybrid'"
    Import-Module -Name AzFilesHybrid
}

Images below depicted configuration progress.
You will be asked to log on to the Azure

Initial configuration
Archive extraction
Hosts file update
AzFilesHybrid configuration
Importing module

After the module installation, you will need to close the PowerShell session.

Run the following code in the new PowerShell window

$StorageAccountName = 'stfsazureblog'
$StorageAccountResourceGroupName = 'rg-wvd-mgmt'

Import-Module -Name AZfilesHybrid
Connect-AzAccount

$VerbosePreference = "continue"
Write-Verbose "Joining storage account '$StorageAccountName' into the domain."
Join-AzStorageaccountForAuth -ResourceGroupName $StorageAccountResourceGroupName -Name $StorageAccountName -DomainAccountType "ComputerAccount" 

Write-Verbose "Remember to assign NTFS permissions on a network share."
Write-Verbose "User Account   Folder   Permissions"
Write-Verbose "Users   This Folder Only   Modify"
Write-Verbose "Creator / Owner   Subfolders and Files Only   Modify"
Write-Verbose "Administrator (optional)   This Folder, Subfolders, and Files   Full Control."
Importing AZFilesHybrid module
AzFilesHybrid run
AzFilesHybrid run

As we are using Azure AD DS we need to do one more thing from Azure Portal.
Go to the storage account configuration and Disable
Active Directory Domain Services (AD DS) integration. Save your change and then switch Azure Active Directory Domain Services (Azure AD DS) to Enabled again save your changes.

From this moment our Azure Files share is available for users authenticated to the Azure AD DS

Azure File share configuration

Let’s configure NTFS permissions on Azure Files Share.

In order to do that we need to configure share under Azure Files.

Go to the Azure Portal / Storage Account configured for Azure Files and create a new share called fslogixprofiles with 1024GiB capacity

Strage Account Overview
File share settings
New File share configuration

From the VM1 access the following network share:

\\StorageAccountName.file.core.windows.net\fslogixprofiles\
where StorageAccountName is stfsazureblog and the full path looks like
\\stfsazureblog.file.core.windows.net\fslogixprofiles\

Open share Properties and go to the Security Tab

Network share properties

From the advanced settings assign the following permissions:

User AccountFolderPermissions
UsersThis Folder OnlyModify
Creator / OwnerSubfolders and Files OnlyModify
AdministratorThis Folder, Subfolders, and FilesFull Control
FSLogix share NTFS permissions

Image below describes the settings configured on the LAB environment

Domain Users SID

To get the Domain Users group SID run the following command.
Remember that SID value will be different for each Active Directory deployment.

Get-ADGroup -Identity "Domain Users" | ft Name,SID
Ad Group SID

In our Domain Users SID is S-1-5-21-1581098281-3520177802-4203237917-513

FSLogix Installation

Now it is the time to install FSLogix.
As always we are going to do that using PowerShell code below:

$VerbosePreference = 'Continue'
$toolsPath = "C:\Tools"
$fsLogixURL = "https://aka.ms/fslogix_download"
$fslogixZip = "$toolsPath\FSLogix.zip"
$fslogixFolderName = "$toolsPath\" + [System.IO.Path]::GetFileNameWithoutExtension("$fslogixZip")

$toolsTest = Test-Path -Path $toolsPath
if ($toolsTest -eq $false){
    Write-Verbose "Creating '$toolsPath' directory"
    New-Item -ItemType Directory -Path $toolsPath | Out-Null
}
else {
    Write-Verbose "Directory '$toolsPath' already exists."
}
Write-Verbose "Downloading '$fsLogixURL' into '$fslogixZip'"
Invoke-WebRequest -Uri $fsLogixURL -OutFile $fslogixZip
New-Item -ItemType Directory -Path "$fslogixFolderName"
Write-Verbose "Expanding Archive '$fslogixZip ' into '$fslogixFolderName'"
Expand-Archive -LiteralPath $fslogixZip -DestinationPath $fslogixFolderName
Set-Location "$fslogixFolderName\x64\Release"
Write-Verbose "Installing FSLogix software"
.\FSLogixAppsSetup.exe /quiet /norestart /install

After couple minutes we should see the output similar as below

FSLogix installation

FSLogix Configuration

This is the last step regarding FSLogix configuration.
We are going to configure registry settings that will setup the VM1 for the FSLogix usage.
More information’s regarding FSLogix configuration could be found here:
https://docs.microsoft.com/en-us/fslogix/profile-container-configuration-reference

$VerbosePreference = 'Continue'
$storageAccountName = Read-Host "Please provide storage account name for FSLogix usage"
$regPath = "HKLM:\SOFTWARE\FSLogix\Profiles"
New-Item -Path $regPath -Force | Out-Null
New-ItemProperty -Path $regPath -name "Enabled" -PropertyType "DWord" -Value "1" -Force | Out-Null
New-ItemProperty -Path $regPath -name "VHDLocations" -PropertyType "String" -Value "\\$($storageAccountName).file.core.windows.net\fslogixprofiles" -Force | Out-Null
New-ItemProperty -Path $regPath -name "VolumeType" -PropertyType "String" -Value "VHDX" -Force | Out-Null
New-ItemProperty -Path $regPath -name "SizeInMBs" -PropertyType "DWord" -Value "2000" -Force | Out-Null
New-ItemProperty -Path $regPath -name "IsDynamic" -PropertyType "DWord" -Value "00000001" -Force | Out-Null
New-ItemProperty -Path $regPath -name "LockedRetryCount" -PropertyType "DWord" -Value "00000003" -Force | Out-Null
New-ItemProperty -Path $regPath -name "LockedRetryInterval" -PropertyType "DWord" -Value "00000005" -Force | Out-Null
New-ItemProperty -Path $regPath -name "FlipFlopProfileDirectoryName" -PropertyType "DWord" -Value "00000001" -Force | Out-Null
New-ItemProperty -Path $regPath -name "DeleteLocalProfileWhenVHDShouldApply" -PropertyType "DWord" -Value "00000001" -Force | Out-Null
New-ItemProperty -Path $regPath -name "PreventLoginWithTempProfile" -PropertyType "DWord" -Value "00000000" -Force | Out-Null
New-ItemProperty -Path $regPath -name "PreventLoginWithFailure" -PropertyType "DWord" -Value "00000001" -Force | Out-Null
$sid_LocalAdministrators = 'S-1-5-32-544'
$sid_DomainUsers = (Get-ADGroup -identity 'Domain Users' -Properties *).SID.value
Remove-LocalGroupMember -Group 'FSLogix Profile Include List' -Member 'Everyone' | Out-Null
Add-LocalGroupMember -Group 'FSLogix Profile Include List' -Member $sid_DomainUsers | Out-Null
Add-LocalGroupMember -Group 'FSLogix Profile Exclude List' -Member $sid_LocalAdministrators | Out-Null
FSLogix configuration output

So FSLogix finally configured. Now we can logo off from the VM1 and logon one more time to see if the FSLogix Profile works properly

Logon Screen with FSLogix information
Disk Management with Profile VHD file

Next steps

Finally we have managed to customize our golden image, optimize it using dedicated script and setup FSLogix.
With all those settings we can create image from VM1 and use it in our environment.

Comments are closed.