Skip to content

Commit 11b4206

Browse files
Merge pull request #295 from JulianHayward/6.7.3_fix294
6.7.3 fixed issue 294
2 parents e24e905 + 1aff587 commit 11b4206

9 files changed

Lines changed: 11 additions & 478 deletions

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,10 @@ Azure Architecture Center (Landing zones): [Azure Governance Visualizer deployme
8686

8787
## Release history
8888

89+
**Changes** (2026-May-18 / 6.7.3 Patch)
90+
91+
- fix issue 294; retirement Classic Administrators
92+
8993
**Changes** (2025-May-21 / 6.7.2 Patch)
9094

9195
- use [AzAPICall](https://aka.ms/AzAPICall) PowerShell module version 1.4.1 (previous 1.4.0). Handle token refresh for OIDC in Azure Devops and GitHub Actions fix
@@ -195,7 +199,6 @@ Short presentation on Azure Governance Visualizer: [download](slides/AzGovViz_in
195199
- PIM (Privileged Identity Management) eligibility for role assignments
196200
- Get a full report of all PIM eligible role assignments for Management Groups and subscriptions, including resolved user members of Microsoft Entra ID groups that have assigned eligibility
197201
- 💡 Note: this feature requires you to execute as service principal with `Application` API permission `PrivilegedAccess.Read.AzureResources`
198-
- Role assignments ClassicAdministrators
199202
- Security & best practice analysis
200203
- Existence of custom role definition that reflect 'Owner' permissions
201204
- Report all role definitions that are capable to write role assignments, list all role assignments for those role definitions

history.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44

55
### Azure Governance Visualizer version 6
66

7+
**Changes** (2026-May-18 / 6.7.3 Patch)
8+
9+
- fix issue 294; retirement Classic Administrators
10+
711
**Changes** (2025-May-21 / 6.7.2 Patch)
812

913
- use [AzAPICall](https://aka.ms/AzAPICall) PowerShell module version 1.4.1 (previous 1.4.0). Handle token refresh for OIDC in Azure Devops and GitHub Actions fix

pwsh/AzGovVizParallel.ps1

Lines changed: 1 addition & 238 deletions
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,7 @@ Param
406406
$Product = 'AzGovViz',
407407

408408
[string]
409-
$ProductVersion = '6.7.2',
409+
$ProductVersion = '6.7.3',
410410

411411
[string]
412412
$GithubRepository = 'aka.ms/AzGovViz',
@@ -6984,7 +6984,6 @@ function processDataCollection {
69846984
$htSubscriptionsRoleAssignmentLimit = $using:htSubscriptionsRoleAssignmentLimit
69856985
$arrayPsRule = $using:arrayPsRule
69866986
$arrayPSRuleTracking = $using:arrayPSRuleTracking
6987-
$htClassicAdministrators = $using:htClassicAdministrators
69886987
$htRoleAssignmentsPIM = $using:htRoleAssignmentsPIM
69896988
$alzPolicies = $using:alzPolicies
69906989
$alzPolicySets = $using:alzPolicySets
@@ -7026,7 +7025,6 @@ function processDataCollection {
70267025
$function:dataCollectionPolicyAssignmentsSub = $using:funcDataCollectionPolicyAssignmentsSub
70277026
$function:dataCollectionRoleDefinitions = $using:funcDataCollectionRoleDefinitions
70287027
$function:dataCollectionRoleAssignmentsSub = $using:funcDataCollectionRoleAssignmentsSub
7029-
$function:dataCollectionClassicAdministratorsSub = $using:funcDataCollectionClassicAdministratorsSub
70307028
$function:dataCollectionDefenderEmailContacts = $using:funcDataCollectionDefenderEmailContacts
70317029
$function:dataCollectionVNets = $using:funcDataCollectionVNets
70327030
$function:dataCollectionPrivateEndpoints = $using:funcDataCollectionPrivateEndpoints
@@ -7210,9 +7208,6 @@ function processDataCollection {
72107208
if ($functionReturn.'addRowToTableDone') {
72117209
$addRowToTableDone = $true
72127210
}
7213-
7214-
#SubscriptionClassicAdministrators
7215-
dataCollectionClassicAdministratorsSub @baseParameters -SubscriptionMgPath $childMgMgPath
72167211
}
72177212

72187213
if ($addRowToTableDone -ne $true) {
@@ -13736,94 +13731,6 @@ extensions: [{ name: 'sort' }]
1373613731
'@)
1373713732
#endregion ScopeInsightsBlueprintsScoped
1373813733

13739-
if ($mgOrSub -eq 'sub') {
13740-
#region ScopeInsightsClassicAdministrators
13741-
if ($htClassicAdministrators.($subscriptionId).ClassicAdministrators.Count -gt 0) {
13742-
$tfCount = $htClassicAdministrators.($subscriptionId).ClassicAdministrators.Count
13743-
$htmlTableId = "ScopeInsights_ClassicAdministrators_$($subscriptionId -replace '\(','_' -replace '\)','_' -replace '-','_' -replace '\.','_')"
13744-
$randomFunctionName = "func_$htmlTableId"
13745-
[void]$htmlScopeInsights.AppendLine(@"
13746-
<button onclick="loadtf$("func_$htmlTableId")()" type="button" class="collapsible"><i class="fa fa-check-circle blue" aria-hidden="true"></i> <span class="valignMiddle">$tfCount Classic Administrators</span></button>
13747-
<div class="content $SIDivContentClass">
13748-
&nbsp;&nbsp;<i class="fa fa-table" aria-hidden="true"></i> Download CSV <a class="externallink" href="#" onclick="download_table_as_csv_semicolon('$htmlTableId');">semicolon</a> | <a class="externallink" href="#" onclick="download_table_as_csv_comma('$htmlTableId');">comma</a>
13749-
<table id="$htmlTableId" class="$cssClass">
13750-
<thead>
13751-
<tr>
13752-
<th>Role</th>
13753-
<th>Identity</th>
13754-
</tr>
13755-
</thead>
13756-
<tbody>
13757-
"@)
13758-
$htmlScopeInsightsClassicAdministrators = $null
13759-
$htmlScopeInsightsClassicAdministrators = foreach ($classicAdministrator in $htClassicAdministrators.($subscriptionId).ClassicAdministrators | Sort-Object -Property Role, Identity) {
13760-
@"
13761-
<tr>
13762-
<td>$($classicAdministrator.Role)</td>
13763-
<td>$($classicAdministrator.Identity)</td>
13764-
</tr>
13765-
"@
13766-
}
13767-
[void]$htmlScopeInsights.AppendLine($htmlScopeInsightsClassicAdministrators)
13768-
[void]$htmlScopeInsights.AppendLine(@"
13769-
</tbody>
13770-
</table>
13771-
</div>
13772-
<script>
13773-
function loadtf$("func_$htmlTableId")() { if (window.helpertfConfig4$htmlTableId !== 1) {
13774-
window.helpertfConfig4$htmlTableId =1;
13775-
var tfConfig4$htmlTableId = {
13776-
base_path: 'https://www.azadvertizer.net/azgovvizv4/tablefilter/', rows_counter: true,
13777-
"@)
13778-
if ($tfCount -gt 10) {
13779-
$spectrum = "10, $tfCount"
13780-
if ($tfCount -gt 50) {
13781-
$spectrum = "10, 25, 50, $tfCount"
13782-
}
13783-
if ($tfCount -gt 100) {
13784-
$spectrum = "10, 30, 50, 100, $tfCount"
13785-
}
13786-
if ($tfCount -gt 500) {
13787-
$spectrum = "10, 30, 50, 100, 250, $tfCount"
13788-
}
13789-
if ($tfCount -gt 1000) {
13790-
$spectrum = "10, 30, 50, 100, 250, 500, 750, $tfCount"
13791-
}
13792-
if ($tfCount -gt 2000) {
13793-
$spectrum = "10, 30, 50, 100, 250, 500, 750, 1000, 1500, $tfCount"
13794-
}
13795-
if ($tfCount -gt 3000) {
13796-
$spectrum = "10, 30, 50, 100, 250, 500, 750, 1000, 1500, 3000, $tfCount"
13797-
}
13798-
[void]$htmlScopeInsights.AppendLine(@"
13799-
paging: {results_per_page: ['Records: ', [$spectrum]]},/*state: {types: ['local_storage'], filters: true, page_number: true, page_length: true, sort: true},*/
13800-
"@)
13801-
}
13802-
[void]$htmlScopeInsights.AppendLine(@"
13803-
btn_reset: true, highlight_keywords: true, alternate_rows: true, auto_filter: { delay: 1100 }, no_results_message: true,
13804-
col_types: [
13805-
'caseinsensitivestring',
13806-
'caseinsensitivestring'
13807-
],
13808-
extensions: [{ name: 'sort' }]
13809-
};
13810-
var tf = new TableFilter('$htmlTableId', tfConfig4$htmlTableId);
13811-
tf.init();}}
13812-
</script>
13813-
"@)
13814-
}
13815-
else {
13816-
[void]$htmlScopeInsights.AppendLine(@'
13817-
<i class="fa fa-ban" aria-hidden="true"></i> No Classic Administrators
13818-
'@)
13819-
}
13820-
[void]$htmlScopeInsights.AppendLine(@'
13821-
</td></tr>
13822-
<tr><td class="detailstd">
13823-
'@)
13824-
#endregion ScopeInsightsClassicAdministrators
13825-
}
13826-
1382713734
#RoleAssignments
1382813735
#region ScopeInsightsRoleAssignments
1382913736
if ($mgOrSub -eq 'mg') {
@@ -19712,106 +19619,6 @@ extensions: [{ name: 'sort' }]
1971219619
}
1971319620
#endregion SUMMARYOrphanedRoleAssignments
1971419621

19715-
#region SUMMARYClassicAdministrators
19716-
Write-Host ' processing TenantSummary ClassicAdministrators'
19717-
19718-
if ($htClassicAdministrators.Keys.Count -gt 0) {
19719-
$tfCount = $htClassicAdministrators.Values.ClassicAdministrators.Count
19720-
$htmlTableId = 'TenantSummary_ClassicAdministrators'
19721-
[void]$htmlTenantSummary.AppendLine(@"
19722-
<button onclick="loadtf$("func_$htmlTableId")()" type="button" class="collapsible" id="buttonTenantSummary_ClassicAdministrators"><i class="padlx fa fa-check-circle blue" aria-hidden="true"></i> <span class="valignMiddle">$($tfCount) Classic Administrators</span>
19723-
</button>
19724-
<div class="content TenantSummary">
19725-
<i class="padlxx fa fa-table" aria-hidden="true"></i> Download CSV <a class="externallink" href="#" onclick="download_table_as_csv_semicolon('$htmlTableId');">semicolon</a> | <a class="externallink" href="#" onclick="download_table_as_csv_comma('$htmlTableId');">comma</a>
19726-
<table id= "$htmlTableId" class="summaryTable">
19727-
<thead>
19728-
<tr>
19729-
<th>Subscription</th>
19730-
<th>SubscriptionId</th>
19731-
<th>MgPath</th>
19732-
<th>Role</th>
19733-
<th>Identity</th>
19734-
</tr>
19735-
</thead>
19736-
<tbody>
19737-
"@)
19738-
$htmlSUMMARYClassicAdministrators = $null
19739-
$classicAdministrators = $htClassicAdministrators.Values.ClassicAdministrators | Sort-Object -Property Subscription, Role, Identity
19740-
if (-not $NoCsvExport) {
19741-
$csvFilename = "$($filename)_ClassicAdministrators"
19742-
Write-Host " Exporting ClassicAdministrators CSV '$($outputPath)$($DirectorySeparatorChar)$($csvFilename).csv'"
19743-
$classicAdministrators | Select-Object -ExcludeProperty Id | Sort-Object -Property Subscription, SubscriptionId, Role | Export-Csv -Encoding utf8 -Path "$($outputPath)$($DirectorySeparatorChar)$($csvFilename).csv" -Delimiter $csvDelimiter -NoTypeInformation
19744-
}
19745-
$htmlSUMMARYClassicAdministrators = foreach ($classicAdministrator in $classicAdministrators) {
19746-
@"
19747-
<tr>
19748-
<td>$($classicAdministrator.Subscription)</td>
19749-
<td>$($classicAdministrator.SubscriptionId)</td>
19750-
<td>$($classicAdministrator.SubscriptionMgPath)</td>
19751-
<td>$($classicAdministrator.Role)</td>
19752-
<td>$($classicAdministrator.Identity)</td>
19753-
</tr>
19754-
"@
19755-
}
19756-
[void]$htmlTenantSummary.AppendLine($htmlSUMMARYClassicAdministrators)
19757-
[void]$htmlTenantSummary.AppendLine(@"
19758-
</tbody>
19759-
</table>
19760-
</div>
19761-
<script>
19762-
function loadtf$("func_$htmlTableId")() { if (window.helpertfConfig4$htmlTableId !== 1) {
19763-
window.helpertfConfig4$htmlTableId =1;
19764-
var tfConfig4$htmlTableId = {
19765-
base_path: 'https://www.azadvertizer.net/azgovvizv4/tablefilter/', rows_counter: true,
19766-
"@)
19767-
if ($tfCount -gt 10) {
19768-
$spectrum = "10, $tfCount"
19769-
if ($tfCount -gt 50) {
19770-
$spectrum = "10, 25, 50, $tfCount"
19771-
}
19772-
if ($tfCount -gt 100) {
19773-
$spectrum = "10, 30, 50, 100, $tfCount"
19774-
}
19775-
if ($tfCount -gt 500) {
19776-
$spectrum = "10, 30, 50, 100, 250, $tfCount"
19777-
}
19778-
if ($tfCount -gt 1000) {
19779-
$spectrum = "10, 30, 50, 100, 250, 500, 750, $tfCount"
19780-
}
19781-
if ($tfCount -gt 2000) {
19782-
$spectrum = "10, 30, 50, 100, 250, 500, 750, 1000, 1500, $tfCount"
19783-
}
19784-
if ($tfCount -gt 3000) {
19785-
$spectrum = "10, 30, 50, 100, 250, 500, 750, 1000, 1500, 3000, $tfCount"
19786-
}
19787-
[void]$htmlTenantSummary.AppendLine(@"
19788-
paging: {results_per_page: ['Records: ', [$spectrum]]},/*state: {types: ['local_storage'], filters: true, page_number: true, page_length: true, sort: true},*/
19789-
"@)
19790-
}
19791-
[void]$htmlTenantSummary.AppendLine(@"
19792-
btn_reset: true, highlight_keywords: true, alternate_rows: true, auto_filter: { delay: 1100 }, no_results_message: true,
19793-
col_3: 'select',
19794-
col_types: [
19795-
'caseinsensitivestring',
19796-
'caseinsensitivestring',
19797-
'caseinsensitivestring',
19798-
'caseinsensitivestring',
19799-
'caseinsensitivestring'
19800-
],
19801-
extensions: [{ name: 'sort' }]
19802-
};
19803-
var tf = new TableFilter('$htmlTableId', tfConfig4$htmlTableId);
19804-
tf.init();}}
19805-
</script>
19806-
"@)
19807-
}
19808-
else {
19809-
[void]$htmlTenantSummary.AppendLine(@'
19810-
<p><i class="padlx fa fa-ban" aria-hidden="true"></i> No ClassicAdministrators</p>
19811-
'@)
19812-
}
19813-
#endregion SUMMARYClassicAdministrators
19814-
1981519622
#region SUMMARYRoleAssignmentsAll
1981619623
$startRoleAssignmentsAll = Get-Date
1981719624
Write-Host ' processing TenantSummary RoleAssignments'
@@ -35783,49 +35590,6 @@ function dataCollectionRoleAssignmentsSub {
3578335590
return $returnObject
3578435591
}
3578535592
$funcDataCollectionRoleAssignmentsSub = $function:dataCollectionRoleAssignmentsSub.ToString()
35786-
35787-
function dataCollectionClassicAdministratorsSub {
35788-
[CmdletBinding()]Param(
35789-
[string]$scopeId,
35790-
[string]$scopeDisplayName,
35791-
[string]$subscriptionMgPath,
35792-
$subscriptionQuotaId
35793-
)
35794-
35795-
$apiEndPoint = $azAPICallConf['azAPIEndpointUrls'].ARM
35796-
$api = "/subscriptions/$($scopeId)/providers/Microsoft.Authorization/classicAdministrators"
35797-
$apiVersion = '?api-version=2015-07-01'
35798-
$uri = $apiEndPoint + $api + $apiVersion
35799-
$azAPICallPayload = @{
35800-
uri = $uri
35801-
method = 'GET'
35802-
currentTask = "classicAdministrators '$($scopeDisplayName)' ('$scopeId') [quotaId:'$subscriptionQuotaId']"
35803-
AzAPICallConfiguration = $azAPICallConf
35804-
}
35805-
35806-
$AzApiCallResult = AzAPICall @azAPICallPayload
35807-
if ($AzApiCallResult -ne 'ClassicAdministratorListFailed') {
35808-
$arrayClassicAdministrators = [System.Collections.ArrayList]@()
35809-
foreach ($roleAll in $AzApiCallResult) {
35810-
$splitPropertiesRole = $roleAll.properties.role.Split(';')
35811-
foreach ($role in $splitPropertiesRole) {
35812-
$null = $arrayClassicAdministrators.Add([PSCustomObject]@{
35813-
Subscription = $scopeDisplayName
35814-
SubscriptionId = $scopeId
35815-
SubscriptionMgPath = $subscriptionMgPath
35816-
Identity = $roleAll.properties.emailAddress
35817-
Role = $role
35818-
Id = $roleAll.id
35819-
})
35820-
}
35821-
}
35822-
$script:htClassicAdministrators.($scopeId) = @{
35823-
ClassicAdministrators = $arrayClassicAdministrators
35824-
}
35825-
}
35826-
}
35827-
$funcDataCollectionClassicAdministratorsSub = $function:dataCollectionClassicAdministratorsSub.ToString()
35828-
3582935593
#endregion functions4DataCollection
3583035594
#endregion Functions
3583135595

@@ -36129,7 +35893,6 @@ if (-not $HierarchyMapOnly) {
3612935893
}
3613035894
$arrayPsRule = [System.Collections.ArrayList]::Synchronized((New-Object System.Collections.ArrayList))
3613135895
$arrayPSRuleTracking = [System.Collections.ArrayList]::Synchronized((New-Object System.Collections.ArrayList))
36132-
$htClassicAdministrators = [System.Collections.Hashtable]::Synchronized(@{})
3613335896
$arrayOrphanedResources = [System.Collections.ArrayList]::Synchronized((New-Object System.Collections.ArrayList))
3613435897
$arrayPIMEligible = [System.Collections.ArrayList]::Synchronized((New-Object System.Collections.ArrayList))
3613535898
$alzPolicies = @{}

pwsh/dev/devAzGovVizParallel.ps1

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,7 @@ Param
406406
$Product = 'AzGovViz',
407407

408408
[string]
409-
$ProductVersion = '6.7.2',
409+
$ProductVersion = '6.7.3',
410410

411411
[string]
412412
$GithubRepository = 'aka.ms/AzGovViz',
@@ -1103,7 +1103,6 @@ if (-not $HierarchyMapOnly) {
11031103
}
11041104
$arrayPsRule = [System.Collections.ArrayList]::Synchronized((New-Object System.Collections.ArrayList))
11051105
$arrayPSRuleTracking = [System.Collections.ArrayList]::Synchronized((New-Object System.Collections.ArrayList))
1106-
$htClassicAdministrators = [System.Collections.Hashtable]::Synchronized(@{})
11071106
$arrayOrphanedResources = [System.Collections.ArrayList]::Synchronized((New-Object System.Collections.ArrayList))
11081107
$arrayPIMEligible = [System.Collections.ArrayList]::Synchronized((New-Object System.Collections.ArrayList))
11091108
$alzPolicies = @{}

pwsh/dev/functions/dataCollection/dataCollectionFunctions.ps1

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -4186,47 +4186,4 @@ function dataCollectionRoleAssignmentsSub {
41864186
return $returnObject
41874187
}
41884188
$funcDataCollectionRoleAssignmentsSub = $function:dataCollectionRoleAssignmentsSub.ToString()
4189-
4190-
function dataCollectionClassicAdministratorsSub {
4191-
[CmdletBinding()]Param(
4192-
[string]$scopeId,
4193-
[string]$scopeDisplayName,
4194-
[string]$subscriptionMgPath,
4195-
$subscriptionQuotaId
4196-
)
4197-
4198-
$apiEndPoint = $azAPICallConf['azAPIEndpointUrls'].ARM
4199-
$api = "/subscriptions/$($scopeId)/providers/Microsoft.Authorization/classicAdministrators"
4200-
$apiVersion = '?api-version=2015-07-01'
4201-
$uri = $apiEndPoint + $api + $apiVersion
4202-
$azAPICallPayload = @{
4203-
uri = $uri
4204-
method = 'GET'
4205-
currentTask = "classicAdministrators '$($scopeDisplayName)' ('$scopeId') [quotaId:'$subscriptionQuotaId']"
4206-
AzAPICallConfiguration = $azAPICallConf
4207-
}
4208-
4209-
$AzApiCallResult = AzAPICall @azAPICallPayload
4210-
if ($AzApiCallResult -ne 'ClassicAdministratorListFailed') {
4211-
$arrayClassicAdministrators = [System.Collections.ArrayList]@()
4212-
foreach ($roleAll in $AzApiCallResult) {
4213-
$splitPropertiesRole = $roleAll.properties.role.Split(';')
4214-
foreach ($role in $splitPropertiesRole) {
4215-
$null = $arrayClassicAdministrators.Add([PSCustomObject]@{
4216-
Subscription = $scopeDisplayName
4217-
SubscriptionId = $scopeId
4218-
SubscriptionMgPath = $subscriptionMgPath
4219-
Identity = $roleAll.properties.emailAddress
4220-
Role = $role
4221-
Id = $roleAll.id
4222-
})
4223-
}
4224-
}
4225-
$script:htClassicAdministrators.($scopeId) = @{
4226-
ClassicAdministrators = $arrayClassicAdministrators
4227-
}
4228-
}
4229-
}
4230-
$funcDataCollectionClassicAdministratorsSub = $function:dataCollectionClassicAdministratorsSub.ToString()
4231-
42324189
#endregion functions4DataCollection

0 commit comments

Comments
 (0)