Recently I've been deploying a number of WVD platforms and one of the tricky bit is making the WVD assignment 'support staff friendly'.
       
 
   
   
 
   
 
   
 
   
 
   
 
       
 
             
             
                   
             
             
             
I've previously posted a blog entry documenting the required PowerShell commands to help out with setting up WVD users, but still this was manual and needed work in order for it to be used in a production environment. I also had some concerns as it exposed the Azure Storage Account Access Key.
With the thanks to @James_Tighe here is a script that will read a saved secret created within an Azure Key Vault that contains storage account Access Key.
The below script will create a Function called AssignWVDUser that you can use with the following syntax to either enable an individual user via a UPN or a batch of users from a CSV:
 AssignWVDUser -upn "john.smith@domain.com"
 AssignWVDUser -CSVPath "C:\temp\list.csv"
This will save a log file to the %TEMP%\WVDLog.log but this can be amended if required.
On overview of function is:
- Import PowerShell Modules
 - Connect to Azure
 - Set the correct Azure Subscription
 - Connect to the WVD tenant
 - Obtain the Storage Key from the Key Vault
 - Assign users to the WVD Host Pool
 - Set the FSLogix Profile store as the storage scope
 - Set the IAM Roles to the Azure Storage for FSLogix
 - Map a Z: drive to the FSLogix Storage
 - Assign the correct NTFS permissions to the FSLogix Profile share
 - Disconnect the Z: drive
 
To set this up:
- First of all, I've created a service account within AD to use with the script, this saves support staff from having to enter credentials (and means the script could also be published as a Remote App if required).
 - I then use New-StoredCredential.ps1 from https://blogs.technet.microsoft.com/exoshellwizard/2016/05/12/scheduling-a-task-against-exchange-online/ to save the credentials of a service account in a text file in the directory I'm saving my script.
 - Now you need to setup an Azure Key Vault and save the Storage Account access key as a Secret (Take a look at https://docs.microsoft.com/en-us/azure/storage/common/storage-account-keys-manage#view-access-keys-and-connection-string to see how to access the Access Keys)
 - Give the service account permission to the Key Vault in the way of an Access Policy - https://docs.microsoft.com/en-us/azure/key-vault/key-vault-secure-your-key-vault
 - Give the Service Account permission to your storage account and the Azure File Share hosting your FSLogix profiles
 - Assign the service account the RDS Owner Role - https://docs.microsoft.com/en-us/powershell/module/windowsvirtualdesktop/new-rdsroleassignment
 - Run the script by running the following commands (you could store this in your PowerShell profile if you wished):
 
Set-ExecutionPolicy Unrestricted -Scope Process
Import-module C:\InstallPoint\WVD-AssignWVDUser\AssignWVDUser.ps1 -Force
AssignWVDUser -upn "FIRSTNAME.LASTNAME@DOMAIN.COM"  
The script will run through and if you have everything configured correctly  you'll see the following output and your users should now be able to login to the WVD using the account detials you have configured:
I've saved a copy of the script here but also highlighted below areas you need to amend:
Function AssignWVDUser {
   
<# 
       .SYNOPSIS
       Creates a Window
Virtual Desktop users for the WVD Host Pool.
       .EXAMPLE
       AssignWVDUser -upn
"john.smith@domain.com"
       .EXAMPLE
       AssignWVDUser
-CSVPath "C:\temp\list.csv"
       .DESCRIPTION
       This commands add
the relevant user to the WVD platform. 
     #>
   
param (
       
[Parameter(Mandatory=$false)]
       
[string] $upn,
       
[Parameter(Mandatory=$false)]
       
[string] $CSVPath,
       
[Parameter(Mandatory=$false)]
       
[String] $HostPoolName = "YourHostPool",
       
[Parameter(Mandatory=$false)]
       
[String] $AppGroupName = "Desktop
Application Group"
   
)
   
#Variables
   
$tenantName = "youWVDtenant"
   
$brokerurl = "https://rdbroker.wvd.microsoft.com"
   
$aadTenantId = "yourAADteantID"
   
$azureSubscriptionId = "YouSubscriptionID"
   
$vaultName = "YourKeyVault"
   
$secretName = "YourAccessKey"
   
$PasswordPath = "C:\yoursavepassword.txt"
   
$SecurePassword = Get-Content $PasswordPath | ConvertTo-SecureString 
   
$UserCreds = New-Object System.Management.Automation.PSCredential -ArgumentList Username@domain.com, $SecurePassword
   
#Logging
   
function Write-Log
   
{
       
param
       
(
            [Parameter(Mandatory = $false)]
            [string]$Message,
            [Parameter(Mandatory = $false)]
            [string]$Error,
            [Parameter(Mandatory = $false)]
            [bool]$Tee = $false
       
)
       
try {
            $Date = Get-Date -format "dd-MM-yy
HH:mm:ss"
            $invocation = "$($MyInvocation.MyCommand.Source):$($MyInvocation.ScriptLineNumber)"
            if ($Message)
            {
                Add-Content -Value "$Date - $invocation - $Message" -Path "$([environment]::GetEnvironmentVariable('TEMP', 'Machine'))\WVDLog.log"
                if ($Tee) {
                    Write-Host -ForegroundColor Green $Message
                }
            }
            else {
                Add-Content -Value "$Date - $invocation - $Error" -Path "$([environment]::GetEnvironmentVariable('TEMP', 'Machine'))\WVDLog.log"
                if ($Tee) {
                    Write-Host -ForegroundColor Red $Error
                }
            }
       
}
       
catch {
            Write-Error $_.Exception.Message
       
}
   
}
   
#Import PowerShell
Modules 
   
try {
       
Write-Log -Message "Importing
RDInfra PowerShell Module" -Tee:$true
       
Import-Module -Name Microsoft.RDInfra.RDPowershell -ErrorAction stop
       
Write-Log -Message "Module
Imported Successfuly"
   
} catch {
       
Write-Log -Error "Error
importing RDInfr PowerShell Module" -Tee:$true
       
Write-Log -Error $_.Exception.Message
-Tee:$true
   
} 
   
#Connect to Azure
   
try { 
       
Write-Log -Message "Connecting
to Azure as user: $($usercreds.username)" -Tee:$true
       
Connect-AzAccount -Credential $userCreds -ErrorAction Stop
   
} catch {
       
Write-Log -Error "Error
connecting to Azure" -Tee:$true
       
Write-Log -Error $_.Exception.Message
-Tee:$true
       
return
   
}
   
#Set the correct Azure
Subscription
   
Write-Log -Message "Setting
Azure Context to customer subscription" -Tee:$true
   
Set-AzContext -SubscriptionId $azureSubscriptionId
   
#Connect to the WVD
tenant
   
try {
       
Write-Log -Message "Authenticating
to WVD Tenant: $tenantName" -Tee:$true
       
Add-RdsAccount -DeploymentUrl $brokerurl -Credential $UserCreds -ErrorAction Stop
       
Write-Log -Message "Successfully
Authenticated to WVD Tenant: $tenantName" -Tee:$true
   
} catch {
       
Write-Log -Error "Error
authenticating to WVD Tenant: $tenantName" -Tee:$true
       
Write-Log -Error $_.Exception.Message
-Tee:$true
       
return
   
}
   
#Set the IAM Roles
   
$FileShareContributorRole = Get-AzRoleDefinition "Storage
File Data SMB Share Contributor"
   
#Obtain the Storage Key
from the Key Vault
   
Write-Log -Message "Getting
Storage Account Key" -Tee:$true
   
$secret = Get-AzKeyVaultSecret -VaultName $vaultName -Name $secretName
   
$secret = $secret.SecretValueText
   
#CSV File Process
   
if ($CSVPath) {
       
#Map the Storage
       
Write-Log -Message "Mapping
Storage Account File Share to Z:" -Tee:$true
       
$cmd = net use z: \\yourstorageaccount.file.core.windows.net\yourshare $secret /user:Azure\storageaccountname
       
if ($cmd -match "The
command completed successfully.")
       
{
            Write-Log -Message "Azure
Storage Successfully mapped" -Tee:$true
       
} else {
            Write-Log -Error "Error
mapping Azure Storage. Exiting . . ." -Tee:$true
            Write-Log -Error $cmd -Tee:$true
            return
       
}
       
Write-Log -Message "Adding
multiple users from CSV file" -Tee:$true
       
try {
            Write-Log -Message "Importing
User List from: $CSVPath" -Tee:$true
            $csv = Import-CSV -Path $CSVPath -ErrorAction Stop
            Write-Log -Message "User
List Imported" -Tee:$true
       
} catch {
            Write-Log -Error "Error
Importing User List" -Tee:$true
            Write-Log -Error $_.Exception.Message
-Tee:$true
       
}
       
#Assign users to the WVD
Host Pool
       
foreach ($user in $csv)
       
{
            try {
                Write-Log -Message "Adding user: $($user.upn) to WVD" -Tee:$true
                Add-RdsAppGroupUser -TenantName $tenantName -HostPoolName $HostPoolName -AppGroupName $AppGroupName -UserPrincipalName $user.upn
-ErrorAction Stop
                Write-Log -Message "Successfully added user" -Tee:$true
            } catch {
                Write-Log -Error "Error adding user" -Tee:$true
                Write-Log -Error $_.Exception.Message
-Tee:$true
            }
            #Set
the FSLogix Profile store as the storage scope
            $scope = "/subscriptions/yourAzureSubscription/resourceGroups/yourresourcegroup/providers/Microsoft.Storage/storageAccounts/yourstorageaccount/fileServices/default/fileshares/yourshare"
            #Assign
users the required IAM roles
            try {
                Write-Log -Message "Adding role definitions to user $($user.upn)" -Tee:$true
                New-AzRoleAssignment -SignInName $($user.upn)
-RoleDefinitionName $FileShareContributorRole.Name
-Scope $scope -ErrorAction Stop
   
            Write-Log -Message "Successfully added role definitions to user $($user.upn)" -Tee:$true
          
} catch {
               Write-Log -Error "Error adding role definitions to user $($user.upn)" -Tee:$true
               Write-Log -Error $_.Exception.Message
-Tee:$true
          
}
          
#Assign the corect NTFS
permissions to the FSLogix Profile share
          
$grant = $user.upn
+ ":(f)"
          
$result = icacls z: /grant $grant
          
if ($result -match "Successfully
processed 1 files") {
               Write-Log -Message "NTFS permissions assigned for user: $($user.upn)" -Tee:$true
          
} else {
               Write-Log -Error "Failed to assign NTFS permissions for user: $($user.upn)" -Tee:$true
               Write-Log -Error $result -Tee:$true
          
}
       
}
       
#Disconnect the Storage
       
Write-Log -Message "Removing
mapping for Azure Storage" -Tee:$true
       
$result = net use z: /delete
       
if ($result -match "z:
was deleted successfully") {
            Write-Log -Message "Drive
mapping removed" -Tee:$true
       
} else {
            Write-Log -Error "Error
removing drive map" -Tee:$true
            Write-Log -Error $result -Tee:$true
            return
       
} 
   
#UPN Process
   
} else {
       
if ($upn)
       
{
            #Map
the Storage
            Write-Log -Message "Mapping
Storage Account File Share to Z:" -Tee:$true
            $cmd = net use z: \\yourstorageaccount.file.core.windows.net\yourshare $secret /user:Azure\storageaccountname
            if ($cmd -match "The
command completed successfully.")
            {
                Write-Log -Message "Azure Storage Successfully mapped" -Tee:$true
            } else {
                Write-Log -Error "Error mapping Azure Storage. Exiting . . ." -Tee:$true
                Write-Log -Error $cmd -Tee:$true
                return
            }
            #Assign
user to the WVD Host Pool
            Write-Log -Message "Adding
single user: $upn" -Tee:$true
            try {
                Write-Log -Message "Adding user: $($upn)
to WVD" -Tee:$true
                Add-RdsAppGroupUser -TenantName $tenantName -HostPoolName $HostPoolName -AppGroupName $AppGroupName -UserPrincipalName $upn -ErrorAction Stop
                Write-Log -Message "Successfully added user" -Tee:$true
            } catch {
                Write-Log -Error "Error adding user" -Tee:$true
                Write-Log -Error $_.Exception.Message
-Tee:$true
            }
            #Set
the FSLogix Profile store as the storage scope
            $scope = "/subscriptions/yourAzureSubscription/resourceGroups/yourresourcegroup/providers/Microsoft.Storage/storageAccounts/yourstorageaccount/fileServices/default/fileshares/yourshare"
            #Assign
user the required IAM roles
            try {
                Write-Log -Message "Adding role definitions to user $upn" -Tee:$true
                New-AzRoleAssignment -SignInName $upn -RoleDefinitionName $FileShareContributorRole.Name
-Scope $scope -ErrorAction Stop
                Write-Log -Message "Successfully added role definitions to user $upn" -Tee:$true
            } catch {
            Write-Log -Error "Error
adding role definitions to user $upn" -Tee:$true
            Write-Log -Error $_.Exception.Message
-Tee:$true
            }
            #Assign
the corect NTFS permissions to the FSLogix Profile share
            $grant = $upn + ":(f)"
            $result = icacls z: /grant $grant
            if ($result -match "Successfully
processed 1 files") {
                Write-Log -Message "NTFS permissions assigned for user: $upn" -Tee:$true
            } else {
                Write-Log -Error "Failed to assign NTFS permissions for user: $upn" -Tee:$true
                Write-Log -Error $result -Tee:$true
            }
            #Disconnect
the Storage
            Write-Log -Message "Removing
mapping for Azure Storage" -Tee:$true
            $result = net use z: /delete
            if ($result -match "z:
was deleted successfully") {
                Write-Log -Message "Drive mapping removed" -Tee:$true
            } else {
                Write-Log -Error "Error removing drive map" -Tee:$true
                Write-Log -Error $result -Tee:$true
                return
            }
       
} else {
            Write-Log -Message "No
User Entered. Exiting. . . " -Tee:$true
            return
       
}
   
}
}
As I said before, big thanks to @James_Tighe for the help with the logging and Azure Key Vault aspect of this one!


Comments
Post a Comment