Azure Files with Entra ID Join for AVD FSLogix Using Secure Variables and Powershell Credential Manager

This is a method to use Azure Files with Entra ID Joined session hosts for FSLogix. Credit to Tony Cai, Marcel Meurer, and Dave Stephenson. This is largely based on Tony Cai's previous article on the subject with the added benefit of using Nerdio secure variables and powershell secure strings.

 

In Nerdio:

Create a new Storage Account:

Apply the following settings:

  • Storage account: "unique storage account name here"
  • Resource Group: "resource group name here"
  • Location: "azure region here"
  • Performance: Premium
  • File Share name: "any name here"
  • Provisioned capacity: 100GB minimum
  •     Keep in mind the default fslogix profile size is set to 10GB per user so plan your capacity accordingly. Also, remember you pay for the size of the storage account and not for the amount of used space so don't oversize either.
  • Uncheck "Join to AD", this is because we are using a workaround to access the fileshare since they don't support cloud only identity authentication to azure files
  • Don't worry about any other settings, they aren't necessary for this workaround

In Azure:

Find your storage account you just created and open it, then select access keys on the left. You'll see the following screen.

Make sure to note down your storage account name and key by pressing show then copy the key.

 

Back in Nerdio:

Step 1:

Create a new Scripted Action and set Script Execution Method as "Individual" and name the script "Entra ID Joined Azure Files for FSLgx".

 

For the script copy the following:

-----------------------------------------------------------------------------------------------------------------------------------------

# Set Variables
# Requires the Secure Variables to be added: FSLgxStorageAccount and FSLgxSecret
$storageAccount = "$($SecureVars.FSLgxStorageAccount)"
$secret = "$($SecureVars.FSLgxSecret)" | ConvertTo-SecureString -AsPlainText -Force

# Adds credentials to Windows Credential Manager for Azure File Share
Function Add-FileServerCredential
{
    param(
        [string] $storageAccount,
        [securestring] $secret
    )

    # Create credentials from passed in Secure creds
    $fileServer = "$storageAccount.file.core.windows.net"
    $username = "localhost\$storageAccount"

    # Create credential object with storageAccountName as username and secret as password
    $credential = New-Object -TypeName PSCredential -ArgumentList $username, $secret

    $windowsCredential = New-StoredCredential -Target $fileServer -Credential $credential -Persist LocalMachine -Type DomainPassword
    if ($null -eq $windowsCredential)
    {
        throw "Error adding credential to Windows Credential Manager:`n $_"
    }
    else
    {
        Write-Host "Credentials successfully added to Windows Credential Manager."
    }
}

# Creates Registry Key LoadCredKeyFromProfile
Function Add-RegKey
{
    # Create Registry Path

    # Specify the registry path
    $regKeyPath = "HKLM:\Software\Policies\Microsoft\AzureADAccount"

    # Check if the registry path exists, and create it if not
    if (-not (Test-Path $regKeyPath))
    {
        New-Item -Path $regKeyPath -Force
    }

    # Create Registry Key
    $regKeyValue = "loadCredKeyFromProfile"
    $regKeyType = "DWORD"
    $regKeyData = 1
    New-ItemProperty -Path $regKeyPath -Name $regKeyValue -Value $regKeyData -PropertyType $regKeyType -Force | Out-Null

    #Check if key was created
    if (Test-Path $regKeyPath)
    {
        # Check if the registry value exists
        $regValue = Get-ItemProperty -Path $regKeyPath -Name $regKeyValue -ErrorAction SilentlyContinue
        if ($null -eq $regValue)
        {
            throw "Registry key '$regKeyValue' exists, but the value is not set."
        }
        else
        {
            Write-Host "Registry key successfully created."
        }
    }
    else
    {
        throw "Registry key does not exist."
    }
}

Function Main
{
    param(
        [string] $storageAccount,
        [securestring] $secret
    )

    Add-FileServerCredential -storageAccount $storageAccount -secret $secret
    Add-RegKey
}

Main -storageAccount $storageAccount -secret $secret

-----------------------------------------------------------------------------------------------------------------------------------------

One caveat with this script, make sure on your base image to install powershell credential manager using the following command: "install-module -name CredentialManager".

I tried bundling this into the same script but was running into issues installing the module on vm creation. If you have luck please post in the comments.

Step 2:

In your host pool properties select FSLogix on the left:

For FSLogix Profiles path enter the path:

  • \\[[STORAGEACCOUNTFQDN]\[FILESHARE]
  • Ex: \\test01182024.file.core.windows.net\fslogix

Next, add the following registry key:

  • AccessNetworkAsComputerObject
  • Vaue: 1

You can find this by selecting FSLogix Registry Options, then All Settings and filter it by name.

 

Now, select VM Deployments on the left side

Set your script to deploy on VM Creation and VM Started. This way the credential is sure to stick in Windows Credential Manager and not expire.

 

Last part, we need to add the secure variables in Nerdio.

Go to settings inside the account you are using the script on, then select Portal, then add a new variable. Use the following settings:

  • Name: FSLgxStorageAccount
  • Value: "storage account name you noted down earlier"
  • Secure vs Inherited: Secure
  • Windows Scripts: "Entra ID Joined Azure Files for FSLgx"
  • Azure Runbooks: Deselect "All Scripts"

Add a second variable, and use the following settings:

  • Name: FSLgxSecret
  • Value: "Storage account access key you noted down earlier"
  • Secure vs Inherited: Secure
  • Windows Scripts: "Entra ID Joined Azure Files for FSLgx"
  • Azure Runbooks: Deselect "All Scripts"
1

Comments (5 comments)

Avatar
DStephenson

Very cool, Kris Pennington! (and welcome to the community 🙂)

For the "credential manager" issue, could you do that as a separate scripted action and then add it and the "Entra ID Joined Azure Files for FSLgx" scripted action to a scripted action group and call that scripted action group during the VM creation?

Overview of Scripted Actions Groups – Nerdio Help Center

0
Avatar
Kris Pennington

I'm fairly certain that you would still run into the same issue doing it that way. I haven't tested that to confirm, but from when I was testing trying to install the powershell module credentialmanager during vm creation I was getting error's that I couldn't resolve until I waited after the vm was already created. It just seemed easier to install the module on the image and re-image my hosts than to troubleshoot the module installation. There's no reason you couldn't create a separate scripted action to install that module and run that on the image to make it easier though.

1
Avatar
Michal Szarecki

Hi Kris,
Is this working well for you?
This script is not adding credentials for me in Windows 11, or I can't see them.
Script works fine when I login as local admin and run it with variables filled in.

But when executed by Nerdio I'm getting below and credentials are not there, regardless of the message below

Script reference: https://cssa941f53e4bf3ab9b4d06f.blob.core.windows.net/custom-scripts/c9cfe393cf6424442bdbb40cce25a5a2.ps1
Pass variables: FSLgxStorageAccount, FSLgxSecret
Pass environment variables: TenantDomain:domain.onmicrosoft.com, DefaultDomain:, TenantId:TenantId, SubscriptionId:SubId, CustomerName:domain.com
Remove old extension, old result: {"name":"vm-avdh-cg-ae2d-run-script-ext-c9cfe393cf6424442bdbb40cce25a5a2.ps1","type":"Microsoft.Compute.CustomScriptExtension","typeHandlerVersion":"1.9.5","substatuses":[{"code":"ComponentStatus/StdOut/succeeded","level":"Info","displayStatus":"Provisioning succeeded","message":"","time":null},{"code":"ComponentStatus/StdErr/succeeded","level":"Info","displayStatus":"Provisioning succeeded","message":"Error adding credential to Windows Credential Manager:\r\n \r\nAt \r\nC:\\Packages\\Plugins\\Microsoft.Compute.CustomScriptExtension\\1.9.5\\Downloads\\0\\c9cfe393cf6424442bdbb40cce25a5a2.ps1:59 \r\nchar:9\r\n+ throw \"Error adding credential to Windows Credential Manager: ...\r\n+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n + CategoryInfo : OperationStopped: (Error adding cr...tial Manager:\r\n :String) [], RuntimeException\r\n + FullyQualifiedErrorId : Error adding credential to Windows Credential Manager:\r\n \r\n \r\n","time":null}],"statuses":[{"code":"ProvisioningState/failed/1","level":"Error","displayStatus":"Provisioning failed","message":"Finished executing command","time":null}]}
Extension was removed
Script output:
VERBOSE: Writing credential to Windows Credential Manager
Credentials successfully added to Windows Credential Manager.
Registry key successfully created.
Extension added successfully

0
Avatar
Kris Pennington

Yes, this is currently still working form me. Usually, when I would get that error it has something to do with the way Nerdio is passing the variable to your script. I would run a basic debug script just passing those variables and outputting to console to see if Nerdio is passing them correctly or not.

0
Avatar
Ryan Dorman

This is much more comprehensive than my solution but here's what I came up with:

Was a bit of a challenges due to escaping characters and stringing everything together in a way PowerShell liked.

$arguments = '/add:'+$SecureVars.profileAccountName+'.file.core.windows.net'+' /user:localhost\'+$SecureVars.profileAccountName+' /pass:'+$Securevars.profileAccountSASkey
start-process cmdkey.exe -ArgumentList $arguments

0

Please sign in to leave a comment.