Back to Blog
DevOps
3 min read

Domain Joining Azure Storage Accounts via DevOps Pipeline

AzureStorageActive DirectoryDevOpsAutomation

Azure Files with Active Directory authentication is great for file shares that need NTFS permissions. But the setup requires domain joining the storage account - and doing that in a pipeline is tricky.

Why Domain Join?

Without AD authentication, Azure Files only supports:

  • Storage account key (shared key)
  • Azure AD (Entra) authentication for specific scenarios

For NTFS permissions that work like traditional file servers, you need AD DS authentication.

The AzFilesHybrid Module

Microsoft provides the AzFilesHybrid PowerShell module to domain join storage accounts:

# Download and import
$url = "https://github.com/Azure-Samples/azure-files-samples/releases/latest/download/AzFilesHybrid.zip"
Invoke-WebRequest -Uri $url -OutFile AzFilesHybrid.zip
Expand-Archive -Path AzFilesHybrid.zip -DestinationPath .

Import-Module .\AzFilesHybrid\AzFilesHybrid.psd1

Pipeline Configuration

Create a pipeline that can authenticate to both Azure and AD:

trigger: none

pool:
  vmImage: 'windows-latest'

variables:
  storageAccountName: 'stfilesproduction'
  resourceGroupName: 'rg-storage'
  ouPath: 'OU=Storage,DC=corp,DC=local'

steps:
- task: AzurePowerShell@5
  displayName: 'Domain Join Storage Account'
  inputs:
    azureSubscription: 'your-service-connection'
    ScriptType: 'InlineScript'
    Inline: |
      # Download AzFilesHybrid
      $url = "https://github.com/Azure-Samples/azure-files-samples/releases/latest/download/AzFilesHybrid.zip"
      Invoke-WebRequest -Uri $url -OutFile AzFilesHybrid.zip
      Expand-Archive -Path AzFilesHybrid.zip -DestinationPath . -Force

      Import-Module .\AzFilesHybrid\AzFilesHybrid.psd1 -Force

      # Join the storage account to AD
      Join-AzStorageAccount `
        -ResourceGroupName "$(resourceGroupName)" `
        -StorageAccountName "$(storageAccountName)" `
        -DomainAccountType "ComputerAccount" `
        -OrganizationalUnitDistinguishedName "$(ouPath)"
    azurePowerShellVersion: 'LatestVersion'

The Challenge: AD Credentials

The problem is Join-AzStorageAccount needs credentials that can create objects in AD. Your pipeline service connection authenticates to Azure, not AD.

Option 1: Hybrid Runbook Worker

Run the pipeline on a self-hosted agent that's domain joined:

pool:
  name: 'OnPremAgents'
  demands:
    - Agent.OS -equals Windows_NT

The agent runs as a domain account with permission to create computer objects.

Option 2: AD Service Account

Store AD credentials in Key Vault and use them in the script:

- task: AzureKeyVault@2
  inputs:
    azureSubscription: 'your-service-connection'
    KeyVaultName: 'kv-devops-secrets'
    SecretsFilter: 'AD-Join-Username,AD-Join-Password'
    RunAsPreJob: true

- task: PowerShell@2
  displayName: 'Join Storage to AD'
  inputs:
    targetType: 'inline'
    script: |
      $username = "$(AD-Join-Username)"
      $password = ConvertTo-SecureString "$(AD-Join-Password)" -AsPlainText -Force
      $cred = New-Object System.Management.Automation.PSCredential($username, $password)

      # This requires line of sight to AD domain controller
      Join-AzStorageAccount `
        -ResourceGroupName "$(resourceGroupName)" `
        -StorageAccountName "$(storageAccountName)" `
        -DomainAccountType "ComputerAccount" `
        -OrganizationalUnitDistinguishedName "$(ouPath)" `
        -Domain "corp.local" `
        -Credential $cred

Option 3: Azure Automation with Hybrid Worker

Use Azure Automation with a Hybrid Runbook Worker for the best of both:

# Automation Runbook
param(
  [string]$StorageAccountName,
  [string]$ResourceGroupName,
  [string]$OUPath
)

Import-Module AzFilesHybrid

# Use managed identity for Azure auth
Connect-AzAccount -Identity

# AD auth happens automatically on domain-joined worker
Join-AzStorageAccount `
  -ResourceGroupName $ResourceGroupName `
  -StorageAccountName $StorageAccountName `
  -DomainAccountType "ComputerAccount" `
  -OrganizationalUnitDistinguishedName $OUPath

Verifying the Join

After joining, verify in the portal or with PowerShell:

$storageAccount = Get-AzStorageAccount `
  -ResourceGroupName $resourceGroupName `
  -Name $storageAccountName

$storageAccount.AzureFilesIdentityBasedAuth

# Should show:
# DirectoryServiceOptions : AD
# ActiveDirectoryProperties : ...

Setting NTFS Permissions

After domain join, set permissions on the share:

# Map with storage key first
$key = (Get-AzStorageAccountKey -ResourceGroupName $rg -AccountName $sa)[0].Value
net use Z: "\\$sa.file.core.windows.net\share" /user:Azure\$sa $key

# Set NTFS permissions
icacls Z: /grant "CORP\FileUsers:(M)"
icacls Z: /grant "CORP\FileAdmins:(F)"

Need help with Azure Files and hybrid identity? Get in touch - we help organisations integrate cloud storage with existing infrastructure.

Need help with your Azure environment?

Get in touch for a free consultation.

Get in Touch