diff --git a/readme.md b/readme.md index b618655a5..f03749dac 100644 --- a/readme.md +++ b/readme.md @@ -37,7 +37,7 @@ If you are having deployment challenges, refer to the [LZA baseline troubleshoot ## Azure Virtual Desktop - LZA Optional Deployments -### Brownfield scenarios +### Brownfield scenarios The brownfield section contains templates to deploy additional features for Azure Virtual Desktop when existing infrastructure already exists. These templates can be used individually as required. Here is the list of deployment options available: diff --git a/workload/bicep/deploy-baseline.bicep b/workload/bicep/deploy-baseline.bicep index 1f643d6c2..74e2ab7d4 100644 --- a/workload/bicep/deploy-baseline.bicep +++ b/workload/bicep/deploy-baseline.bicep @@ -55,19 +55,14 @@ param avdIdentityServiceProvider string = 'ADDS' @sys.description('Required, Eronll session hosts on Intune. (Default: false)') param createIntuneEnrollment bool = false -@sys.description('Optional, Identity ID array to grant RBAC role to access AVD application group. (Default: "")') -param avdApplicationGroupIdentitiesIds array = [] +@sys.description('Optional, Identity ID to grant RBAC role to access AVD application group and NTFS permissions. (Default: "")') +param securityPrincipalId string = '' -@allowed([ - 'Group' - 'ServicePrincipal' - 'User' -]) -@sys.description('Optional, Identity type to grant RBAC role to access AVD application group. (Default: Group)') -param avdApplicationGroupIdentityType string = 'Group' +@sys.description('Optional, Identity name to grant RBAC role to access AVD application group and NTFS permissions. (Default: "")') +param securityPrincipalName string = '' -@sys.description('AD domain name.') -param avdIdentityDomainName string +@sys.description('FQDN of on-premises AD domain, used for FSLogix storage configuration and NTFS setup. (Default: "")') +param identityDomainName string = '' @sys.description('AD domain GUID. (Default: "")') param identityDomainGuid string = '' @@ -282,9 +277,6 @@ param avdImageTemplateDefinitionId string = '' @sys.description('OU name for Azure Storage Account. It is recommended to create a new AD Organizational Unit (OU) in AD and disable password expiration policy on computer accounts or service logon accounts accordingly. (Default: "")') param storageOuPath string = '' -@sys.description('If OU for Azure Storage needs to be created - set to true and ensure the domain join credentials have priviledge to create OU and create computer objects or join to domain. (Default: false)') -param createOuForStorage bool = false - // Custom Naming // Input must followe resource naming rules on https://docs.microsoft.com/azure/azure-resource-manager/management/resource-name-rules @sys.description('AVD resources custom naming. (Default: false)') @@ -536,17 +528,18 @@ var varStorageManagedIdentityName = 'id-storage-${varComputeStorageResourcesNami var varFslogixFileShareName = avdUseCustomNaming ? fslogixFileShareCustomName : 'fslogix-pc-${varDeploymentPrefixLowercase}-${varDeploymentEnvironmentLowercase}-${varSessionHostLocationAcronym}-001' var varMsixFileShareName = avdUseCustomNaming ? msixFileShareCustomName : 'msix-pc-${varDeploymentPrefixLowercase}-${varDeploymentEnvironmentLowercase}-${varSessionHostLocationAcronym}-001' var varFslogixStorageName = avdUseCustomNaming ? '${storageAccountPrefixCustomName}fsl${varDeploymentPrefixLowercase}${varDeploymentEnvironmentComputeStorage}${varNamingUniqueStringThreeChar}' : 'stfsl${varDeploymentPrefixLowercase}${varDeploymentEnvironmentComputeStorage}${varNamingUniqueStringThreeChar}' +var varFslogixStorageFqdn = '${varFslogixStorageName}.file.${environment().suffixes.storage}' +var varMsixStorageFqdn = '${varMsixStorageName}.file.${environment().suffixes.storage}' var varMsixStorageName = avdUseCustomNaming ? '${storageAccountPrefixCustomName}msx${varDeploymentPrefixLowercase}${varDeploymentEnvironmentComputeStorage}${varNamingUniqueStringThreeChar}' : 'stmsx${varDeploymentPrefixLowercase}${varDeploymentEnvironmentComputeStorage}${varNamingUniqueStringThreeChar}' var varManagementVmName = 'vmmgmt${varDeploymentPrefixLowercase}${varDeploymentEnvironmentComputeStorage}${varSessionHostLocationAcronym}' var varAlaWorkspaceName = avdUseCustomNaming ? avdAlaWorkspaceCustomName : 'log-avd-${varDeploymentEnvironmentLowercase}-${varManagementPlaneLocationAcronym}' //'log-avd-${varAvdComputeStorageResourcesNamingStandard}-${varAvdNamingUniqueStringSixChar}' var varZtKvName = avdUseCustomNaming ? '${ztKvPrefixCustomName}-${varComputeStorageResourcesNamingStandard}-${varNamingUniqueStringTwoChar}' : 'kv-key-${varComputeStorageResourcesNamingStandard}-${varNamingUniqueStringTwoChar}' // max length limit 24 characters var varZtKvPrivateEndpointName = 'pe-${varZtKvName}-vault' // -var varFsLogixScriptArguments = (avdIdentityServiceProvider == 'AAD') ? '-volumeshare ${varFslogixSharePath} -storageAccountName ${varFslogixStorageName} -identityDomainName ${avdIdentityDomainName}' : '-volumeshare ${varFslogixSharePath}' var varFslogixSharePath = '\\\\${varFslogixStorageName}.file.${environment().suffixes.storage}\\${varFslogixFileShareName}' var varBaseScriptUri = 'https://raw.githubusercontent.com/Azure/avdaccelerator/main/workload/' -var varFslogixScriptUri = (avdIdentityServiceProvider == 'AAD') ? '${varBaseScriptUri}scripts/Set-FSLogixRegKeysAAD.ps1' : '${varBaseScriptUri}scripts/Set-FSLogixRegKeys.ps1' -var varFsLogixScript = (avdIdentityServiceProvider == 'AAD') ? './Set-FSLogixRegKeysAad.ps1' : './Set-FSLogixRegKeys.ps1' +var varSessionHostConfigurationScriptUri = '${varBaseScriptUri}scripts/Set-SessionHostConfiguration.ps1' +var varSessionHostConfigurationScript = './Set-SessionHostConfiguration.ps1' var varDiskEncryptionKeyExpirationInEpoch = dateTimeToEpoch(dateTimeAdd(time, 'P${string(diskEncryptionKeyExpirationInDays)}D')) var varAvdAgentPackageLocation = 'https://wvdportalstorageblob.blob.${environment().suffixes.storage}/galleryartifacts/Configuration_09-08-2022.zip' var varCreateStorageDeployment = (createAvdFslogixDeployment || createMsixDeployment == true) ? true : false @@ -766,7 +759,6 @@ var varStorageToDomainScript = './Manual-DSC-Storage-Scripts.ps1' var varOuStgPath = !empty(storageOuPath) ? '"${storageOuPath}"' : '"${varDefaultStorageOuPath}"' var varDefaultStorageOuPath = (avdIdentityServiceProvider == 'AADDS') ? 'AADDC Computers' : 'Computers' var varStorageCustomOuPath = !empty(storageOuPath) ? 'true' : 'false' -var varCreateOuForStorageString = string(createOuForStorage) var varAllDnsServers = '${customDnsIps},168.63.129.16' var varDnsServers = empty(customDnsIps) ? [] : (split(varAllDnsServers, ',')) var varCreateVnetPeering = !empty(existingHubVnetResourceId) ? true : false @@ -785,7 +777,7 @@ var varCustomResourceTags = createResourceTags ? { CostCenter: costCenterTag } : {} var varAllComputeStorageTags = { - DomainName: avdIdentityDomainName + DomainName: identityDomainName IdentityServiceProvider: avdIdentityServiceProvider } var varAvdDefaultTags = { @@ -968,8 +960,7 @@ module managementPLane './modules/avdManagementPlane/deploy.bicep' = { startVmOnConnect: (avdHostPoolType == 'Pooled') ? avdDeployScalingPlan : avdStartVmOnConnect workloadSubsId: avdWorkloadSubsId identityServiceProvider: avdIdentityServiceProvider - applicationGroupIdentitiesIds: avdApplicationGroupIdentitiesIds - applicationGroupIdentityType: avdApplicationGroupIdentityType + securityPrincipalIds: array(securityPrincipalId) tags: createResourceTags ? union(varCustomResourceTags, varAvdDefaultTags) : varAvdDefaultTags alaWorkspaceResourceId: avdDeployMonitoring ? (deployAlaWorkspace ? monitoringDiagnosticSettings.outputs.avdAlaWorkspaceResourceId : alaExistingWorkspaceResourceId) : '' hostPoolAgentUpdateSchedule: varHostPoolAgentUpdateSchedule @@ -996,7 +987,7 @@ module identity './modules/identity/deploy.bicep' = { enableStartVmOnConnect: avdStartVmOnConnect identityServiceProvider: avdIdentityServiceProvider createStorageDeployment: varCreateStorageDeployment - appGroupIdentitiesIds: avdApplicationGroupIdentitiesIds + securityPrincipalIds: array(securityPrincipalId) tags: createResourceTags ? union(varCustomResourceTags, varAvdDefaultTags) : varAvdDefaultTags } dependsOn: [ @@ -1132,7 +1123,7 @@ module managementVm './modules/storageAzureFiles/.bicep/managementVm.bicep' = if domainJoinUserName: avdDomainJoinUserName wrklKvName: varWrklKvName serviceObjectsRgName: varServiceObjectsRgName - identityDomainName: avdIdentityDomainName + identityDomainName: identityDomainName ouPath: varMgmtVmSpecs.ouPath osDiskType: varMgmtVmSpecs.osDiskType location: avdSessionHostLocation @@ -1165,6 +1156,7 @@ module fslogixAzureFilesStorage './modules/storageAzureFiles/deploy.bicep' = if fileShareMultichannel: (fslogixStoragePerformance == 'Premium') ? true : false storageSku: varFslogixStorageSku fileShareQuotaSize: fslogixFileShareQuotaSize + storageAccountFqdn: varFslogixStorageFqdn storageAccountName: varFslogixStorageName storageToDomainScript: varStorageToDomainScript storageToDomainScriptUri: varStorageToDomainScriptUri @@ -1174,12 +1166,12 @@ module fslogixAzureFilesStorage './modules/storageAzureFiles/deploy.bicep' = if managementVmName: varManagementVmName deployPrivateEndpoint: deployPrivateEndpointKeyvaultStorage ouStgPath: varOuStgPath - createOuForStorageString: varCreateOuForStorageString managedIdentityClientId: varCreateStorageDeployment ? identity.outputs.managedIdentityStorageClientId : '' + securityPrincipalName: securityPrincipalName domainJoinUserName: avdDomainJoinUserName wrklKvName: varWrklKvName serviceObjectsRgName: varServiceObjectsRgName - identityDomainName: avdIdentityDomainName + identityDomainName: identityDomainName identityDomainGuid: identityDomainGuid sessionHostLocation: avdSessionHostLocation storageObjectsRgName: varStorageObjectsRgName @@ -1207,6 +1199,7 @@ module msixAzureFilesStorage './modules/storageAzureFiles/deploy.bicep' = if (cr fileShareMultichannel: (msixStoragePerformance == 'Premium') ? true : false storageSku: varMsixStorageSku fileShareQuotaSize: msixFileShareQuotaSize + storageAccountFqdn: varMsixStorageFqdn storageAccountName: varMsixStorageName storageToDomainScript: varStorageToDomainScript storageToDomainScriptUri: varStorageToDomainScriptUri @@ -1216,12 +1209,12 @@ module msixAzureFilesStorage './modules/storageAzureFiles/deploy.bicep' = if (cr managementVmName: varManagementVmName deployPrivateEndpoint: deployPrivateEndpointKeyvaultStorage ouStgPath: varOuStgPath - createOuForStorageString: varCreateOuForStorageString managedIdentityClientId: varCreateStorageDeployment ? identity.outputs.managedIdentityStorageClientId : '' + securityPrincipalName: securityPrincipalName domainJoinUserName: avdDomainJoinUserName wrklKvName: varWrklKvName serviceObjectsRgName: varServiceObjectsRgName - identityDomainName: avdIdentityDomainName + identityDomainName: identityDomainName identityDomainGuid: identityDomainGuid sessionHostLocation: avdSessionHostLocation storageObjectsRgName: varStorageObjectsRgName @@ -1280,13 +1273,13 @@ module sessionHosts './modules/avdSessionHosts/deploy.bicep' = [for i in range(1 wrklKvName: varWrklKvName serviceObjectsRgName: varServiceObjectsRgName hostPoolName: varHostPoolName - identityDomainName: avdIdentityDomainName + identityDomainName: identityDomainName avdImageTemplateDefinitionId: avdImageTemplateDefinitionId sessionHostOuPath: avdOuPath diskType: avdSessionHostDiskType location: avdSessionHostLocation namePrefix: varSessionHostNamePrefix - size: avdSessionHostsSize + vmSize: avdSessionHostsSize enableAcceleratedNetworking: enableAcceleratedNetworking securityType: securityType == 'Standard' ? '' : securityType secureBootEnabled: secureBootEnabled @@ -1298,10 +1291,10 @@ module sessionHosts './modules/avdSessionHosts/deploy.bicep' = [for i in range(1 encryptionAtHost: diskZeroTrust createAvdFslogixDeployment: createAvdFslogixDeployment storageManagedIdentityResourceId: (varCreateStorageDeployment) ? identity.outputs.managedIdentityStorageResourceId : '' - fslogixScript: varFsLogixScript - fslogixScriptUri: varFslogixScriptUri - fslogixSharePath: '\\\\${varFslogixStorageName}.file.${environment().suffixes.storage}\\${varFslogixFileShareName}' - fslogixScriptArguments: varFsLogixScriptArguments + fslogixSharePath: varFslogixSharePath + fslogixStorageFqdn: varFslogixStorageFqdn + sessionHostConfigurationScriptUri: varSessionHostConfigurationScriptUri + sessionHostConfigurationScript: varSessionHostConfigurationScript marketPlaceGalleryWindows: varMarketPlaceGalleryWindows[avdOsImage] useSharedImage: useSharedImage tags: createResourceTags ? union(varCustomResourceTags, varAvdDefaultTags) : varAvdDefaultTags diff --git a/workload/bicep/modules/avdManagementPlane/deploy.bicep b/workload/bicep/modules/avdManagementPlane/deploy.bicep index 7014b3dae..0964543d8 100644 --- a/workload/bicep/modules/avdManagementPlane/deploy.bicep +++ b/workload/bicep/modules/avdManagementPlane/deploy.bicep @@ -16,10 +16,7 @@ param computeTimeZone string param identityServiceProvider string @sys.description('Identity ID to grant RBAC role to access AVD application group.') -param applicationGroupIdentitiesIds array - -@sys.description('Identity type to grant RBAC role to access AVD application group.') -param applicationGroupIdentityType string +param securityPrincipalIds array @sys.description('AVD OS image source.') param osImage string @@ -236,11 +233,11 @@ module applicationGroups '../../../../carml/1.3.0/Microsoft.DesktopVirtualizatio hostpoolName: hostPoolName tags: tags applications: (applicationGroup.applicationGroupType == 'RemoteApp') ? varRAppApplicationGroupsApps : [] - roleAssignments: !empty(applicationGroupIdentitiesIds) ? [ + roleAssignments: !empty(securityPrincipalIds) ? [ { roleDefinitionIdOrName: 'Desktop Virtualization User' - principalIds: applicationGroupIdentitiesIds - principalType: applicationGroupIdentityType + principalIds: securityPrincipalIds + principalType: 'Group' } ]: [] diagnosticWorkspaceId: alaWorkspaceResourceId diff --git a/workload/bicep/modules/avdSessionHosts/.bicep/configureFslogixOnSessionHosts.bicep b/workload/bicep/modules/avdSessionHosts/.bicep/configureFslogixOnSessionHosts.bicep deleted file mode 100644 index a28abdbc8..000000000 --- a/workload/bicep/modules/avdSessionHosts/.bicep/configureFslogixOnSessionHosts.bicep +++ /dev/null @@ -1,41 +0,0 @@ -// ========== // -// Parameters // -// ========== // - -@sys.description('Extension deployment name.') -param name string - -@sys.description('Location where to deploy compute services.') -param location string - -@sys.description('URI for FSlogix configuration script.') -param baseScriptUri string - -@sys.description('FSlogix configuration script file name.') -param file string - -@sys.description('Configuration arguments for FSlogix.') -param fsLogixScriptArguments string - -// =========== // -// Deployments // -// =========== // - -// FSLogix configuration. -resource fslogixConfigure 'Microsoft.Compute/virtualMachines/extensions@2022-08-01' = { - name: '${name}/FSlogixSetup' - location: location - properties: { - publisher: 'Microsoft.Compute' - type: 'CustomScriptExtension' - typeHandlerVersion: '1.10' - autoUpgradeMinorVersion: true - settings: {} - protectedSettings: { - fileUris: array(baseScriptUri) - commandToExecute: 'powershell -ExecutionPolicy Unrestricted -File ${file} ${fsLogixScriptArguments}' - } - } -} - - diff --git a/workload/bicep/modules/avdSessionHosts/.bicep/configureSessionHost.bicep b/workload/bicep/modules/avdSessionHosts/.bicep/configureSessionHost.bicep new file mode 100644 index 000000000..035ba829b --- /dev/null +++ b/workload/bicep/modules/avdSessionHosts/.bicep/configureSessionHost.bicep @@ -0,0 +1,88 @@ +// ========== // +// Parameters // +// ========== // + +@sys.description('Extension deployment name.') +param name string + +@sys.description('The service providing domain services for Azure Virtual Desktop.') +param identityServiceProvider string + +@sys.description('Identity domain name.') +param identityDomainName string + +@sys.description('Location where to deploy compute services.') +param location string + +@sys.description('URI for AVD session host configuration URI path.') +param baseScriptUri string + +@sys.description('URI for AVD session host configuration script.') +param scriptName string + +@sys.description('Deploy FSlogix configuration.') +param fslogix bool + +@sys.description('File share path for FSlogix storage.') +param fslogixFileShare string + +@sys.description('FSLogix storage account FDQN.') +param fslogixStorageFqdn string + +@sys.description('Session host VM size.') +param vmSize string + +@sys.description('AVD Host Pool registration token') +@secure() +param hostPoolToken string + +// =========== // +// Variable declaration // +// =========== // +// var ScreenCaptureProtection = true +var varScriptArguments = '-IdentityDomainName ${identityDomainName} -AmdVmSize ${varAmdVmSize} -IdentityServiceProvider ${identityServiceProvider} -Fslogix ${fslogix} -FslogixFileShare ${fslogixFileShare} -FslogixStorageFqdn ${fslogixStorageFqdn} -HostPoolRegistrationToken ${hostPoolToken} -NvidiaVmSize ${varNvidiaVmSize} -verbose' // -ScreenCaptureProtection ${ScreenCaptureProtection} -verbose' +var varAmdVmSizes = [ + 'Standard_NV4as_v4' + 'Standard_NV8as_v4' + 'Standard_NV16as_v4' + 'Standard_NV32as_v4' +] +var varAmdVmSize = contains(varAmdVmSizes, vmSize) +var varNvidiaVmSizes = [ + 'Standard_NV6' + 'Standard_NV12' + 'Standard_NV24' + 'Standard_NV12s_v3' + 'Standard_NV24s_v3' + 'Standard_NV48s_v3' + 'Standard_NC4as_T4_v3' + 'Standard_NC8as_T4_v3' + 'Standard_NC16as_T4_v3' + 'Standard_NC64as_T4_v3' + 'Standard_NV6ads_A10_v5' + 'Standard_NV12ads_A10_v5' + 'Standard_NV18ads_A10_v5' + 'Standard_NV36ads_A10_v5' + 'Standard_NV36adms_A10_v5' + 'Standard_NV72ads_A10_v5' +] +var varNvidiaVmSize = contains(varNvidiaVmSizes, vmSize) +// =========== // +// Deployments // +// =========== // +resource sessionHostConfig 'Microsoft.Compute/virtualMachines/extensions@2022-08-01' = { + name: '${name}/SessionHostConfig' + location: location + properties: { + publisher: 'Microsoft.Compute' + type: 'CustomScriptExtension' + typeHandlerVersion: '1.10' + autoUpgradeMinorVersion: true + settings: { + fileUris: array(baseScriptUri) + } + protectedSettings: { + commandToExecute: 'powershell -ExecutionPolicy Unrestricted -File ${scriptName} ${varScriptArguments}' + } + } +} diff --git a/workload/bicep/modules/identity/deploy.bicep b/workload/bicep/modules/identity/deploy.bicep index d6ca8721c..02b5665a7 100644 --- a/workload/bicep/modules/identity/deploy.bicep +++ b/workload/bicep/modules/identity/deploy.bicep @@ -28,7 +28,7 @@ param enableStartVmOnConnect bool param identityServiceProvider string @sys.description('Required, Identity ID to grant RBAC role to access AVD application group.') -param appGroupIdentitiesIds array +param securityPrincipalIds array @sys.description('Deploy scaling plan.') param deployScalingPlan bool @@ -136,7 +136,7 @@ module storageContributorRoleAssign '../../../../carml/1.3.0/Microsoft.Authoriza }] // Storage File Data SMB Share Contributor -module storageSmbShareContributorRoleAssign '../../../../carml/1.3.0/Microsoft.Authorization/roleAssignments/resourceGroup/deploy.bicep' = [for appGroupIdentitiesId in appGroupIdentitiesIds: if (createStorageDeployment && (identityServiceProvider == 'AAD') && (!empty(appGroupIdentitiesIds))) { +module storageSmbShareContributorRoleAssign '../../../../carml/1.3.0/Microsoft.Authorization/roleAssignments/resourceGroup/deploy.bicep' = [for appGroupIdentitiesId in securityPrincipalIds: if (createStorageDeployment && (!empty(securityPrincipalIds))) { name: 'Stora-SmbContri-RolAssign-${take('${appGroupIdentitiesId}', 6)}-${time}' scope: resourceGroup('${subscriptionId}', '${storageObjectsRgName}') params: { @@ -146,7 +146,7 @@ module storageSmbShareContributorRoleAssign '../../../../carml/1.3.0/Microsoft.A }] // VM AAD access roles compute RG -module aadIdentityLoginRoleAssign '../../../../carml/1.3.0/Microsoft.Authorization/roleAssignments/resourceGroup/deploy.bicep' = [for appGroupIdentitiesId in appGroupIdentitiesIds: if (identityServiceProvider == 'AAD' && !empty(appGroupIdentitiesIds)) { +module aadIdentityLoginRoleAssign '../../../../carml/1.3.0/Microsoft.Authorization/roleAssignments/resourceGroup/deploy.bicep' = [for appGroupIdentitiesId in securityPrincipalIds: if (identityServiceProvider == 'AAD' && !empty(securityPrincipalIds)) { name: 'VM-Login-Comp-${take('${appGroupIdentitiesId}', 6)}-${time}' scope: resourceGroup('${subscriptionId}', '${computeObjectsRgName}') params: { @@ -156,7 +156,7 @@ module aadIdentityLoginRoleAssign '../../../../carml/1.3.0/Microsoft.Authorizati }] // VM AAD access roles service objects RG -module aadIdentityLoginAccessServiceObjects '../../../../carml/1.3.0/Microsoft.Authorization/roleAssignments/resourceGroup/deploy.bicep' = [for appGroupIdentitiesId in appGroupIdentitiesIds: if (identityServiceProvider == 'AAD' && !empty(appGroupIdentitiesIds)) { +module aadIdentityLoginAccessServiceObjects '../../../../carml/1.3.0/Microsoft.Authorization/roleAssignments/resourceGroup/deploy.bicep' = [for appGroupIdentitiesId in securityPrincipalIds: if (identityServiceProvider == 'AAD' && !empty(securityPrincipalIds)) { name: 'VM-Login-Serv-${take('${appGroupIdentitiesId}', 6)}-${time}' scope: resourceGroup('${subscriptionId}', '${serviceObjectsRgName}') params: { diff --git a/workload/bicep/modules/storageAzureFiles/.bicep/azureFilesDomainJoin.bicep b/workload/bicep/modules/storageAzureFiles/.bicep/azureFilesDomainJoin.bicep index 094ebc09f..f5878b553 100644 --- a/workload/bicep/modules/storageAzureFiles/.bicep/azureFilesDomainJoin.bicep +++ b/workload/bicep/modules/storageAzureFiles/.bicep/azureFilesDomainJoin.bicep @@ -18,7 +18,7 @@ param scriptArguments string @secure() @sys.description('Domain join user password.') -param domainJoinUserPassword string +param domainJoinUserPassword string // =========== // // Variable declaration // diff --git a/workload/bicep/modules/storageAzureFiles/.bicep/managementVm.bicep b/workload/bicep/modules/storageAzureFiles/.bicep/managementVm.bicep index c1d440026..2ad7760d5 100644 --- a/workload/bicep/modules/storageAzureFiles/.bicep/managementVm.bicep +++ b/workload/bicep/modules/storageAzureFiles/.bicep/managementVm.bicep @@ -55,7 +55,7 @@ param storageManagedIdentityResourceId string @sys.description('Local administrator username.') param vmLocalUserName string -@sys.description('AD domain name.') +@sys.description('Identity domain name.') param identityDomainName string @sys.description('Keyvault name to get credentials from.') diff --git a/workload/bicep/modules/storageAzureFiles/deploy.bicep b/workload/bicep/modules/storageAzureFiles/deploy.bicep index d464ed7f2..d499966f1 100644 --- a/workload/bicep/modules/storageAzureFiles/deploy.bicep +++ b/workload/bicep/modules/storageAzureFiles/deploy.bicep @@ -31,7 +31,7 @@ param sessionHostLocation string @sys.description('File share SMB multichannel.') param fileShareMultichannel bool -@sys.description('AD domain name.') +@sys.description('Identity domain name.') param identityDomainName string @sys.description('AD domain GUID.') @@ -86,12 +86,15 @@ param storageCustomOuPath string @sys.description('OU Storage Path') param ouStgPath string -@sys.description('If OU for Azure Storage needs to be created - set to true and ensure the domain join credentials have priviledge to create OU and create computer objects or join to domain.') -param createOuForStorageString string - @sys.description('Managed Identity Client ID') param managedIdentityClientId string +@sys.description('Identity name array to grant RBAC role to access AVD application group and NTFS permissions.') +param securityPrincipalName string + +@sys.description('storage account FDQN.') +param storageAccountFqdn string + // =========== // // Variable declaration // // =========== // @@ -105,7 +108,8 @@ var varAvdFileShareMetricsDiagnostic = [ ] var varWrklStoragePrivateEndpointName = 'pe-${storageAccountName}-file' var vardirectoryServiceOptions = (identityServiceProvider == 'AADDS') ? 'AADDS': (identityServiceProvider == 'AAD') ? 'AADKERB': 'None' -var varStorageToDomainScriptArgs = '-DscPath ${dscAgentPackageLocation} -StorageAccountName ${storageAccountName} -StorageAccountRG ${storageObjectsRgName} -StoragePurpose ${storagePurpose} -DomainName ${identityDomainName} -IdentityServiceProvider ${identityServiceProvider} -AzureCloudEnvironment ${varAzureCloudName} -SubscriptionId ${workloadSubsId} -DomainAdminUserName ${domainJoinUserName} -CustomOuPath ${storageCustomOuPath} -OUName ${ouStgPath} -CreateNewOU ${createOuForStorageString} -ShareName ${fileShareName} -ClientId ${managedIdentityClientId}' + +var varStorageToDomainScriptArgs = '-DscPath ${dscAgentPackageLocation} -StorageAccountName ${storageAccountName} -StorageAccountRG ${storageObjectsRgName} -StoragePurpose ${storagePurpose} -DomainName ${identityDomainName} -IdentityServiceProvider ${identityServiceProvider} -AzureCloudEnvironment ${varAzureCloudName} -SubscriptionId ${workloadSubsId} -DomainAdminUserName ${domainJoinUserName} -CustomOuPath ${storageCustomOuPath} -OUName ${ouStgPath} -ShareName ${fileShareName} -ClientId ${managedIdentityClientId} -SecurityPrincipalName ${securityPrincipalName} -StorageAccountFqdn ${storageAccountFqdn} ' // =========== // // Deployments // // =========== // @@ -193,7 +197,3 @@ module addShareToDomainScript './.bicep/azureFilesDomainJoin.bicep' = { storageAndFile ] } - -// =========== // -// Outputs // -// =========== // diff --git a/workload/bicep/parameters/deploy-baseline-parameters-example.json b/workload/bicep/parameters/deploy-baseline-parameters-example.json index 1a2699415..0d8c69e62 100644 --- a/workload/bicep/parameters/deploy-baseline-parameters-example.json +++ b/workload/bicep/parameters/deploy-baseline-parameters-example.json @@ -35,13 +35,13 @@ "createIntuneEnrollment": { "value": false }, - "avdApplicationGroupIdentitiesIds": { + "securityPrincipalId": { + "value": "" + }, + "securityPrincipalName": { "value": "" - }, - "avdApplicationGroupIdentityType": { - "value": "Group" }, - "avdIdentityDomainName": { + "identityDomainName": { "value": "<>" }, "avdDomainJoinUserName": { @@ -182,9 +182,6 @@ "storageOuPath": { "value": "" }, - "createOuForStorage": { - "value": false - }, "createResourceTags": { "value": false }, diff --git a/workload/docs/autoGenerated/deploy-baseline.bicep.md b/workload/docs/autoGenerated/deploy-baseline.bicep.md index 38452972e..11cb1de56 100644 --- a/workload/docs/autoGenerated/deploy-baseline.bicep.md +++ b/workload/docs/autoGenerated/deploy-baseline.bicep.md @@ -76,7 +76,6 @@ managementVmOsImage | No | Management VM image SKU (Default: winServer_202 useSharedImage | No | Set to deploy image from Azure Compute Gallery. (Default: false) avdImageTemplateDefinitionId | No | Source custom image ID. (Default: "") storageOuPath | No | OU name for Azure Storage Account. It is recommended to create a new AD Organizational Unit (OU) in AD and disable password expiration policy on computer accounts or service logon accounts accordingly. (Default: "") -createOuForStorage | No | If OU for Azure Storage needs to be created - set to true and ensure the domain join credentials have priviledge to create OU and create computer objects or join to domain. (Default: false) avdUseCustomNaming | No | AVD resources custom naming. (Default: false) avdServiceObjectsRgCustomName | No | AVD service resources resource group custom name. (Default: rg-avd-app1-dev-use2-service-objects) avdNetworkObjectsRgCustomName | No | AVD network resources resource group custom name. (Default: rg-avd-app1-dev-use2-network) @@ -674,14 +673,6 @@ Source custom image ID. (Default: "") OU name for Azure Storage Account. It is recommended to create a new AD Organizational Unit (OU) in AD and disable password expiration policy on computer accounts or service logon accounts accordingly. (Default: "") -### createOuForStorage - -![Parameter Setting](https://img.shields.io/badge/parameter-optional-green?style=flat-square) - -If OU for Azure Storage needs to be created - set to true and ensure the domain join credentials have priviledge to create OU and create computer objects or join to domain. (Default: false) - -- Default value: `False` - ### avdUseCustomNaming ![Parameter Setting](https://img.shields.io/badge/parameter-optional-green?style=flat-square) @@ -1280,9 +1271,6 @@ Enable usage and telemetry feedback to Microsoft. "storageOuPath": { "value": "" }, - "createOuForStorage": { - "value": false - }, "avdUseCustomNaming": { "value": false }, diff --git a/workload/portal-ui/portal-ui-baseline.json b/workload/portal-ui/portal-ui-baseline.json index 7b9fac590..4fbca7007 100644 --- a/workload/portal-ui/portal-ui-baseline.json +++ b/workload/portal-ui/portal-ui-baseline.json @@ -199,33 +199,41 @@ } }, { - "name": "identityAvdUserAccessGroupsDropDown", + "name": "identityAvdUserAccessGroupDropDown", "type": "Microsoft.Common.DropDown", - "visible": "[not(steps('identity').identityAvdAccess.identityAvdUserAccessGroupsCheckBox)]", + "visible": "[not(steps('identity').identityAvdAccess.identityAvdUserAccessGroupCheckBox)]", "label": "Groups", "defaultValue": "", "filter": true, - "toolTip": "Select the desired group(s) to give access to Azure Virtual Desktop resources and if applicable to FSLogix file share.", - "multiselect": true, + "toolTip": "Select the desired group to give access to Azure Virtual Desktop resources and if applicable to FSLogix file share", + "multiselect": false, "constraints": { "allowedValues": "[map(steps('identity').identityAvdAccess.groupsApi.value, (item) => parse(concat('{\"label\":\"', item.displayName, '\",\"value\": {\"name\":\"', item.displayName, '\",\"id\":\"', item.id, '\"}}')))]" } }, { - "name": "identityAvdUserAccessGroupsCheckBox", + "name": "identityAvdUserAccessGroupCheckBox", "type": "Microsoft.Common.CheckBox", "visible": true, - "label": "Provide groups IDs instead", + "label": "Provide group details", "defaultValue": false, - "toolTip": "When the desired groups are not listed in the drop down, selecting this box will allow for entering the group's ObjectIDs." + "toolTip": "When the desired group is not listed in the drop down, selecting this box will allow for entering the group's ObjectID and name. this information will be used to setup AVD access and FSLogix's file share NTFS permissions." + }, + { + "name": "identityAvdUserAccessGroupTextBox1", + "type": "Microsoft.Common.TextBox", + "visible": "[steps('identity').identityAvdAccess.identityAvdUserAccessGroupCheckBox]", + "label": "Name", + "toolTip": "Group name to be granted access to Azure Virtual Desktop published items and FSLogix NTFS permissions.", + "placeholder": "Example: AVD-users" }, { - "name": "identityAvdUserAccessGroupsTextBox", + "name": "identityAvdUserAccessGroupTextBox2", "type": "Microsoft.Common.TextBox", - "visible": "[steps('identity').identityAvdAccess.identityAvdUserAccessGroupsCheckBox]", - "label": "ObjectIDs", - "toolTip": "Comma separated list of security groups (ObjectIDs) to be granted access to Azure Virtual Desktop published items and to create sessions on VMs and single sign-on (SSO) when using AAD as identity provider.", - "placeholder": "Example: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX,XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" + "visible": "[steps('identity').identityAvdAccess.identityAvdUserAccessGroupCheckBox]", + "label": "Object ID", + "toolTip": "Group objectID to be granted access to Azure Virtual Desktop published items and FSLogix NTFS permissions.", + "placeholder": "Example: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" } ] }, @@ -854,6 +862,17 @@ "label": "General settings:", "visible": true, "elements": [ + { + "name": "identityDomainName", + "type": "Microsoft.Common.TextBox", + "visible": "[or(steps('storage').storageFslogix.fslogixDeployment, steps('storage').storageMsix.msixDeployment)]", + "label": "AD Domain name", + "toolTip": "The full qualified domain name of the on-premises domain where the hybrid identities originated from, this information is used for Azure files authentication setup..", + "placeholder": "Example: contoso.com", + "constraints": { + "required": true + } + }, { "name": "identityDomainOuPathStorageExisting", "type": "Microsoft.Common.TextBox", @@ -2295,7 +2314,7 @@ "avdPersonalAssignType": "[if(equals(steps('managementPlane').managementPlaneHostPoolSettings.hostPoolType, 'Personal'), steps('managementPlane').managementPlaneHostPoolSettings.assignmentType, 'Automatic')]", "avdIdentityServiceProvider": "[steps('identity').identityDomainInformation.identityServiceProvider]", "createIntuneEnrollment": "[if(equals(steps('identity').identityDomainInformation.identityServiceProvider, 'AAD'), steps('identity').identityDomainInformation.identityServiceProviderIntuneEnrollment, false)]", - "avdIdentityDomainName": "[steps('identity').identityDomainInformation.identityDomainName]", + "identityDomainName": "[if(or(steps('storage').storageFslogix.fslogixDeployment, steps('storage').storageMsix.msixDeployment), steps('storage').storageGeneralSettings.identityDomainName, '')]", "avdOuPath": "[if(equals(steps('identity').identityDomainInformation.identityServiceProvider, 'AAD'), 'no', steps('sessionHosts').sessionHostsComputeStorageSection.identityDomainOuPath)]", "avdDomainJoinUserName": "[if(equals(steps('identity').identityDomainInformation.identityServiceProvider, 'AAD'), 'no', steps('identity').identityDomainCredentials.identityDomainJoinUserName)]", "avdDomainJoinUserPassword": "[if(equals(steps('identity').identityDomainInformation.identityServiceProvider, 'AAD'), 'no', steps('identity').identityDomainCredentials.identityDomainJoinUserPassword)]", @@ -2375,7 +2394,8 @@ "opsTeamTag": "[if(equals(steps('resourceTagging').resourceTaggingSelection, true), steps('resourceTagging').resourceTags.tagsOpsTeamTag, 'no')]", "ownerTag": "[if(equals(steps('resourceTagging').resourceTaggingSelection, true), steps('resourceTagging').resourceTags.tagsOwnerTag, 'no')]", "costCenterTag": "[if(equals(steps('resourceTagging').resourceTaggingSelection, true), steps('resourceTagging').resourceTags.tagsCostCenterTag, 'no')]", - "avdApplicationGroupIdentitiesIds": "[if(equals(steps('identity').identityAvdAccess.identityAvdUserAccessGroupsCheckBox, true), split(steps('identity').identityAvdAccess.identityAvdUserAccessGroupsTextBox, ','), map(steps('identity').identityAvdAccess.identityAvdUserAccessGroupsDropDown, (item) => item.id))]", + "avdApplicationGroupIdentityId": "[if(steps('identity').identityAvdAccess.identityAvdUserAccessGroupCheckBox, steps('identity').identityAvdAccess.identityAvdUserAccessGroupTextBox2, steps('identity').identityAvdAccess.identityAvdUserAccessGroupDropDown.id)]", + "avdApplicationGroupIdentityName": "[if(equals(steps('identity').identityAvdAccess.identityAvdUserAccessGroupCheckBox, true), steps('identity').identityAvdAccess.identityAvdUserAccessGroupTextBox1, steps('identity').identityAvdAccess.identityAvdUserAccessGroupDropDown.name)]", "avdDeployMonitoring": "[steps('monitoring').deployMonitoring]", "deployAlaWorkspace": "[if(equals(steps('monitoring').deployMonitoring, true), steps('monitoring').deployMonitoringAlaWorkspace, false)]", "avdAlaWorkspaceDataRetention": "[if(equals(steps('monitoring').deployMonitoringAlaWorkspace, true), steps('monitoring').deployMonitoringNewAlaWorkspaceRetention, 0)]", diff --git a/workload/scripts/DSCStorageScripts.zip b/workload/scripts/DSCStorageScripts.zip index ee50a9067..8cd0c76f8 100644 Binary files a/workload/scripts/DSCStorageScripts.zip and b/workload/scripts/DSCStorageScripts.zip differ diff --git a/workload/scripts/DSCStorageScripts/AzFilesHybrid_0.2.4.zip b/workload/scripts/DSCStorageScripts/AzFilesHybrid_0.2.4.zip deleted file mode 100644 index 36054efd2..000000000 Binary files a/workload/scripts/DSCStorageScripts/AzFilesHybrid_0.2.4.zip and /dev/null differ diff --git a/workload/scripts/DSCStorageScripts/AzFilesHybrid_0.2.8.zip b/workload/scripts/DSCStorageScripts/AzFilesHybrid_0.2.8.zip new file mode 100644 index 000000000..e6b9e6f93 Binary files /dev/null and b/workload/scripts/DSCStorageScripts/AzFilesHybrid_0.2.8.zip differ diff --git a/workload/scripts/DSCStorageScripts/Configuration.ps1 b/workload/scripts/DSCStorageScripts/Configuration.ps1 index 3b019318e..6ebaf1a6a 100644 --- a/workload/scripts/DSCStorageScripts/Configuration.ps1 +++ b/workload/scripts/DSCStorageScripts/Configuration.ps1 @@ -44,13 +44,13 @@ param [ValidateNotNullOrEmpty()] [string] $ClientId, - [Parameter(Mandatory = $true)] + [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] - [string] $OUName, + [string]$SecurityPrincipalName, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [string] $CreateNewOU, + [string] $OUName, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -59,6 +59,10 @@ param [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $DomainAdminUserName, + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [string] $StorageAccountFqdn, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -106,13 +110,13 @@ Configuration DomainJoinFileShare [ValidateNotNullOrEmpty()] [string] $ClientId, - [Parameter(Mandatory = $true)] + [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] - [string] $OUName, + [string]$SecurityPrincipalName, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [string] $CreateNewOU, + [string] $OUName, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -121,6 +125,10 @@ Configuration DomainJoinFileShare [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $DomainAdminUserName, + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [string] $StorageAccountFqdn, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -154,7 +162,7 @@ Configuration DomainJoinFileShare . (Join-Path $using:ScriptPath "Logger.ps1") try { Write-Log "DSC DomainJoinStorage SetScript Domain joining storage account $Using:StorageAccountName" - & "$using:ScriptPath\Script-DomainJoinStorage.ps1" -StorageAccountName $Using:StorageAccountName -StorageAccountRG $Using:StorageAccountRG -SubscriptionId $Using:SubscriptionId -ClientId $Using:ClientId -ShareName $Using:ShareName -DomainName $Using:DomainName -IdentityServiceProvider $Using:IdentityServiceProvider -AzureCloudEnvironment $Using:AzureCloudEnvironment -CustomOuPath $Using:CustomOuPath -OUName $Using:OUName -CreateNewOU $Using:CreateNewOU -StoragePurpose $Using:StoragePurpose + & "$using:ScriptPath\Script-DomainJoinStorage.ps1" -StorageAccountName $Using:StorageAccountName -StorageAccountRG $Using:StorageAccountRG -SubscriptionId $Using:SubscriptionId -ClientId $Using:ClientId -SecurityPrincipalName $Using:SecurityPrincipalName -ShareName $Using:ShareName -DomainName $Using:DomainName -IdentityServiceProvider $Using:IdentityServiceProvider -AzureCloudEnvironment $Using:AzureCloudEnvironment -CustomOuPath $Using:CustomOuPath -OUName $Using:OUName -StoragePurpose $Using:StoragePurpose -StorageAccountFqdn $Using:StorageAccountFqdn Write-Log "Successfully domain joined and/or NTFS permission set on Storage account" } @@ -208,4 +216,4 @@ $config = @{ ) } -DomainJoinFileShare -ConfigurationData $config -StorageAccountName $StorageAccountName -StorageAccountRG $StorageAccountRG -SubscriptionId $SubscriptionId -ShareName $ShareName -DomainName $DomainName -IdentityServiceProvider $IdentityServiceProvider -AzureCloudEnvironment $AzureCloudEnvironment -CustomOuPath $CustomOuPath -OUName $OUName -CreateNewOU $CreateNewOU -DomainAdminUserName $DomainAdminUserName -DomainAdminUserPassword $DomainAdminUserPassword -ClientId $ClientId -StoragePurpose $StoragePurpose -Verbose; \ No newline at end of file +DomainJoinFileShare -ConfigurationData $config -StorageAccountName $StorageAccountName -StorageAccountRG $StorageAccountRG -SubscriptionId $SubscriptionId -ShareName $ShareName -DomainName $DomainName -IdentityServiceProvider $IdentityServiceProvider -AzureCloudEnvironment $AzureCloudEnvironment -CustomOuPath $CustomOuPath -OUName $OUName -DomainAdminUserName $DomainAdminUserName -DomainAdminUserPassword $DomainAdminUserPassword -ClientId $ClientId -SecurityPrincipalName $SecurityPrincipalName -StoragePurpose $StoragePurpose -StorageAccountFqdn $StorageAccountFqdn -Verbose; \ No newline at end of file diff --git a/workload/scripts/DSCStorageScripts/script-domainjoinstorage.ps1 b/workload/scripts/DSCStorageScripts/script-domainjoinstorage.ps1 index 7a367f682..ca5a954af 100644 --- a/workload/scripts/DSCStorageScripts/script-domainjoinstorage.ps1 +++ b/workload/scripts/DSCStorageScripts/script-domainjoinstorage.ps1 @@ -18,6 +18,10 @@ param( [ValidateNotNullOrEmpty()] [string] $ClientId, + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [string]$SecurityPrincipalName, + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $SubscriptionId, @@ -31,8 +35,8 @@ param( [string] $CustomOuPath, [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string] $IdentityServiceProvider, + [ValidateNotNullOrEmpty()] + [string] $IdentityServiceProvider, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -44,11 +48,11 @@ param( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [string] $CreateNewOU, + [string] $StoragePurpose, - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string] $StoragePurpose, + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [string] $StorageAccountFqdn, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -62,8 +66,8 @@ $ErrorActionPreference = "Stop" Write-Log "Forcing group policy updates" gpupdate /force -Write-Log "Waiting for domain policies to be applied (2 minutes)" -Start-Sleep -Seconds 120 +Write-Log "Waiting for domain policies to be applied (1 minute)" +Start-Sleep -Seconds 60 Write-Log "Turning off Windows firewall. " @@ -101,24 +105,9 @@ if ($IdentityServiceProvider -eq 'ADDS') { Write-Log "Storage account $StorageAccountName is already domain joined." return } - if ( $CreateNewOU -eq 'true') { - Write-Log "Creating AD Organizational unit $OUName'" - Get-ADOrganizationalUnit -Filter 'Name -like $OUName' - $OrganizationalUnit = Get-ADOrganizationalUnit -Filter 'Name -like $OUName ' - if (-not $OrganizationalUnit) { - foreach ($DCName in $DomainName.split('.')) { - $OUPath = $OUPath + ',DC=' + $DCName - } - - $OUPath = $OUPath.substring(1) - New-ADOrganizationalUnit -name $OUName -path $OUPath - } - - } } Write-Log "Connecting to managed identity account" -# Add-AzAccount -Environment $AzureCloudEnvironment -identity Connect-AzAccount -Identity -AccountId $ClientId Write-Log "Setting Azure subscription to $SubscriptionId" @@ -127,67 +116,71 @@ Select-AzSubscription -SubscriptionId $SubscriptionId if ($IdentityServiceProvider -eq 'ADDS') { Write-Log "Domain joining storage account $StorageAccountName in Resource group $StorageAccountRG" if ( $CustomOuPath -eq 'true') { - Join-AzStorageAccountForAuth -ResourceGroupName $StorageAccountRG -StorageAccountName $StorageAccountName -DomainAccountType 'ComputerAccount' -OrganizationalUnitDistinguishedName $OUName -OverwriteExistingADObject + #Join-AzStorageAccountForAuth -ResourceGroupName $StorageAccountRG -StorageAccountName $StorageAccountName -DomainAccountType 'ComputerAccount' -OrganizationalUnitDistinguishedName $OUName -OverwriteExistingADObject + Join-AzStorageAccount -ResourceGroupName $StorageAccountRG -StorageAccountName $StorageAccountName -OrganizationalUnitDistinguishedName $OUName -DomainAccountType 'ComputerAccount' -EncryptionType 'AES256' -OverwriteExistingADObject #-SamAccountName $SamAccountName Write-Log -Message "Successfully domain joined the storage account $StorageAccountName to custom OU path $OUName" - } else { - Join-AzStorageAccountForAuth -ResourceGroupName $StorageAccountRG -StorageAccountName $StorageAccountName -DomainAccountType 'ComputerAccount' -OrganizationalUnitName $OUName -OverwriteExistingADObject + } + else { + #Join-AzStorageAccountForAuth -ResourceGroupName $StorageAccountRG -StorageAccountName $StorageAccountName -DomainAccountType 'ComputerAccount' -OrganizationalUnitName $OUName -OverwriteExistingADObject + Join-AzStorageAccount -ResourceGroupName $StorageAccountRG -StorageAccountName $StorageAccountName -OrganizationalUnitName $OUName -DomainAccountType 'ComputerAccount' -EncryptionType 'AES256' -OverwriteExistingADObject #-SamAccountName $SamAccountName Write-Log -Message "Successfully domain joined the storage account $StorageAccountName to default OU path $OUName" } } -## Setting default permissions -#$defaultPermission = "None | StorageFileDataSmbShareContributor | StorageFileDataSmbShareReader | StorageFileDataSmbShareElevatedContributor" # Set the default permission of your choice - -$defaultPermission = "StorageFileDataSmbShareContributor" # Set the default permission of your choice -Write-Log "Setting up the default permission of $defaultPermission to storage account $StorageAccountName in $StorageAccountRG" -$account = Set-AzStorageAccount -ResourceGroupName $StorageAccountRG -AccountName $StorageAccountName -DefaultSharePermission $defaultPermission -$account.AzureFilesIdentityBasedAuth - # Remove Administrators from full control - - if ($StoragePurpose -eq 'fslogix') { $DriveLetter = 'Y' - } +} if ($StoragePurpose -eq 'msix') { $DriveLetter = 'X' - } +} Write-Log "Mounting $StoragePurpose storage account on Drive $DriveLetter" - -$FileShareLocation = '\\'+ $StorageAccountName + '.file.core.windows.net\'+$ShareName -$StorageAccountNameFull = $StorageAccountName + '.file.core.windows.net' -$connectTestResult = Test-NetConnection -ComputerName $StorageAccountNameFull -Port 445 -Write-Log "Test connection access to port 445 for $StorageAccountNameFull was $connectTestResult" + +$FileShareLocation = '\\' + $StorageAccountFqdn + '\' + $ShareName +$connectTestResult = Test-NetConnection -ComputerName $StorageAccountFqdn -Port 445 + +Write-Log "Test connection access to port 445 for $StorageAccountFqdn was $connectTestResult" + Try { - Write-Log "Mounting Profile storage $StorageAccountName as a drive $DriveLetter" - if (-not (Get-PSDrive -Name $DriveLetter -ErrorAction SilentlyContinue)) { - - $UserStorage = "/user:Azure\$StorageAccountName" + Write-Log "Mounting Profile storage $StorageAccountName as a drive $DriveLetter" + if (-not (Get-PSDrive -Name $DriveLetter -ErrorAction SilentlyContinue)) { + $UserStorage = "/user:Azure\$StorageAccountName" Write-Log "User storage: $UserStorage" - $StorageKey = (Get-AzStorageAccountKey -ResourceGroupName $StorageAccountRG -AccountName $StorageAccountName) | Where-Object {$_.KeyName -eq "key1"} - Write-Log "Storage key: $StorageKey" + $StorageKey = (Get-AzStorageAccountKey -ResourceGroupName $StorageAccountRG -AccountName $StorageAccountName) | Where-Object { $_.KeyName -eq "key1" } Write-Log "File Share location: $FileShareLocation" net use ${DriveLetter}: $FileShareLocation $UserStorage $StorageKey.Value - #New-PSDrive -Name $DriveLetter -PSProvider FileSystem -Root $FileShareLocation -Persist + #$StorageKey1 = ConvertTo-SecureString $StorageKey.value -AsPlainText -Force + #$credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList ("Azure\stfsly206dorg", $StorageKey1) + #New-PSDrive -Name $DriveLetter -PSProvider FileSystem -Root $FileShareLocation -Credential $credential + } + else { + Write-Log "Drive $DriveLetter already mounted." } - else { - Write-Log "Drive $DriveLetter already mounted." - } } Catch { - Write-Log -Err "Error while mounting profile storage as drive $DriveLetter" - Write-Log -Err $_.Exception.Message - Throw $_ + Write-Log -Err "Error while mounting profile storage as drive $DriveLetter" + Write-Log -Err $_.Exception.Message + Throw $_ } Try { - Write-Log "setting up NTFS permission for FSLogix" - $Commands = "icacls ${DriveLetter}: /remove ('BUILTIN\Administrators')" - Invoke-Expression -Command $Commands - Write-Log "ACLs set" + Write-Log "setting up NTFS permission for FSLogix" + icacls ${DriveLetter}: /remove "BUILTIN\Administrators" + icacls ${DriveLetter}: /grant "Creator Owner:(OI)(CI)(IO)(M)" + icacls ${DriveLetter}: /remove "Authenticated Users" + icacls ${DriveLetter}: /remove "Builtin\Users" + # AVD group permissions + $Group = $DomainName + '\' + $SecurityPrincipalName + icacls ${DriveLetter}: /grant "${Group}:(M)" + Write-Log "ACLs set" + + Write-Log "Unmounting drive" + # Remove-PSDrive -Name $DriveLetter -Force + net use ${DriveLetter} /delete + Write-Log "Drive unmounted" } Catch { - Write-Log -Err "Error while setting up NTFS permission for FSLogix" - Write-Log -Err $_.Exception.Message - Throw $_ + Write-Log -Err "Error while setting up NTFS permission for FSLogix" + Write-Log -Err $_.Exception.Message + Throw $_ } diff --git a/workload/scripts/Manual-DSC-Storage-Scripts.ps1 b/workload/scripts/Manual-DSC-Storage-Scripts.ps1 index 02c99c379..2a533ba05 100644 --- a/workload/scripts/Manual-DSC-Storage-Scripts.ps1 +++ b/workload/scripts/Manual-DSC-Storage-Scripts.ps1 @@ -15,11 +15,15 @@ param ( [ValidateNotNullOrEmpty()] [string] $SubscriptionId, - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string] $ClientId, + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [string] $ClientId, + + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [String]$SecurityPrincipalName, - [Parameter(Mandatory = $true)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $ShareName, @@ -39,13 +43,9 @@ param ( [ValidateNotNullOrEmpty()] [string] $AzureCloudEnvironment, - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string] $OUName, - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string] $CreateNewOU, + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [string] $OUName, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -57,21 +57,24 @@ param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [string] $StoragePurpose + [string] $StorageAccountFqdn, + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [string] $StoragePurpose ) Write-Host "Add domain join account as local administrator" Add-LocalGroupMember -Group "Administrators" -Member $DomainAdminUserName Write-Host "Downloading the DSCStorageScripts.zip from $DscPath" -$DscArhive="DSCStorageScripts.zip" -$appName = 'DSCStorageScripts-'+$StoragePurpose +$DscArhive = "DSCStorageScripts.zip" +$appName = 'DSCStorageScripts-' + $StoragePurpose $drive = 'C:\Packages' New-Item -Path $drive -Name $appName -ItemType Directory -ErrorAction SilentlyContinue Write-Host "Setting DSC local path to $LocalPath" -$LocalPath = $drive+'\DSCStorageScripts-'+$StoragePurpose +$LocalPath = $drive + '\DSCStorageScripts-' + $StoragePurpose $OutputPath = $LocalPath + '\' + $DscArhive Invoke-WebRequest -Uri $DscPath -OutFile $OutputPath @@ -83,14 +86,36 @@ Set-Location -Path $LocalPath Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force Install-Module 'PSDscResources' -Force - -$DscCompileCommand="./Configuration.ps1 -StorageAccountName " + $StorageAccountName + " -StorageAccountRG " + $StorageAccountRG+ " -StoragePurpose " + $StoragePurpose +" -ShareName " + $ShareName + " -SubscriptionId " + $SubscriptionId + " -ClientId " + $ClientId +" -DomainName " + $DomainName + " -IdentityServiceProvider " + $IdentityServiceProvider + " -AzureCloudEnvironment " + $AzureCloudEnvironment + " -CustomOuPath " + $CustomOuPath + " -OUName """ + $OUName + """ -CreateNewOU " + $CreateNewOU + " -DomainAdminUserName " + $DomainAdminUserName + " -DomainAdminUserPassword " + $DomainAdminUserPassword + " -Verbose" +# Handling special characters on password +function Set-EscapeCharacters { + Param( + [parameter(Mandatory = $true, Position = 0)] + [String] + $string + ) + $string = $string -replace '\*', '`*' + $string = $string -replace '\\', '`\' + $string = $string -replace '\~', '`~' + $string = $string -replace '\;', '`;' + $string = $string -replace '\(', '`(' + $string = $string -replace '\%', '`%' + $string = $string -replace '\?', '`?' + $string = $string -replace '\.', '`.' + $string = $string -replace '\:', '`:' + $string = $string -replace '\@', '`@' + $string = $string -replace '\/', '`/' + $string = $string -replace '\$', '`$' + $string +} +$DomainAdminUserPasswordEscaped = Set-EscapeCharacters $DomainAdminUserPassword + +$DscCompileCommand = "./Configuration.ps1 -StorageAccountName """ + $StorageAccountName + """ -StorageAccountRG """ + $StorageAccountRG + """ -StoragePurpose """ + $StoragePurpose + """ -StorageAccountFqdn """ + $StorageAccountFqdn + """ -ShareName """ + $ShareName + """ -SubscriptionId """ + $SubscriptionId + """ -ClientId """ + $ClientId + """ -SecurityPrincipalName """ + $SecurityPrincipalName + """ -DomainName """ + $DomainName + """ -IdentityServiceProvider """ + $IdentityServiceProvider + """ -AzureCloudEnvironment """ + $AzureCloudEnvironment + """ -CustomOuPath " + $CustomOuPath + " -OUName """ + $OUName + """ -DomainAdminUserName """ + $DomainAdminUserName + """ -DomainAdminUserPassword """ + $DomainAdminUserPasswordEscaped + """ -Verbose" Write-Host "Executing the commmand $DscCompileCommand" Invoke-Expression -Command $DscCompileCommand -$MofFolder='DomainJoinFileShare' -$MofPath=$LocalPath + '\' + $MofFolder +$MofFolder = 'DomainJoinFileShare' +$MofPath = $LocalPath + '\' + $MofFolder Write-Host "Generated MOF files here: $MofPath" Write-Host "Applying MOF files. DSC configuration" diff --git a/workload/scripts/Set-FSLogixRegKeys.ps1 b/workload/scripts/Set-FSLogixRegKeys.ps1 deleted file mode 100644 index 20c343b01..000000000 --- a/workload/scripts/Set-FSLogixRegKeys.ps1 +++ /dev/null @@ -1,15 +0,0 @@ -param( - [string]$volumeshare -) - - -reg.exe add 'HKEY_LOCAL_MACHINE\Software\FSLogix\Profiles' /v VHDLocations /t REG_MULTI_SZ /d $volumeshare /f - -reg.exe add 'HKEY_LOCAL_MACHINE\Software\FSLogix\Profiles' /v VolumeType /t REG_SZ /d vhdx /f - -reg.exe add 'HKEY_LOCAL_MACHINE\Software\FSLogix\Profiles' /v DeleteLocalProfileWhenVHDShouldApply /t REG_DWORD /d 1 /f - -reg.exe add 'HKEY_LOCAL_MACHINE\Software\FSLogix\Profiles' /v FlipFlopProfileDirectoryName /t REG_DWORD /d 1 /f - -reg.exe add 'HKEY_LOCAL_MACHINE\Software\FSLogix\Profiles' /v Enabled /t REG_DWORD /d 1 /f - diff --git a/workload/scripts/Set-FSLogixRegKeysAAD.ps1 b/workload/scripts/Set-FSLogixRegKeysAAD.ps1 deleted file mode 100644 index 2ed150dbf..000000000 --- a/workload/scripts/Set-FSLogixRegKeysAAD.ps1 +++ /dev/null @@ -1,26 +0,0 @@ -param( - [Parameter(Mandatory)] - [string]$volumeshare, - - [Parameter(Mandatory)] - [string]$storageAccountName, - - [Parameter(Mandatory)] - [string]$identityDomainName -) - -reg.exe add 'HKEY_LOCAL_MACHINE\Software\FSLogix\Profiles' /v VHDLocations /t REG_MULTI_SZ /d $volumeshare /f - -reg.exe add 'HKEY_LOCAL_MACHINE\Software\FSLogix\Profiles' /v VolumeType /t REG_SZ /d vhdx /f - -reg.exe add 'HKEY_LOCAL_MACHINE\Software\FSLogix\Profiles' /v DeleteLocalProfileWhenVHDShouldApply /t REG_DWORD /d 1 /f - -reg.exe add 'HKEY_LOCAL_MACHINE\Software\FSLogix\Profiles' /v FlipFlopProfileDirectoryName /t REG_DWORD /d 1 /f - -reg.exe add 'HKEY_LOCAL_MACHINE\Software\FSLogix\Profiles' /v Enabled /t REG_DWORD /d 1 /f - -reg.exe add 'HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\Kerberos\domain_realm' /v $identityDomainName /d $storageAccountName.file.core.windows.net - -reg.exe add 'HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\Kerberos\Parameters' /v CloudKerberosTicketRetrievalEnabled /t REG_DWORD /d 1 - -reg.exe add 'HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\AzureADAccount' /v LoadCredKeyFromProfile /t REG_DWORD /d 1 \ No newline at end of file diff --git a/workload/scripts/Set-SessionHostConfiguration.ps1 b/workload/scripts/Set-SessionHostConfiguration.ps1 new file mode 100644 index 000000000..c5e5d1c9e --- /dev/null +++ b/workload/scripts/Set-SessionHostConfiguration.ps1 @@ -0,0 +1,383 @@ +Param( +[parameter(Mandatory)] +[string] +$IdentityDomainName, + +[parameter(Mandatory)] +[string] +$AmdVmSize, + +[parameter(Mandatory)] +[string] +$IdentityServiceProvider, + +[parameter(Mandatory)] +[string] +$Fslogix, + +[parameter(Mandatory)] +[string] +$FslogixFileShare, + +[parameter(Mandatory)] +[string] +$fslogixStorageFqdn, + +[parameter(Mandatory)] +[string] +$HostPoolRegistrationToken, + +[parameter(Mandatory)] +[string] +$NvidiaVmSize, + +# [parameter(Mandatory)] +# [string] +# $ScreenCaptureProtection +) + +############################################################## +# Functions +############################################################## +function Write-Log { +param( + [parameter(Mandatory)] + [string]$Message, + + [parameter(Mandatory)] + [string]$Type +) +$Path = 'C:\Windows\Temp\AVDSessionHostConfig.log' +if (!(Test-Path -Path $Path)) { + New-Item -Path 'C:\' -Name 'AVDSessionHostConfig.log' | Out-Null +} +$Timestamp = Get-Date -Format 'MM/dd/yyyy HH:mm:ss.ff' +$Entry = '[' + $Timestamp + '] [' + $Type + '] ' + $Message +$Entry | Out-File -FilePath $Path -Append +} + +function Get-WebFile { +param( + [parameter(Mandatory)] + [string]$FileName, + + [parameter(Mandatory)] + [string]$URL +) +$Counter = 0 +do { + Invoke-WebRequest -Uri $URL -OutFile $FileName -ErrorAction 'SilentlyContinue' + if ($Counter -gt 0) { + Start-Sleep -Seconds 30 + } + $Counter++ +} +until((Test-Path $FileName) -or $Counter -eq 9) +} + +$ErrorActionPreference = 'Stop' + +try { + + ############################################################## + # Run the Virtual Desktop Optimization Tool (VDOT) + ############################################################## + # https://github.com/The-Virtual-Desktop-Team/Virtual-Desktop-Optimization-Tool + { + # Download VDOT + $URL = 'https://github.com/The-Virtual-Desktop-Team/Virtual-Desktop-Optimization-Tool/archive/refs/heads/main.zip' + $ZIP = 'VDOT.zip' + Invoke-WebRequest -Uri $URL -OutFile $ZIP + + # Extract VDOT from ZIP archive + Expand-Archive -LiteralPath $ZIP -Force + + # Fix to disable AppX Packages + # As of 2/8/22, all AppX Packages are enabled by default + $Files = (Get-ChildItem -Path .\VDOT\Virtual-Desktop-Optimization-Tool-main -File -Recurse -Filter "AppxPackages.json").FullName + foreach ($File in $Files) { + $Content = Get-Content -Path $File + $Settings = $Content | ConvertFrom-Json + $NewSettings = @() + foreach ($Setting in $Settings) { + $NewSettings += [pscustomobject][ordered]@{ + AppxPackage = $Setting.AppxPackage + VDIState = 'Disabled' + URL = $Setting.URL + Description = $Setting.Description + } + } + + $JSON = $NewSettings | ConvertTo-Json + $JSON | Out-File -FilePath $File -Force + } + + # Run VDOT + & .\VDOT\Virtual-Desktop-Optimization-Tool-main\Windows_VDOT.ps1 -Optimizations 'All' -AdvancedOptimizations 'Edge', 'RemoveLegacyIE' -AcceptEULA + + Write-Log -Message 'Optimized the operating system using VDOT' -Type 'INFO' + } + + ############################################################## + # Add Recommended AVD Settings + ############################################################## + $Settings = @( + + # Disable Automatic Updates: https://docs.microsoft.com/en-us/azure/virtual-desktop/set-up-customize-master-image#disable-automatic-updates + [PSCustomObject]@{ + Name = 'NoAutoUpdate' + Path = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU' + PropertyType = 'DWord' + Value = 1 + }, + + # Enable Time Zone Redirection: https://docs.microsoft.com/en-us/azure/virtual-desktop/set-up-customize-master-image#set-up-time-zone-redirection + [PSCustomObject]@{ + Name = 'fEnableTimeZoneRedirection' + Path = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services' + PropertyType = 'DWord' + Value = 1 + } + ) + + ############################################################## + # Add GPU Settings + ############################################################## + # This setting applies to the VM Size's recommended for AVD with a GPU + if ($AmdVmSize -eq 'true' -or $NvidiaVmSize -eq 'true') { + $Settings += @( + + # Configure GPU-accelerated app rendering: https://docs.microsoft.com/en-us/azure/virtual-desktop/configure-vm-gpu#configure-gpu-accelerated-app-rendering + [PSCustomObject]@{ + Name = 'bEnumerateHWBeforeSW' + Path = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services' + PropertyType = 'DWord' + Value = 1 + }, + + # Configure fullscreen video encoding: https://docs.microsoft.com/en-us/azure/virtual-desktop/configure-vm-gpu#configure-fullscreen-video-encoding + [PSCustomObject]@{ + Name = 'AVC444ModePreferred' + Path = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services' + PropertyType = 'DWord' + Value = 1 + } + ) + } + # This setting applies only to VM Size's recommended for AVD with a Nvidia GPU + if ($NvidiaVmSize -eq 'true') { + $Settings += @( + + # Configure GPU-accelerated frame encoding: https://docs.microsoft.com/en-us/azure/virtual-desktop/configure-vm-gpu#configure-gpu-accelerated-frame-encoding + [PSCustomObject]@{ + Name = 'AVChardwareEncodePreferred' + Path = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services' + PropertyType = 'DWord' + Value = 1 + } + ) + } + + # ############################################################## + # # Add Screen Capture Protection Setting + # ############################################################## + # if ($ScreenCaptureProtection -eq 'true') { + # $Settings += @( + + # # Enable Screen Capture Protection: https://docs.microsoft.com/en-us/azure/virtual-desktop/screen-capture-protection + # [PSCustomObject]@{ + # Name = 'fEnableScreenCaptureProtect' + # Path = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services' + # PropertyType = 'DWord' + # Value = 1 + # } + # ) + # } + + ############################################################## + # Add Fslogix Settings + ############################################################## + if ($Fslogix -eq 'true') { + $Settings += @( + # Enables Fslogix profile containers: https://docs.microsoft.com/en-us/fslogix/profile-container-configuration-reference#enabled + [PSCustomObject]@{ + Name = 'Enabled' + Path = 'HKLM:\SOFTWARE\Fslogix\Profiles' + PropertyType = 'DWord' + Value = 1 + }, + # Deletes a local profile if it exists and matches the profile being loaded from VHD: https://docs.microsoft.com/en-us/fslogix/profile-container-configuration-reference#deletelocalprofilewhenvhdshouldapply + [PSCustomObject]@{ + Name = 'DeleteLocalProfileWhenVHDShouldApply' + Path = 'HKLM:\SOFTWARE\FSLogix\Profiles' + PropertyType = 'DWord' + Value = 1 + }, + # The folder created in the Fslogix fileshare will begin with the username instead of the SID: https://docs.microsoft.com/en-us/fslogix/profile-container-configuration-reference#flipflopprofiledirectoryname + [PSCustomObject]@{ + Name = 'FlipFlopProfileDirectoryName' + Path = 'HKLM:\SOFTWARE\FSLogix\Profiles' + PropertyType = 'DWord' + Value = 1 + }, + # Loads FRXShell if there's a failure attaching to, or using an existing profile VHD(X): https://docs.microsoft.com/en-us/fslogix/profile-container-configuration-reference#preventloginwithfailure + [PSCustomObject]@{ + Name = 'PreventLoginWithFailure' + Path = 'HKLM:\SOFTWARE\FSLogix\Profiles' + PropertyType = 'DWord' + Value = 1 + }, + # Loads FRXShell if it's determined a temp profile has been created: https://docs.microsoft.com/en-us/fslogix/profile-container-configuration-reference#preventloginwithtempprofile + [PSCustomObject]@{ + Name = 'PreventLoginWithTempProfile' + Path = 'HKLM:\SOFTWARE\FSLogix\Profiles' + PropertyType = 'DWord' + Value = 1 + }, + # List of file system locations to search for the user's profile VHD(X) file: https://docs.microsoft.com/en-us/fslogix/profile-container-configuration-reference#vhdlocations + [PSCustomObject]@{ + Name = 'VHDLocations' + Path = 'HKLM:\SOFTWARE\FSLogix\Profiles' + PropertyType = 'MultiString' + Value = $FslogixFileShare + }, + [PSCustomObject]@{ + Name = 'VolumeType' + Path = 'HKLM:\SOFTWARE\FSLogix\Profiles' + PropertyType = 'MultiString' + Value = 'vhdx' + } + ) + } + if ($IdentityServiceProvider -eq "AAD" -and $Fslogix -eq 'true') { + $Settings += @( + [PSCustomObject]@{ + Name = 'CloudKerberosTicketRetrievalEnabled' + Path = 'HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\Kerberos\Parameters' + PropertyType = 'DWord' + Value = 1 + }, + [PSCustomObject]@{ + Name = 'LoadCredKeyFromProfile' + Path = 'HKLM:\Software\Policies\Microsoft\AzureADAccount' + PropertyType = 'DWord' + Value = 1 + }, + [PSCustomObject]@{ + Name = $IdentityDomainName + Path = 'HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\Kerberos\domain_realm' + PropertyType = 'String' + Value = $fslogixStorageFqdn + } + + ) + } + + ############################################################## + # Add Azure AD Join Setting + ############################################################## + if ($IdentityServiceProvider -eq "AAD") { + $Settings += @( + + # Enable PKU2U: https://docs.microsoft.com/en-us/azure/virtual-desktop/troubleshoot-azure-ad-connections#windows-desktop-client + [PSCustomObject]@{ + Name = 'AllowOnlineID' + Path = 'HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\pku2u' + PropertyType = 'DWord' + Value = 1 + } + ) + } + + # Set registry settings + foreach ($Setting in $Settings) { + # Create registry key(s) if necessary + if (!(Test-Path -Path $Setting.Path)) { + New-Item -Path $Setting.Path -Force + } + + # Checks for existing registry setting + $Value = Get-ItemProperty -Path $Setting.Path -Name $Setting.Name -ErrorAction 'SilentlyContinue' + $LogOutputValue = 'Path: ' + $Setting.Path + ', Name: ' + $Setting.Name + ', PropertyType: ' + $Setting.PropertyType + ', Value: ' + $Setting.Value + + # Creates the registry setting when it does not exist + if (!$Value) { + New-ItemProperty -Path $Setting.Path -Name $Setting.Name -PropertyType $Setting.PropertyType -Value $Setting.Value -Force + Write-Log -Message "Added registry setting: $LogOutputValue" -Type 'INFO' + } + # Updates the registry setting when it already exists + elseif ($Value.$($Setting.Name) -ne $Setting.Value) { + Set-ItemProperty -Path $Setting.Path -Name $Setting.Name -Value $Setting.Value -Force + Write-Log -Message "Updated registry setting: $LogOutputValue" -Type 'INFO' + } + # Writes log output when registry setting has the correct value + else { + Write-Log -Message "Registry setting exists with correct value: $LogOutputValue" -Type 'INFO' + } + Start-Sleep -Seconds 1 + } + + + ############################################################## + # Add Defender Exclusions for FSLogix + ############################################################## + # https://docs.microsoft.com/en-us/azure/architecture/example-scenario/wvd/windows-virtual-desktop-fslogix#antivirus-exclusions + if ($Fslogix -eq 'true') { + + $Files = @( + "%ProgramFiles%\FSLogix\Apps\frxdrv.sys", + "%ProgramFiles%\FSLogix\Apps\frxdrvvt.sys", + "%ProgramFiles%\FSLogix\Apps\frxccd.sys", + "%TEMP%\*.VHD", + "%TEMP%\*.VHDX", + "%Windir%\TEMP\*.VHD", + "%Windir%\TEMP\*.VHDX" + "$FslogixFileShareName\*.VHD" + "$FslogixFileShareName\*.VHDX" + ) + + foreach ($File in $Files) { + Add-MpPreference -ExclusionPath $File + } + Write-Log -Message 'Enabled Defender exlusions for FSLogix paths' -Type 'INFO' + + $Processes = @( + "%ProgramFiles%\FSLogix\Apps\frxccd.exe", + "%ProgramFiles%\FSLogix\Apps\frxccds.exe", + "%ProgramFiles%\FSLogix\Apps\frxsvc.exe" + ) + + foreach ($Process in $Processes) { + Add-MpPreference -ExclusionProcess $Process + } + Write-Log -Message 'Enabled Defender exlusions for FSLogix processes' -Type 'INFO' + } + + + ############################################################## + # Install the AVD Agent + ############################################################## + # Disabling this method for installing the AVD agent until AAD Join can completed successfully + $BootInstaller = 'AVD-Bootloader.msi' + Get-WebFile -FileName $BootInstaller -URL 'https://query.prod.cms.rt.microsoft.com/cms/api/am/binary/RWrxrH' + Start-Process -FilePath 'msiexec.exe' -ArgumentList "/i $BootInstaller /quiet /qn /norestart /passive" -Wait -Passthru + Write-Log -Message 'Installed AVD Bootloader' -Type 'INFO' + Start-Sleep -Seconds 5 + + $AgentInstaller = 'AVD-Agent.msi' + Get-WebFile -FileName $AgentInstaller -URL 'https://query.prod.cms.rt.microsoft.com/cms/api/am/binary/RWrmXv' + Start-Process -FilePath 'msiexec.exe' -ArgumentList "/i $AgentInstaller /quiet /qn /norestart /passive REGISTRATIONTOKEN=$HostPoolRegistrationToken" -Wait -PassThru + Write-Log -Message 'Installed AVD Agent' -Type 'INFO' + Start-Sleep -Seconds 5 + + ############################################################## + # Restart VM + ############################################################## + if ($IdentityServiceProvider -eq "AAD" -and $AmdVmSize -eq 'false' -and $NvidiaVmSize -eq 'false') { + Start-Process -FilePath 'shutdown' -ArgumentList '/r /t 30' + } + } + catch { + Write-Log -Message $_ -Type 'ERROR' + throw + } \ No newline at end of file diff --git a/workload/scripts/cleanUpRgDeployments.ps1 b/workload/scripts/cleanUpRgDeployments.ps1 deleted file mode 100644 index f05b79467..000000000 --- a/workload/scripts/cleanUpRgDeployments.ps1 +++ /dev/null @@ -1,41 +0,0 @@ -############################################################## -# Clean up resource group deployments -############################################################## - -[CmdletBinding(SupportsShouldProcess)] -param( - [Parameter(Mandatory)] - [string]$subscriptionId, - - [Parameter(Mandatory)] - [string]$resourceGroupName, - - [Parameter(Mandatory)] - [string]$clientId -) - -# Get powershell modules -Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -Install-Module -Name PowershellGet -MinimumVersion 2.2.4.1 -Force -Install-Module 'PSDscResources' -Force -Install-Module -Name Az.Accounts -Force -Install-Module -Name Az.Resources -Force -Import-Module -Name activedirectory -Force - -# Connecting Azure account -Write-Log "Connecting to managed identity account" -Connect-AzAccount -Identity -AccountId $clientId - -# Select subscription -Write-Output "Selecting subscription Subscription $subscriptionId." -Select-AzSubscription -subscriptionid $subscriptionId - -# Get resource group succeeded deployments -Write-Output "Getting $resourceGroupName succeeded deployments" -$resourceGroupDeployments = Get-AzResourceGroupDeployment -ResourceGroupName $resourceGroupName | Where-Object ProvisioningState -EQ 'Succeeded' - -# Delete resource group deployments -Write-Output "Deleting succeded deployments on $resourceGroupName" -foreach ($resourceGroupDeployment in $resourceGroupDeployments) { - Remove-AzResourceGroupDeployment -ResourceGroupName $resourceGroupName -Name $resourceGroupDeployment.DeploymentName -} \ No newline at end of file diff --git a/workload/scripts/get-validation.ps1 b/workload/scripts/get-validation.ps1 deleted file mode 100644 index e3d5404a5..000000000 --- a/workload/scripts/get-validation.ps1 +++ /dev/null @@ -1,272 +0,0 @@ -param( - - [parameter(Mandatory)] - [string]$Availability, - - [parameter(Mandatory)] - [string]$DiskEncryption, - - [parameter(Mandatory)] - [string]$DiskSku, - - [parameter(Mandatory)] - [string]$DomainName, - - [parameter(Mandatory)] - [string]$DomainServices, - - [parameter(Mandatory)] - [string]$EphemeralOsDisk, - - [parameter(Mandatory)] - [string]$ImageSku, - - [parameter(Mandatory)] - [string]$KerberosEncryption, - - [parameter(Mandatory)] - [string]$Location, - - [parameter(Mandatory)] - [string]$PooledHostPool, - - [parameter(Mandatory)] - [string]$RecoveryServices, - - [parameter(Mandatory)] - [int]$SecurityPrincipalIdsCount, - - [parameter(Mandatory)] - [int]$SecurityPrincipalNamesCount, - - [parameter(Mandatory)] - [int]$SessionHostCount, - - [parameter(Mandatory)] - [int]$SessionHostIndex, - - [parameter(Mandatory)] - [string]$StartVmOnConnect, - - [parameter(Mandatory)] - [int]$StorageCount, - - [parameter(Mandatory)] - [string]$StorageSolution, - - [parameter(Mandatory)] - [string]$VmSize, - - [parameter(Mandatory)] - [string]$VnetName, - - [parameter(Mandatory)] - [string]$VnetResourceGroupName -) - -$ErrorActionPreference = 'Stop' - -function Test-AzureCoreQuota { -param( - - [Parameter(Mandatory)] - [string]$Location, - - [Parameter(Mandatory)] - [int]$RequestedCores, - - [Parameter(Mandatory)] - [string]$VmSize - -) - - $Family = (Get-AzComputeResourceSku -Location $Location | Where-Object {$_.Name -eq $VmSize}).Family - $CpuData = Get-AzVMUsage -Location $Location | Where-Object {$_.Name.Value -eq $Family} - $AvailableCores = $CpuData.Limit - $CpuData.CurrentValue - $RequestedCores = $vCPUs * $SessionHostCount - if($RequestedCores -gt $AvailableCores) - { - return $false - } - else - { - return $true - } -} - -# Object for collecting output -$DeploymentScriptOutputs = @{} - -# Info required for validation -$Sku = Get-AzComputeResourceSku -Location $Location | Where-Object {$_.ResourceType -eq 'virtualMachines' -and $_.Name -eq $VmSize} - -############################################################################################ -# Validations & Output -############################################################################################ -# Accelerated Networking -$DeploymentScriptOutputs["acceleratedNetworking"] = ($Sku.capabilities | Where-Object {$_.name -eq 'AcceleratedNetworkingEnabled'}).value - - -# Availability Zone Validation -if($Availability -eq 'AvailabilityZones' -and $Sku.locationInfo.zones.count -lt 3) -{ - Write-Error -Exception 'INVALID AVAILABILITY: The selected VM Size does not support availability zones in this Azure location. https://docs.microsoft.com/azure/virtual-machines/windows/create-powershell-availability-zone' -} - - -# AVD Object ID Output -# This cannot be supported until a user-assigned identity can run Get-AzADServicePrincipal with Azure permissions -# https://docs.microsoft.com/azure/virtual-desktop/start-virtual-machine-connect?tabs=azure-portal#assign-the-custom-role-with-the-azure-portal -<# if($StartVmOnConnect -eq 'true') -{ - $AvdObjectId = (Get-AzADServicePrincipal -ApplicationId '9cdead84-a844-4324-93f2-b2e6bb768d07').Id -} -$DeploymentScriptOutputs["avdObjectId"] = $AvdObjectId #> - - -# Azure NetApp Files Validation & Output -if($StorageSolution -eq "AzureNetAppFiles") -{ - $Vnet = Get-AzVirtualNetwork -Name $VnetName -ResourceGroupName $VnetResourceGroupName - $DnsServers = "$($Vnet.DhcpOptions.DnsServers[0]),$($Vnet.DhcpOptions.DnsServers[1])" - $SubnetId = ($Vnet.Subnets | Where-Object {$_.Delegations[0].ServiceName -eq "Microsoft.NetApp/volumes"}).Id - if($null -eq $SubnetId -or $SubnetId -eq '') - { - Write-Error -Exception 'INVALID AZURE NETAPP FILES CONFIGURATION: A dedicated subnet must be delegated to the ANF resource provider.' - } - Install-Module -Name "Az.NetAppFiles" -Force - $DeployAnfAd = "true" - $Accounts = Get-AzResource -ResourceType "Microsoft.NetApp/netAppAccounts" | Where-Object {$_.Location -eq $Location} - foreach($Account in $Accounts) - { - $AD = Get-AzNetAppFilesActiveDirectory -ResourceGroupName $Account.ResourceGroupName -AccountName $Account.Name - if($AD.ActiveDirectoryId){$DeployAnfAd = 'false'} - } - $DeploymentScriptOutputs["anfDnsServers"] = $DnsServers - $DeploymentScriptOutputs["anfSubnetId"] = $SubnetId - $DeploymentScriptOutputs["anfActiveDirectory"] = $DeployAnfAd -} -else -{ - $DeploymentScriptOutputs["anfDnsServers"] = 'NotApplicable' - $DeploymentScriptOutputs["anfSubnetId"] = 'NotApplicable' - $DeploymentScriptOutputs["anfActiveDirectory"] = 'false' -} - - -# Disk SKU validation -if($DiskSku -like "Premium*" -and ($Sku.capabilities | Where-Object {$_.name -eq 'PremiumIO'}).value -eq $false) -{ - Write-Error -Exception 'INVALID DISK SKU: The selected VM Size does not support the Premium SKU for managed disks.' -} - - -# DNS Forwarders -# This information is used to support Azure Private Link and only used when Private Endpoints are selected for the FSLogix storage. -[array]$DnsForwarders = (Get-AzVirtualNetwork -Name $VnetName -ResourceGroupName $VnetResourceGroupName).DhcpOptions.DnsServers -$DeploymentScriptOutputs["dnsForwarders"] = $DnsForwarders - - -# DNS Server Size -# This information is used to support Azure Private Link and only used when Private Endpoints are selected for the FSLogix storage. -$Tests = @() -$DnsServerSizes = (Get-AzVMSize -Location $Location | Where-Object {$_.Name -match "Standard_D[0-9]s_v[3-9]" -and $_.NumberOfCores -eq 2}).Name -foreach($DnsServerSize in $DnsServerSizes) -{ - $Tests += Test-AzureCoreQuota -Location $Location -RequestedCores 2 -VmSize $DnsServerSize -} -$Index = [array]::indexof($Tests,$true) -if($Index -eq -1) -{ - Write-Error -Exception 'INSUFFICIENT CORE QUOTA: The selected VM Family does not have adequate core quota in the selected location.' -} -$DeploymentScriptOutputs["dnsServerSize"] = $DnsServerSizes[$Index] - - -# Ephemeral Disks Validation & Output -if($EphemeralOsDisk -eq 'true') -{ - # Validate if the VM Size supports Ephemeral Disks - if(($Sku.Capabilities | Where-Object {$_.Name -eq 'EphemeralOSDiskSupported'}).value) - { - # Azure Disk Encryption is not support with Ephemeral Disks - if($DiskEncryption -eq 'true') - { - Write-Error -Exception 'INVALID EPHEMERAL DISK CONFIGURATION: Azure Disk Encryption is not supported with an Ephemeral OS Disk.' - } - - # Azure Disk Encryption is not support with Ephemeral Disks - if($RecoveryServices -eq 'true' -and $PooledHostPool -eq 'false') - { - Write-Error -Exception 'INVALID EPHEMERAL DISK CONFIGURATION: Azure Backup is not supported with an Ephemeral OS Disk.' - } - - $ImageSize = 127 * 1GB - $ResourceVolumeMB = ($Sku.Capabilities | Where-Object {$_.Name -eq 'MaxResourceVolumeMB'}).Value - $ResourceVolumeSize = if($ResourceVolumeMB){[int64]$ResourceVolumeMB * 1MB}else{0} - $CachedDiskBytes = ($Sku.Capabilities | Where-Object {$_.Name -eq 'CachedDiskBytes'}).Value - $CacheVolumeSize = if($CachedDiskBytes){[int64]$CachedDiskBytes}else{0} - - if($ResourceVolumeSize -gt $ImageSize) - { - $DeploymentScriptOutputs["ephemeralOsDisk"] = 'ResourceDisk' - } - elseif ($CacheVolumeSize -gt $ImageSize) - { - $DeploymentScriptOutputs["ephemeralOsDisk"] = 'CacheDisk' - } - } - else - { - Write-Error -Exception "INVALID VM SIZE: VM Size, $VmSize, does not support Ephemeral Disks." - } -} -else -{ - $DeploymentScriptOutputs["ephemeralOsDisk"] = 'None' -} - - -# Hyper-V Generation validation -if($ImageSku -like "*-g2" -and ($Sku.capabilities | Where-Object {$_.name -eq 'HyperVGenerations'}).value -notlike "*2") -{ - Write-Error -Exception 'INVALID HYPER-V GENERATION: The selected VM size does not support the selected Image Sku.' -} - - -# Kerberos Encryption Type validation -if($DomainServices -eq 'AzureActiveDirectory') -{ - $KerberosRc4Encryption = (Get-AzResource -Name $DomainName -ExpandProperties).Properties.domainSecuritySettings.kerberosRc4Encryption - if($KerberosRc4Encryption -eq 'Enabled' -and $KerberosEncryption -eq 'AES256') - { - Write-Error -Exception 'INVALID KERBEROS ENCRYPTION: The Kerberos Encryption on Azure AD DS does not match your Kerberos Encyrption selection.' - } -} - - -# Storage Assignment Validation -# Validate the array length for the Security Principal ID's, Security Principal Names, and Storage Count align -if(($StorageCount -ne $SecurityPrincipalIdsCount -or $StorageCount -ne $SecurityPrincipalNamesCount) -and $StorageCount -gt 0) -{ - Write-Error -Exception 'INVALID ARRAYS: The "SecurityPrinicaplIds" count, "SecurityPrincipalNames" count, and "StorageCount" value must be equal.' -} - - -# vCPU Count Validation -# Recommended range is 4 min, 24 max -# https://docs.microsoft.com/windows-server/remote/remote-desktop-services/virtual-machine-recs?context=/azure/virtual-desktop/context/context -$vCPUs = [int]($Sku.capabilities | Where-Object {$_.name -eq 'vCPUs'}).value -if($vCPUs -lt 4 -or $vCPUs -gt 24) -{ - Write-Error -Exception 'INVALID VCPU COUNT: The selected VM Size does not contain the appropriate amount of vCPUs for Azure Virtual Desktop. https://docs.microsoft.com/windows-server/remote/remote-desktop-services/virtual-machine-recs' -} - - -# vCPU Quota Validation -$RequestedCores = $vCPUs * $SessionHostCount -$Test = Test-AzureCoreQuota -Location $Location -RequestedCores $RequestedCores -VmSize $VmSize -if(!$Test) -{ - Write-Error -Exception "INSUFFICIENT CORE QUOTA: The selected VM size, $VmSize, does not have adequate core quota in the selected location." -} \ No newline at end of file diff --git a/workload/scripts/postDeploymentTempResourcesCleanUp/Configuration.ps1 b/workload/scripts/postDeploymentTempResourcesCleanUp/Configuration.ps1 deleted file mode 100644 index 3b019318e..000000000 --- a/workload/scripts/postDeploymentTempResourcesCleanUp/Configuration.ps1 +++ /dev/null @@ -1,211 +0,0 @@ -<# - .SYNOPSIS - A DSC configuration file for domain joining storage account - - .DESCRIPTION - This script will be run on a domain joined session host under domain admin credentials. -#> - -param -( - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string] $StorageAccountName, - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string] $StorageAccountRG, - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string] $ShareName, - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string] $DomainName, - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string] $CustomOuPath, - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string] $IdentityServiceProvider, - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string] $AzureCloudEnvironment, - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string] $SubscriptionId, - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string] $ClientId, - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string] $OUName, - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string] $CreateNewOU, - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string] $StoragePurpose, - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string] $DomainAdminUserName, - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string] $DomainAdminUserPassword -) - - -Configuration DomainJoinFileShare -{ - param - ( - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string] $StorageAccountName, - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string] $StorageAccountRG, - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string] $ShareName, - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string] $DomainName, - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string] $CustomOuPath, - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string] $IdentityServiceProvider, - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string] $AzureCloudEnvironment, - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string] $SubscriptionId, - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string] $ClientId, - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string] $OUName, - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string] $CreateNewOU, - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string] $StoragePurpose, - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string] $DomainAdminUserName, - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string] $DomainAdminUserPassword - ) - - # Import the module that contains the File resource. - Import-DscResource -ModuleName PsDesiredStateConfiguration - $secStringPassword = ConvertTo-SecureString $DomainAdminUserPassword -AsPlainText -Force - $DomainAdminCred = New-Object System.Management.Automation.PSCredential ($DomainAdminUserName, $secStringPassword) - - $ErrorActionPreference = 'Stop' - - $ScriptPath = [system.io.path]::GetDirectoryName($PSCommandPath) - . (Join-Path $ScriptPath "Logger.ps1") - - Node localhost - { - LocalConfigurationManager { - RebootNodeIfNeeded = $true - ConfigurationMode = "ApplyOnly" - DebugMode = "All" - } - - Script DomainJoinStorage { - # TestScript runs first and if it returns false, then SetScript runs - GetScript = { - return @{'Result' = '' } - } - SetScript = { - . (Join-Path $using:ScriptPath "Logger.ps1") - try { - Write-Log "DSC DomainJoinStorage SetScript Domain joining storage account $Using:StorageAccountName" - & "$using:ScriptPath\Script-DomainJoinStorage.ps1" -StorageAccountName $Using:StorageAccountName -StorageAccountRG $Using:StorageAccountRG -SubscriptionId $Using:SubscriptionId -ClientId $Using:ClientId -ShareName $Using:ShareName -DomainName $Using:DomainName -IdentityServiceProvider $Using:IdentityServiceProvider -AzureCloudEnvironment $Using:AzureCloudEnvironment -CustomOuPath $Using:CustomOuPath -OUName $Using:OUName -CreateNewOU $Using:CreateNewOU -StoragePurpose $Using:StoragePurpose - - Write-Log "Successfully domain joined and/or NTFS permission set on Storage account" - } - catch { - $ErrMsg = $PSItem | Format-List -Force | Out-String - Write-Log -Err $ErrMsg - throw [System.Exception]::new("Some error occurred in DSC DomainJoinStorage SetScript: $ErrMsg", $PSItem.Exception) - } - } - TestScript = { - . (Join-Path $using:ScriptPath "Logger.ps1") - - try { - Write-Log "DSC DomainJoinStorage TestScript checking if storage account $Using:StorageAccountName is domain joined." - $ADModule = Get-Module -Name ActiveDirectory - if (-not $ADModule) { - return $False - } - else { - Import-Module activedirectory - $IsStorageAccountDomainJoined = Get-ADObject -Filter 'ObjectClass -eq "Computer"' | Where-Object { $_.Name -eq $Using:StorageAccountName } - if ($IsStorageAccountDomainJoined) { - Write-Log "Storage account $Using:StorageAccountName is already domain joined." - return $True - } - else { - Write-Log "Storage account $Using:StorageAccount is not domain joined." - return $False - } - } - } - catch { - $ErrMsg = $PSItem | Format-List -Force | Out-String - Write-Log -Err $ErrMsg - throw [System.Exception]::new("Some error occurred in DSC DomainJoinStorage TestScript: $ErrMsg", $PSItem.Exception) - } - } - - PsDscRunAsCredential = $DomainAdminCred - } - } -} - -$config = @{ - AllNodes = @( - @{ - NodeName = 'localhost'; - PSDscAllowPlainTextPassword = $true - PsDscAllowDomainUser = $true - } - ) -} - -DomainJoinFileShare -ConfigurationData $config -StorageAccountName $StorageAccountName -StorageAccountRG $StorageAccountRG -SubscriptionId $SubscriptionId -ShareName $ShareName -DomainName $DomainName -IdentityServiceProvider $IdentityServiceProvider -AzureCloudEnvironment $AzureCloudEnvironment -CustomOuPath $CustomOuPath -OUName $OUName -CreateNewOU $CreateNewOU -DomainAdminUserName $DomainAdminUserName -DomainAdminUserPassword $DomainAdminUserPassword -ClientId $ClientId -StoragePurpose $StoragePurpose -Verbose; \ No newline at end of file diff --git a/workload/scripts/postDeploymentTempResourcesCleanUp/Logger.ps1 b/workload/scripts/postDeploymentTempResourcesCleanUp/Logger.ps1 deleted file mode 100644 index 10cc96e6a..000000000 --- a/workload/scripts/postDeploymentTempResourcesCleanUp/Logger.ps1 +++ /dev/null @@ -1,25 +0,0 @@ -function Write-Log { - [CmdletBinding()] - param - ( - [Parameter(Mandatory = $true)] - [string]$Message, - - # note: can't use variable named '$Error': https://github.com/PowerShell/PSScriptAnalyzer/blob/master/RuleDocumentation/AvoidAssignmentToAutomaticVariable.md - [switch]$Err - ) - - try { - $DateTime = Get-Date -Format "MM-dd-yy HH:mm:ss" - $Invocation = "$($MyInvocation.MyCommand.Source):$($MyInvocation.ScriptLineNumber)" - - if ($Err) { - $Message = "[ERROR] $Message" - } - - Add-Content -Value "$DateTime - $Invocation - $Message" -Path "$([environment]::GetEnvironmentVariable('TEMP', 'Machine'))\ManualDscStorageScriptsLog.log" - } - catch { - throw [System.Exception]::new("Some error occurred while writing to log file with message: $Message", $PSItem.Exception) - } -} \ No newline at end of file diff --git a/workload/scripts/postDeploymentTempResourcesCleanUp/script-domainjoinstorage.ps1 b/workload/scripts/postDeploymentTempResourcesCleanUp/script-domainjoinstorage.ps1 deleted file mode 100644 index a845a8959..000000000 --- a/workload/scripts/postDeploymentTempResourcesCleanUp/script-domainjoinstorage.ps1 +++ /dev/null @@ -1,193 +0,0 @@ -<# - .SYNOPSIS - Domain Join Storage Account - - .DESCRIPTION - In case of AD_DS scenario, domain join storage account as a machine on the domain. -#> -param( - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string] $StorageAccountName, - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string] $StorageAccountRG, - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string] $ClientId, - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string] $SubscriptionId, - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string] $ShareName, - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string] $CustomOuPath, - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string] $IdentityServiceProvider, - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string] $DomainName, - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string] $OUName, - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string] $CreateNewOU, - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string] $StoragePurpose, - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string] $AzureCloudEnvironment -) - -$ErrorActionPreference = "Stop" - -. (Join-Path $ScriptPath "Logger.ps1") - -Write-Log "Forcing group policy updates" -gpupdate /force - -Write-Log "Waiting for domain policies to be applied (2 minutes)" -Start-Sleep -Seconds 120 - - -Write-Log "Turning off Windows firewall. " -Set-NetFirewallProfile -Profile Domain, Public, Private -Enabled False - -Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -Install-Module -Name PowershellGet -MinimumVersion 2.2.4.1 -Force -Install-Module -Name Az.Accounts -Force -Install-Module -Name Az.Storage -Force -Install-Module -Name Az.Network -Force -Install-Module -Name Az.Resources -Force - -if ($IdentityServiceProvider -eq 'ADDS') { - Write-Log "Installing AzFilesHybrid module" - $AzFilesZipLocation = Get-ChildItem -Path $PSScriptRoot -Filter "AzFilesHybrid*.zip" - Expand-Archive $AzFilesZipLocation.FullName -DestinationPath $PSScriptRoot -Force - Set-Location $PSScriptRoot - $AzFilesHybridPath = (Join-Path $PSScriptRoot "CopyToPSPath.ps1") - & $AzFilesHybridPath -} - -if ($IdentityServiceProvider -eq 'ADDS') { - # Please note: ActiveDirectory powershell module is only available on AD joined machines. - # To install it, RSAT administrative tools must be installed on the VM which will - # install the ActiveDirectory powershell module. AzFilesHybrid module takes care of - # installing the RSAT tool, ActiveDirectory powershell module. - Import-Module -Name AzFilesHybrid -Force - $ADModule = Get-Module -Name ActiveDirectory - if (-not $ADModule) { - Request-OSFeature -WindowsClientCapability "Rsat.ActiveDirectory.DS-LDS.Tools" -WindowsServerFeature "RSAT-AD-PowerShell" - Import-Module -Name activedirectory -Force -Verbose - } - $IsStorageAccountDomainJoined = Get-ADObject -Filter 'ObjectClass -eq "Computer"' | Where-Object { $_.Name -eq $StorageAccountName } - if ($IsStorageAccountDomainJoined) { - Write-Log "Storage account $StorageAccountName is already domain joined." - return - } - if ( $CreateNewOU -eq 'true') { - Write-Log "Creating AD Organizational unit $OUName'" - Get-ADOrganizationalUnit -Filter 'Name -like $OUName' - $OrganizationalUnit = Get-ADOrganizationalUnit -Filter 'Name -like $OUName ' - if (-not $OrganizationalUnit) { - foreach ($DCName in $DomainName.split('.')) { - $OUPath = $OUPath + ',DC=' + $DCName - } - - $OUPath = $OUPath.substring(1) - New-ADOrganizationalUnit -name $OUName -path $OUPath - } - - } -} - -Write-Log "Connecting to managed identity account" -# Add-AzAccount -Environment $AzureCloudEnvironment -identity -Connect-AzAccount -Identity -AccountId $ClientId - -Write-Log "Setting Azure subscription to $SubscriptionId" -Select-AzSubscription -SubscriptionId $SubscriptionId - -if ($IdentityServiceProvider -eq 'ADDS') { - Write-Log "Domain joining storage account $StorageAccountName in Resource group $StorageAccountRG" - if ( $CustomOuPath -eq 'true') { - Join-AzStorageAccountForAuth -ResourceGroupName $StorageAccountRG -StorageAccountName $StorageAccountName -DomainAccountType 'ComputerAccount' -OrganizationalUnitDistinguishedName $OUName -OverwriteExistingADObject - Write-Log -Message "Successfully domain joined the storage account $StorageAccountName to custom OU path $OUName" - } else { - Join-AzStorageAccountForAuth -ResourceGroupName $StorageAccountRG -StorageAccountName $StorageAccountName -DomainAccountType 'ComputerAccount' -OrganizationalUnitName $OUName -OverwriteExistingADObject - Write-Log -Message "Successfully domain joined the storage account $StorageAccountName to default OU path $OUName" - } -} - -## Setting default permissions -#$defaultPermission = "None | StorageFileDataSmbShareContributor | StorageFileDataSmbShareReader | StorageFileDataSmbShareElevatedContributor" # Set the default permission of your choice - -$defaultPermission = "StorageFileDataSmbShareContributor" # Set the default permission of your choice -Write-Log "Setting up the default permission of $defaultPermission to storage account $StorageAccountName in $StorageAccountRG" -$account = Set-AzStorageAccount -ResourceGroupName $StorageAccountRG -AccountName $StorageAccountName -DefaultSharePermission $defaultPermission -$account.AzureFilesIdentityBasedAuth - -# Remove Administrators from full control - - -if ($StoragePurpose -eq 'fslogix') { - $DriveLetter -eq "Y" - } -if ($StoragePurpose -eq 'msix') { - $DriveLetter -eq "X" - } -Write-Log "Mounting $StoragePurpose storage account on Drive $DriveLetter" - -$FileShareLocation = '\\'+ $StorageAccountName + '.file.core.windows.net\'+$ShareName -$StorageAccountNameFull = $StorageAccountName + '.file.core.windows.net' -$connectTestResult = Test-NetConnection -ComputerName $StorageAccountNameFull -Port 445 -Write-Log "Test connection access to port 445 for $StorageAccountNameFull was $connectTestResult" -Try { - Write-Log "Mounting Profile storage $StorageAccountName as a drive $DriveLetter" - if (-not (Get-PSDrive -Name $DriveLetter -ErrorAction SilentlyContinue)) { - - $UserStorage = "/user:Azure\$StorageAccountName" - Write-Log "User storage: $UserStorage" - $StorageKey = (Get-AzStorageAccountKey -ResourceGroupName $StorageAccountRG -AccountName $StorageAccountName) | Where-Object {$_.KeyName -eq "key1"} - Write-Log "Storage key: $StorageKey" - Write-Log "File Share location: $FileShareLocation" - net use ${DriveLetter}: $FileShareLocation $UserStorage $StorageKey.Value - #New-PSDrive -Name $DriveLetter -PSProvider FileSystem -Root $FileShareLocation -Persist - } - else { - Write-Log "Drive $DriveLetter already mounted." - } -} -Catch { - Write-Log -Err "Error while mounting profile storage as drive $DriveLetter" - Write-Log -Err $_.Exception.Message - Throw $_ -} - -Try { - Write-Log "setting up NTFS permission for FSLogix" - $Commands = "icacls ${DriveLetter}: /remove ('BUILTIN\Administrators')" - Invoke-Expression -Command $Commands - Write-Log "ACLs set" -} -Catch { - Write-Log -Err "Error while setting up NTFS permission for FSLogix" - Write-Log -Err $_.Exception.Message - Throw $_ -}