The following script prepares the environment with the latest Graph API and relevant Az.* modules. System level modules will be removed to prevent conflicts and to contain versioning within the user profile. This script should be run within a fresh PS session and contains $requiredModules fit for managing and deploying Azure Virtual Desktop resources.
Note: Module “ActiveDirectory” is provided through RSAT which can be obtained from Microsoft on Windows 10 or enabled through Apps & Features on Windows 11.
PowerShell
# Clear output
Clear-Host
# Text style functions
function formatInfo { process { Write-Host "[$(Get-Date -Format 'MM/dd/yyyy HH:mm:ss')] [#] $_" -ForegroundColor cyan } }
function formatSuccess { process { Write-Host "[$(Get-Date -Format 'MM/dd/yyyy HH:mm:ss')] [+] $_" -ForegroundColor green } }
function formatWarning { process { Write-Host "[$(Get-Date -Format 'MM/dd/yyyy HH:mm:ss')] [!] $_" -ForegroundColor yellow } }
function formatError { process { Write-Host "[$(Get-Date -Format 'MM/dd/yyyy HH:mm:ss')] [-] $_" -ForegroundColor red } }
function formatPrompt { process { Write-Host "[$(Get-Date -Format 'MM/dd/yyyy HH:mm:ss')] [?] $_" -ForegroundColor gray -NoNewline } }
function formatBreak { process { Write-Host $_ -ForegroundColor white -BackgroundColor black } }
# Confirm user PS context
$currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
if ($currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
Write-Output "You are running PowerShell as Local Administrator. Please exit and run as a Named Account" | formatError
Write-Output ""
read-host "Press ENTER to continue..."
$host.Exit()
}
# MOTD
Write-Output "#####################################" | formatBreak
Write-Output "## Az / Graph Module Update Script ##" | formatBreak
Write-Output "##---------------------------------##" | formatBreak
Write-Output "## ~ Version 1.1 ~ ##" | formatBreak
Write-Output "#####################################`n" | formatBreak
# Set execution policy
Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope CurrentUser -Force
# Suppress environment warnings
$ProgressPreference = 'SilentlyContinue'
$WarningPreference = 'SilentlyContinue'
Set-Item -Path Env:\SuppressAzurePowerShellBreakingChangeWarnings -Value $true
Set-Item -Path Env:\SuppressAzureRmModulesRetiringWarning -Value $true
# Fix PS 5.1 Memory Limitation
$maximumfunctioncount = '32768'
#####################
## Session Modules ##
#####################
Write-Output "Verifying Installed Modules" | formatInfo
[System.Collections.ArrayList]$currentProviders = Get-PackageProvider
[System.Collections.ArrayList]$currentModules = Get-Module
[System.Collections.ArrayList]$installedModules = Get-Module -ListAvailable
# Check for NuGet package provider
if ("NuGet" -notin $currentProviders.Name) {
$version = (($currentProviders | Where-Object { $_.Name -eq "NuGet" }).Version).ToString()
Write-Output "Installed Package Provider ${provider} [${version}]" | formatSuccess
}
# Check for RSAT / AD Modules
if ("ActiveDirectory" -notin $installedModules.Name) {
Write-Output "Module ActiveDirectory not installed. Please exit and install RSAT modules" | formatError
Write-Output ""
read-host "Press ENTER to continue..."
$host.Exit()
}
# Delete conflicting system level modules (All modules should be installed at the currentUser scope)
if (Get-ChildItem 'C:\Program Files\WindowsPowerShell\Modules\' | Where-Object { ($_.PSIsContainer) -and (($_.Name -like "Microsoft.Graph*") -or ($_.Name -like "Az.*") -or ($_.Name -like "AzureRM*")) }) {
Start-Process powershell.exe -Verb Runas -WindowStyle hidden -Wait -ArgumentList "-Command `
Remove-Item 'C:\Program Files\WindowsPowerShell\Modules\Az.*' -Force -Recurse;`
Remove-Item 'C:\Program Files\WindowsPowerShell\Modules\AzureRM*' -Force -Recurse;`
Remove-Item 'C:\Program Files\WindowsPowerShell\Modules\Microsoft.Graph.*' -Force -Recurse;"
Write-Output "Removed Modules Az.*, AzureRM*, and Microsoft.Graph* Forcefully" | formatError
}
$graphModules = Find-Module -Name 'Microsoft.Graph*'
[Version]$latestGraphVersion = ($graphModules | Where-Object Name -eq "Microsoft.Graph").Version
# If an older version of Graph Authentication exists, ALL other graph modules must be removed before the Auth module can be removed (Regardless of versions)
if ($installedModules | Where-Object { ($_.Name -eq "Microsoft.Graph.Authentication") -and ($_.Version -ne $latestGraphVersion) }) {
$modules = $installedModules | Where-Object { ($_.Name -like "Microsoft.Graph*") -and ($_.Name -ne "Microsoft.Graph.Authentication") } | Select Name -Unique
Foreach ($module in $modules) {
$moduleName = $module.Name
$versions = $installedModules | Where-Object { $_.Name -eq $moduleName }
Foreach ($version in $versions) {
$moduleVersion = $version.Version
Uninstall-Module $moduleName -RequiredVersion $moduleVersion
$installedModules.Remove(($installedModules | Where-Object { ($_.Name -eq $moduleName) -and ($_.Version -eq $moduleVersion) }))
Write-Output "Removed Module ${moduleName} [${moduleVersion}]" | formatError
}
}
# Sanity Check Manual Deletion
$psPath = "$($env:USERPROFILE)\Documents\WindowsPowerShell\Modules"
$modules = Get-ChildItem $psPath | Where-Object { ($_.PSIsContainer) -and ($_.Name -like "Microsoft.Graph*") -and ($_.Name -ne "Microsoft.Graph.Authentication") }
Foreach ($module in $modules) {
$module | Remove-Item
}
# Remove Graph Authentication Module
$moduleName = "Microsoft.Graph.Authentication"
$modules = $installedModules | Where-Object { $_.Name -eq $moduleName }
Foreach ($module in $modules) {
$moduleVersion = $module.Version
Uninstall-Module $moduleName -RequiredVersion $moduleVersion
$installedModules.Remove(($installedModules | Where-Object { ($_.Name -eq $moduleName) -and ($_.Version -eq $moduleVersion) }))
Write-Output "Removed Module ${moduleName} [${moduleVersion}]" | formatError
}
}
# Remove conflicting module versions in current scope
$conflictModules = @('AzFilesHybrid','AzureAD','AzureADPreview')
if (Compare-Object -Referenceobject $installedModules -DifferenceObject $conflictModules -IncludeEqual | Where-Object { $_.sideIndicator -eq "==" }){
Foreach ($conflictModule in $conflictModules) {
if ($installedModules.Name -contains $conflictModule) {
if ($conflictModule -eq "AzFilesHybrid") {
Remove-Item "$($env:USERPROFILE)\Documents\WindowsPowerShell\Modules\AzFilesHybrid" -Force -Recurse | Out-Null
$installedModules.Remove(($installedModules | Where-Object { $_.Name -eq $conflictModule }))
Write-Output "Removed Module ${conflictModule} Forcefully" | formatError
} else {
$modules = $installedModules | Where-Object { $_.Name -eq $conflictModule }
Foreach ($module in $modules) {
$moduleVersion = $module.Version
Uninstall-Module $conflictModule -RequiredVersion $moduleVersion
$installedModules.Remove(($installedModules | Where-Object { ($_.Name -eq $conflictModule) -and ($_.Version -eq $moduleVersion) }))
Write-Output "Removed Module ${conflictModule} [${moduleVersion}]" | formatError
}
}
}
}
}
# Remove all Az.* modules that do not match the latest version
$requiredModules = @('Az.Accounts','Az.Billing','Az.DesktopVirtualization','Az.Network','Az.Resources','Az.Storage','Az.Subscription','Az.PrivateDns')
$azModules = Find-Module -Name 'Az.*' | Where-Object { $_.Name -in $requiredModules }
Foreach ($requiredModule in $requiredModules) {
[Version]$latestVersion = ($azModules | Where-Object { $_.Name -eq $requiredModule }).Version
$staleModules = $installedModules | Where-Object { ($_.Name -eq $requiredModule) -and ($_.Version -lt $latestVersion) }
if ($staleModules) {
Foreach ($staleModule in $staleModules) {
$moduleVersion = $staleModule.Version
Uninstall-Module $requiredModule -RequiredVersion $moduleVersion
$installedModules.Remove(($installedModules | Where-Object { ($_.Name -eq $requiredModule) -and ($_.Version -eq $moduleVersion) }))
Write-Output "Removed Module ${requiredModule} [${moduleVersion}]" | formatError
}
}
}
# Populate required list with all available graph modules
$requiredGraphModules = $graphModules | Where-Object { ($_.Version -eq $latestGraphVersion) -and ($_.Name -ne "Microsoft.Graph") -and ($_.Name -notmatch "Microsoft.Graph.Beta*") }
Foreach ($graphObj in $requiredGraphModules) {
$requiredModules += $graphObj.Name
}
# Install required modules
foreach ($module in ($requiredModules | Sort-Object)) {
if ($module -notin $installedModules.Name) {
Install-Module $module -Scope CurrentUser -Repository PSGallery -AllowClobber -Force 3>$null
if ($module -like "AZ.*") {
$version = ($azModules | Where-Object { $_.Name -eq $module }).Version
} else {
$version = ($graphModules | Where-Object { $_.Name -eq $module }).Version
}
Write-Output "Installed Module ${module} [${version}]" | formatSuccess
}
}
# Set Config -- This must be done last to avoid importing stale modules prior to updates
Update-AzConfig -DisplaySurveyMessage $false | Out-Null
Update-AzConfig -DisplayBreakingChangeWarning $false | Out-Null
Update-AzConfig -DisableInstanceDiscovery $true | Out-Null
Update-AzConfig -EnableLoginByWam $false | Out-Null
Update-AzConfig -LoginExperienceV2 Off | Out-Null