src/bicep/add-ons/azure-virtual-desktop/artifacts/Set-NtfsPermissions.ps1 (192 lines of code) (raw):
param
(
[String]$ActiveDirectorySolution,
[String]$DomainJoinPassword,
[String]$DomainJoinUserPrincipalName,
[String]$FileShares,
[String]$Netbios,
[String]$OrganizationalUnitPath,
[string]$ResourceManagerUri,
[String]$SecurityPrincipalNames,
[String]$SmbServerNamePrefix,
[String]$StorageAccountPrefix,
[String]$StorageAccountResourceGroupName,
[Int]$StorageCount,
[Int]$StorageIndex,
[String]$StorageService,
[String]$StorageSuffix,
[String]$SubscriptionId,
[String]$UserAssignedIdentityClientId
)
$ErrorActionPreference = 'Stop'
$WarningPreference = 'SilentlyContinue'
##############################################################
# Install Active Directory PowerShell module
##############################################################
if($StorageService -eq 'AzureNetAppFiles' -or ($StorageService -eq 'AzureFiles' -and $ActiveDirectorySolution -eq 'ActiveDirectoryDomainServices'))
{
$RsatInstalled = (Get-WindowsFeature -Name 'RSAT-AD-PowerShell').Installed
if(!$RsatInstalled)
{
Install-WindowsFeature -Name 'RSAT-AD-PowerShell' | Out-Null
}
}
##############################################################
# Variables
##############################################################
# Convert parameters values from JSON array to a PowerShell array
[array]$SecurityPrincipalNames = $SecurityPrincipalNames.Replace('\','') | ConvertFrom-Json
[array]$Shares = $FileShares.Replace('\','') | ConvertFrom-Json
if($StorageService -eq 'AzureNetAppFiles' -or ($StorageService -eq 'AzureFiles' -and $ActiveDirectorySolution -eq 'ActiveDirectoryDomainServices'))
{
# Create Domain credential
$DomainUsername = $DomainJoinUserPrincipalName
$DomainPassword = ConvertTo-SecureString -String $DomainJoinPassword -AsPlainText -Force
[pscredential]$DomainCredential = New-Object System.Management.Automation.PSCredential ($DomainUsername, $DomainPassword)
# Get Domain information
$Domain = Get-ADDomain -Credential $DomainCredential -Current 'LocalComputer'
}
if($StorageService -eq 'AzureFiles')
{
$FilesSuffix = '.file.' + $StorageSuffix
# Fix the resource manager URI since only AzureCloud contains a trailing slash
$ResourceManagerUriFixed = if ($ResourceManagerUri[-1] -eq '/') { $ResourceManagerUri.Substring(0, $ResourceManagerUri.Length - 1) } else { $ResourceManagerUri }
# Get an access token for Azure resources
$AzureManagementAccessToken = (Invoke-RestMethod `
-Headers @{Metadata = "true" } `
-Uri $('http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=' + $ResourceManagerUriFixed + '&client_id=' + $UserAssignedIdentityClientId)).access_token
# Set header for Azure Management API
$AzureManagementHeader = @{
'Content-Type' = 'application/json'
'Authorization' = 'Bearer ' + $AzureManagementAccessToken
}
}
##############################################################
# Process Storage Resources
##############################################################
for($i = 0; $i -lt $StorageCount; $i++)
{
# Determine Principal for assignment
$SecurityPrincipalName = $SecurityPrincipalNames[$i]
$Group = $Netbios + '\' + $SecurityPrincipalName
# Get storage resource details
switch($StorageService)
{
'AzureNetAppFiles' {
$Credential = $DomainCredential
$SmbServerName = (Get-ADComputer -Filter "Name -like '$SmbServerNamePrefix*'" -Credential $DomainCredential).Name
$FileServer = '\\' + $SmbServerName + '.' + $Domain.DNSRoot
}
'AzureFiles' {
$StorageAccountName = $($StorageAccountPrefix + ($i + $StorageIndex).ToString().PadLeft(2,'0')).Substring(0,15)
$FileServer = '\\' + $StorageAccountName + $FilesSuffix
# Get the storage account key
$StorageKey = (Invoke-RestMethod `
-Headers $AzureManagementHeader `
-Method 'POST' `
-Uri $($ResourceManagerUriFixed + '/subscriptions/' + $SubscriptionId + '/resourceGroups/' + $StorageAccountResourceGroupName + '/providers/Microsoft.Storage/storageAccounts/' + $StorageAccountName + '/listKeys?api-version=2023-05-01')).keys[0].value
# Create credential for accessing the storage account
$StorageUsername = 'Azure\' + $StorageAccountName
$StoragePassword = ConvertTo-SecureString -String "$($StorageKey)" -AsPlainText -Force
[pscredential]$StorageKeyCredential = New-Object System.Management.Automation.PSCredential ($StorageUsername, $StoragePassword)
$Credential = $StorageKeyCredential
if($ActiveDirectorySolution -eq 'ActiveDirectoryDomainServices')
{
# Get / create kerberos key for Azure Storage Account
$KerberosKey = ((Invoke-RestMethod `
-Headers $AzureManagementHeader `
-Method 'POST' `
-Uri $($ResourceManagerUriFixed + '/subscriptions/' + $SubscriptionId + '/resourceGroups/' + $StorageAccountResourceGroupName + '/providers/Microsoft.Storage/storageAccounts/' + $StorageAccountName + '/listKeys?api-version=2023-05-01&$expand=kerb')).keys | Where-Object { $_.Keyname -contains 'kerb1' }).Value
if (!$KerberosKey)
{
Invoke-RestMethod `
-Body (@{keyName = 'kerb1' } | ConvertTo-Json) `
-Headers $AzureManagementHeader `
-Method 'POST' `
-Uri $($ResourceManagerUriFixed + '/subscriptions/' + $SubscriptionId + '/resourceGroups/' + $StorageAccountResourceGroupName + '/providers/Microsoft.Storage/storageAccounts/' + $StorageAccountName + '/regenerateKey?api-version=2023-05-01')
$Key = ((Invoke-RestMethod `
-Headers $AzureManagementHeader `
-Method 'POST' `
-Uri $($ResourceManagerUriFixed + '/subscriptions/' + $SubscriptionId + '/resourceGroups/' + $StorageAccountResourceGroupName + '/providers/Microsoft.Storage/storageAccounts/' + $StorageAccountName + '/listKeys?api-version=2023-05-01&$expand=kerb')).keys | Where-Object { $_.Keyname -contains 'kerb1' }).Value
}
else
{
$Key = $KerberosKey
}
# Creates a password for the Azure Storage Account in AD using the Kerberos key
$ComputerPassword = ConvertTo-SecureString -String $Key.Replace("'","") -AsPlainText -Force
# Create the SPN value for the Azure Storage Account; attribute for computer object in AD
$SPN = 'cifs/' + $StorageAccountName + $FilesSuffix
# Create the Description value for the Azure Storage Account; attribute for computer object in AD
$Description = "Computer account object for Azure storage account $($StorageAccountName)."
# Create the AD computer object for the Azure Storage Account
$Computer = Get-ADComputer -Credential $DomainCredential -Filter {Name -eq $StorageAccountName}
if($Computer)
{
Remove-ADComputer -Credential $DomainCredential -Identity $StorageAccountName -Confirm:$false
}
$ComputerObject = New-ADComputer -Credential $DomainCredential -Name $StorageAccountName -Path $OrganizationalUnitPath -ServicePrincipalNames $SPN -AccountPassword $ComputerPassword -Description $Description -PassThru
$Body = (@{
properties = @{
azureFilesIdentityBasedAuthentication = @{
activeDirectoryProperties = @{
accountType = 'Computer'
azureStorageSid = $ComputerObject.SID.Value
domainGuid = $Domain.ObjectGUID.Guid
domainName = $Domain.DNSRoot
domainSid = $Domain.DomainSID.Value
forestName = $Domain.Forest
netBiosDomainName = $Domain.NetBIOSName
samAccountName = $StorageAccountName
}
directoryServiceOptions = 'AD'
}
}
} | ConvertTo-Json -Depth 6 -Compress)
Invoke-RestMethod `
-Body $Body `
-Headers $AzureManagementHeader `
-Method 'PATCH' `
-Uri $($ResourceManagerUriFixed + '/subscriptions/' + $SubscriptionId + '/resourceGroups/' + $StorageAccountResourceGroupName + '/providers/Microsoft.Storage/storageAccounts/' + $StorageAccountName + '?api-version=2023-05-01')
# Set the Kerberos encryption on the computer object
$DistinguishedName = 'CN=' + $StorageAccountName + ',' + $OrganizationalUnitPath
Set-ADComputer -Credential $DomainCredential -Identity $DistinguishedName -KerberosEncryptionType 'AES256' | Out-Null
# Reset the Kerberos key on the Storage Account
Invoke-RestMethod `
-Body (@{keyName = 'kerb1' } | ConvertTo-Json) `
-Headers $AzureManagementHeader `
-Method 'POST' `
-Uri $($ResourceManagerUriFixed + '/subscriptions/' + $SubscriptionId + '/resourceGroups/' + $StorageAccountResourceGroupName + '/providers/Microsoft.Storage/storageAccounts/' + $StorageAccountName + '/regenerateKey?api-version=2023-05-01')
$Key = ((Invoke-RestMethod `
-Headers $AzureManagementHeader `
-Method 'POST' `
-Uri $($ResourceManagerUriFixed + '/subscriptions/' + $SubscriptionId + '/resourceGroups/' + $StorageAccountResourceGroupName + '/providers/Microsoft.Storage/storageAccounts/' + $StorageAccountName + '/listKeys?api-version=2023-05-01&$expand=kerb')).keys | Where-Object { $_.Keyname -contains 'kerb1' }).Value
# Update the password on the computer object with the new Kerberos key on the Storage Account
$NewPassword = ConvertTo-SecureString -String $Key -AsPlainText -Force
Set-ADAccountPassword -Credential $DomainCredential -Identity $DistinguishedName -Reset -NewPassword $NewPassword | Out-Null
}
}
}
foreach($Share in $Shares)
{
# Mount file share
$FileShare = $FileServer + '\' + $Share
New-PSDrive -Name 'Z' -PSProvider 'FileSystem' -Root $FileShare -Credential $Credential | Out-Null
# Set recommended NTFS permissions on the file share
$ACL = Get-Acl -Path 'Z:'
$CreatorOwner = New-Object System.Security.Principal.Ntaccount ("Creator Owner")
$ACL.PurgeAccessRules($CreatorOwner)
$AuthenticatedUsers = New-Object System.Security.Principal.Ntaccount ("Authenticated Users")
$ACL.PurgeAccessRules($AuthenticatedUsers)
$Users = New-Object System.Security.Principal.Ntaccount ("Users")
$ACL.PurgeAccessRules($Users)
$DomainUsers = New-Object System.Security.AccessControl.FileSystemAccessRule("$Group","Modify","None","None","Allow")
$ACL.SetAccessRule($DomainUsers)
$CreatorOwner = New-Object System.Security.AccessControl.FileSystemAccessRule("Creator Owner","Modify","ContainerInherit,ObjectInherit","InheritOnly","Allow")
$ACL.AddAccessRule($CreatorOwner)
$ACL | Set-Acl -Path 'Z:' | Out-Null
# Unmount file share
Remove-PSDrive -Name 'Z' -PSProvider 'FileSystem' -Force | Out-Null
Start-Sleep -Seconds 5 | Out-Null
}
}